apexify.js 4.9.26 → 4.9.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -47
- package/dist/cjs/Canvas/ApexPainter.d.ts +122 -78
- package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/cjs/Canvas/ApexPainter.js +461 -352
- package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
- package/dist/cjs/Canvas/utils/Background/bg.d.ts +23 -11
- package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Background/bg.js +174 -107
- package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -1
- package/dist/cjs/Canvas/utils/Custom/customLines.js +2 -2
- package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts +11 -0
- package/dist/cjs/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Image/imageFilters.js +307 -0
- package/dist/cjs/Canvas/utils/Image/imageFilters.js.map +1 -0
- package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts +47 -112
- package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/Image/imageProperties.js +229 -560
- package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js +351 -0
- package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
- package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
- package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts +29 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.js +334 -0
- package/dist/cjs/Canvas/utils/Shapes/shapes.js.map +1 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
- package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
- package/dist/cjs/Canvas/utils/types.d.ts +227 -131
- package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/types.js +0 -1
- package/dist/cjs/Canvas/utils/types.js.map +1 -1
- package/dist/cjs/Canvas/utils/utils.d.ts +7 -4
- package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/utils.js +17 -7
- package/dist/cjs/Canvas/utils/utils.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/Canvas/ApexPainter.d.ts +122 -78
- package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/esm/Canvas/ApexPainter.js +461 -352
- package/dist/esm/Canvas/ApexPainter.js.map +1 -1
- package/dist/esm/Canvas/utils/Background/bg.d.ts +23 -11
- package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Background/bg.js +174 -107
- package/dist/esm/Canvas/utils/Background/bg.js.map +1 -1
- package/dist/esm/Canvas/utils/Custom/customLines.js +2 -2
- package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageFilters.d.ts +11 -0
- package/dist/esm/Canvas/utils/Image/imageFilters.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Image/imageFilters.js +307 -0
- package/dist/esm/Canvas/utils/Image/imageFilters.js.map +1 -0
- package/dist/esm/Canvas/utils/Image/imageProperties.d.ts +47 -112
- package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/Image/imageProperties.js +229 -560
- package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts +11 -0
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.js +351 -0
- package/dist/esm/Canvas/utils/Image/professionalImageFilters.js.map +1 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts +11 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js +215 -0
- package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts +71 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +392 -0
- package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.d.ts +29 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.js +334 -0
- package/dist/esm/Canvas/utils/Shapes/shapes.js.map +1 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts +127 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +365 -0
- package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -0
- package/dist/esm/Canvas/utils/types.d.ts +227 -131
- package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/types.js +0 -1
- package/dist/esm/Canvas/utils/types.js.map +1 -1
- package/dist/esm/Canvas/utils/utils.d.ts +7 -4
- package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/utils.js +17 -7
- package/dist/esm/Canvas/utils/utils.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/Canvas/ApexPainter.ts +1325 -1218
- package/lib/Canvas/utils/Background/bg.ts +247 -173
- package/lib/Canvas/utils/Custom/customLines.ts +3 -3
- package/lib/Canvas/utils/Image/imageFilters.ts +356 -0
- package/lib/Canvas/utils/Image/imageProperties.ts +322 -775
- package/lib/Canvas/utils/Image/professionalImageFilters.ts +391 -0
- package/lib/Canvas/utils/Image/simpleProfessionalFilters.ts +229 -0
- package/lib/Canvas/utils/Patterns/enhancedPatternRenderer.ts +444 -0
- package/lib/Canvas/utils/Shapes/shapes.ts +528 -0
- package/lib/Canvas/utils/Texts/enhancedTextRenderer.ts +478 -0
- package/lib/Canvas/utils/types.ts +301 -117
- package/lib/Canvas/utils/utils.ts +85 -72
- package/package.json +106 -188
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { loadImage, SKRSContext2D } from "@napi-rs/canvas";
|
|
2
|
-
import { CanvasConfig } from "../types";
|
|
1
|
+
import { createCanvas, loadImage, SKRSContext2D } from "@napi-rs/canvas";
|
|
2
|
+
import { CanvasConfig, gradient } from "../types";
|
|
3
3
|
import path from "path";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export type AlignMode =
|
|
6
|
+
| 'center' | 'top' | 'bottom' | 'left' | 'right'
|
|
7
|
+
| 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
8
|
+
|
|
9
|
+
export type FitMode = 'fill' | 'contain' | 'cover';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Draws a gradient background on the canvas with optional zoom support.
|
|
@@ -15,59 +15,22 @@ interface ZoomConfig {
|
|
|
15
15
|
* @param zoom Optional zoom configuration.
|
|
16
16
|
*/
|
|
17
17
|
export async function drawBackgroundGradient(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
type,
|
|
26
|
-
startX = 0,
|
|
27
|
-
startY = 0,
|
|
28
|
-
endX = canvas.width || 500,
|
|
29
|
-
endY = canvas.height || 500,
|
|
30
|
-
startRadius = 0,
|
|
31
|
-
endRadius = 0,
|
|
32
|
-
colors
|
|
33
|
-
} = canvas.gradientBg;
|
|
34
|
-
|
|
35
|
-
if (!colors || colors.length === 0) {
|
|
36
|
-
throw new Error("You need to provide colors for the gradient.");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let gradient;
|
|
40
|
-
if (type === "linear" || type === undefined) {
|
|
41
|
-
let finalStartX = startX, finalStartY = startY, finalEndX = endX, finalEndY = endY;
|
|
42
|
-
|
|
43
|
-
if (zoom) {
|
|
44
|
-
const scale = Math.max(1, zoom.scale || 1);
|
|
45
|
-
finalStartX = (zoom.x || 0) - (endX - startX) / (2 * scale);
|
|
46
|
-
finalStartY = (zoom.y || 0) - (endY - startY) / (2 * scale);
|
|
47
|
-
finalEndX = (zoom.x || 0) + (endX - startX) / (2 * scale);
|
|
48
|
-
finalEndY = (zoom.y || 0) + (endY - startY) / (2 * scale);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
gradient = ctx.createLinearGradient(finalStartX, finalStartY, finalEndX, finalEndY);
|
|
52
|
-
} else if (type === "radial") {
|
|
53
|
-
gradient = ctx.createRadialGradient(startX, startY, startRadius, endX, endY, endRadius);
|
|
54
|
-
} else {
|
|
55
|
-
throw new Error("Unsupported gradient type.");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
for (const { stop, color } of colors) {
|
|
59
|
-
gradient.addColorStop(stop as number, color as string);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (blur) {
|
|
63
|
-
ctx.filter = `blur(${blur}px)`;
|
|
64
|
-
}
|
|
18
|
+
ctx: SKRSContext2D,
|
|
19
|
+
canvas: CanvasConfig
|
|
20
|
+
) {
|
|
21
|
+
if (!canvas.gradientBg) return;
|
|
22
|
+
const width = canvas.width ?? 500;
|
|
23
|
+
const height = canvas.height ?? 500;
|
|
65
24
|
|
|
66
|
-
|
|
67
|
-
|
|
25
|
+
const grad = buildCanvasGradient(ctx, {
|
|
26
|
+
gradient: canvas.gradientBg,
|
|
27
|
+
width, height
|
|
28
|
+
});
|
|
68
29
|
|
|
69
|
-
|
|
70
|
-
|
|
30
|
+
if (canvas.blur) ctx.filter = `blur(${canvas.blur}px)`;
|
|
31
|
+
ctx.fillStyle = grad;
|
|
32
|
+
ctx.fillRect(0, 0, width, height);
|
|
33
|
+
ctx.filter = 'none';
|
|
71
34
|
}
|
|
72
35
|
|
|
73
36
|
/**
|
|
@@ -77,37 +40,18 @@ export async function drawBackgroundGradient(
|
|
|
77
40
|
* @param zoom Optional zoom configuration.
|
|
78
41
|
*/
|
|
79
42
|
export async function drawBackgroundColor(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
zoom?: ZoomConfig,
|
|
83
|
-
blur?: number
|
|
43
|
+
ctx: SKRSContext2D,
|
|
44
|
+
canvas: CanvasConfig
|
|
84
45
|
): Promise<void> {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
ctx.save();
|
|
46
|
+
const W = canvas.width ?? 500;
|
|
47
|
+
const H = canvas.height ?? 500;
|
|
89
48
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
49
|
+
if (canvas.blur) ctx.filter = `blur(${canvas.blur}px)`;
|
|
50
|
+
if (canvas.colorBg !== 'transparent') {
|
|
51
|
+
ctx.fillStyle = (canvas.colorBg ?? '#000') as string;
|
|
52
|
+
ctx.fillRect(0, 0, W, H);
|
|
93
53
|
}
|
|
94
|
-
|
|
95
|
-
if (zoom) {
|
|
96
|
-
const scale = Math.max(1, zoom.scale || 1);
|
|
97
|
-
const zoomedWidth = canvasWidth / scale;
|
|
98
|
-
const zoomedHeight = canvasHeight / scale;
|
|
99
|
-
|
|
100
|
-
ctx.scale(scale, scale);
|
|
101
|
-
ctx.translate((zoom.x || 0) + zoomedWidth / 2, (zoom.y || 0) + zoomedHeight / 2);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (canvas.colorBg !== "transparent") {
|
|
105
|
-
ctx.fillStyle = canvas.colorBg as string;
|
|
106
|
-
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
ctx.filter = "none";
|
|
110
|
-
ctx.restore();
|
|
54
|
+
ctx.filter = 'none';
|
|
111
55
|
}
|
|
112
56
|
|
|
113
57
|
/**
|
|
@@ -117,95 +61,225 @@ export async function drawBackgroundColor(
|
|
|
117
61
|
* @param zoom Optional zoom configuration.
|
|
118
62
|
*/
|
|
119
63
|
export async function customBackground(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
zoom?: ZoomConfig,
|
|
123
|
-
blur?: number
|
|
64
|
+
ctx: SKRSContext2D,
|
|
65
|
+
canvas: CanvasConfig
|
|
124
66
|
): Promise<void> {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
let imageBuffer: string;
|
|
128
|
-
|
|
129
|
-
if (canvas.customBg.startsWith("http")) {
|
|
130
|
-
imageBuffer = canvas.customBg;
|
|
131
|
-
} else {
|
|
132
|
-
imageBuffer = path.join(process.cwd(), canvas.customBg);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const image = await loadImage(imageBuffer);
|
|
136
|
-
const imgWidth = image.width;
|
|
137
|
-
const imgHeight = image.height;
|
|
138
|
-
const canvasWidth = canvas.width || imgWidth;
|
|
139
|
-
const canvasHeight = canvas.height || imgHeight;
|
|
140
|
-
|
|
141
|
-
if (blur) {
|
|
142
|
-
ctx.filter = `blur(${blur}px)`;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (zoom) {
|
|
146
|
-
const scale = Math.max(1, zoom.scale || 1);
|
|
147
|
-
const cropWidth = imgWidth / scale;
|
|
148
|
-
const cropHeight = imgHeight / scale;
|
|
149
|
-
|
|
150
|
-
let sx = (zoom.x || 0) - cropWidth / 2;
|
|
151
|
-
let sy = (zoom.y || 0) - cropHeight / 2;
|
|
152
|
-
|
|
153
|
-
sx = Math.max(0, Math.min(sx, imgWidth - cropWidth));
|
|
154
|
-
sy = Math.max(0, Math.min(sy, imgHeight - cropHeight));
|
|
155
|
-
|
|
156
|
-
ctx.drawImage(image, sx, sy, cropWidth, cropHeight, 0, 0, canvasWidth, canvasHeight);
|
|
157
|
-
} else {
|
|
158
|
-
ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
|
|
159
|
-
}
|
|
160
|
-
ctx.filter = "none";
|
|
161
|
-
} catch (error: any) {
|
|
162
|
-
console.error("Error loading custom background image:", error.message);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
67
|
+
const cfg = canvas.customBg;
|
|
68
|
+
if (!cfg) return;
|
|
166
69
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const selectedPositions = new Set(borderPosition.toLowerCase().split(",").map((s) => s.trim()));
|
|
184
|
-
|
|
185
|
-
const roundTopLeft = selectedPositions.has("all") || selectedPositions.has("top-left") || (selectedPositions.has("top") && selectedPositions.has("left"));
|
|
186
|
-
const roundTopRight = selectedPositions.has("all") || selectedPositions.has("top-right") || (selectedPositions.has("top") && selectedPositions.has("right"));
|
|
187
|
-
const roundBottomRight = selectedPositions.has("all") || selectedPositions.has("bottom-right") || (selectedPositions.has("bottom") && selectedPositions.has("right"));
|
|
188
|
-
const roundBottomLeft = selectedPositions.has("all") || selectedPositions.has("bottom-left") || (selectedPositions.has("bottom") && selectedPositions.has("left"));
|
|
189
|
-
|
|
190
|
-
const tl = roundTopLeft ? br : 0;
|
|
191
|
-
const tr = roundTopRight ? br : 0;
|
|
192
|
-
const brR = roundBottomRight ? br : 0;
|
|
193
|
-
const bl = roundBottomLeft ? br : 0;
|
|
194
|
-
|
|
195
|
-
ctx.moveTo(x + tl, y);
|
|
196
|
-
ctx.lineTo(x + width - tr, y);
|
|
197
|
-
if (tr > 0) ctx.arc(x + width - tr, y + tr, tr, -Math.PI / 2, 0, false);
|
|
198
|
-
ctx.lineTo(x + width, y + height - brR);
|
|
199
|
-
if (brR > 0) ctx.arc(x + width - brR, y + height - brR, brR, 0, Math.PI / 2, false);
|
|
200
|
-
ctx.lineTo(x + bl, y + height);
|
|
201
|
-
if (bl > 0) ctx.arc(x + bl, y + height - bl, bl, Math.PI / 2, Math.PI, false);
|
|
202
|
-
ctx.lineTo(x, y + tl);
|
|
203
|
-
if (tl > 0) ctx.arc(x + tl, y + tl, tl, Math.PI, -Math.PI / 2, false);
|
|
70
|
+
let imagePath = cfg.source;
|
|
71
|
+
if (!/^https?:\/\//i.test(imagePath)) {
|
|
72
|
+
imagePath = path.join(process.cwd(), imagePath);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const img = await loadImage(imagePath);
|
|
77
|
+
// Canvas size (createCanvas may have overridden via inherit)
|
|
78
|
+
const W = canvas.width ?? img.width;
|
|
79
|
+
const H = canvas.height ?? img.height;
|
|
80
|
+
|
|
81
|
+
if (canvas.blur) ctx.filter = `blur(${canvas.blur}px)`;
|
|
82
|
+
|
|
83
|
+
if (cfg.inherit) {
|
|
84
|
+
// Canvas was resized to image size in createCanvas; just draw 1:1
|
|
85
|
+
ctx.drawImage(img, 0, 0);
|
|
204
86
|
} else {
|
|
205
|
-
|
|
87
|
+
// scale by fit + align
|
|
88
|
+
const fit: FitMode = cfg.fit ?? 'fill';
|
|
89
|
+
let dx = 0, dy = 0, dw = W, dh = H;
|
|
90
|
+
|
|
91
|
+
if (fit === 'contain' || fit === 'cover') {
|
|
92
|
+
const s = fit === 'contain'
|
|
93
|
+
? Math.min(W / img.width, H / img.height)
|
|
94
|
+
: Math.max(W / img.width, H / img.height);
|
|
95
|
+
dw = img.width * s;
|
|
96
|
+
dh = img.height * s;
|
|
97
|
+
// alignment
|
|
98
|
+
const align: AlignMode = cfg.align ?? 'center';
|
|
99
|
+
({ dx, dy } = alignInto(W, H, dw, dh, align));
|
|
100
|
+
} else {
|
|
101
|
+
// 'fill' stretches image to exactly W x H (may distort)
|
|
102
|
+
dx = 0; dy = 0; dw = W; dh = H;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
ctx.drawImage(img, dx, dy, dw, dh);
|
|
206
106
|
}
|
|
207
|
-
|
|
208
|
-
ctx.
|
|
209
|
-
|
|
107
|
+
|
|
108
|
+
ctx.filter = 'none';
|
|
109
|
+
} catch (e: any) {
|
|
110
|
+
console.error('customBackground: failed to load', e?.message ?? e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// helper to place the fitted rect inside canvas by alignment keyword
|
|
115
|
+
function alignInto(
|
|
116
|
+
W: number, H: number, w: number, h: number, align: AlignMode
|
|
117
|
+
): { dx: number; dy: number } {
|
|
118
|
+
const cx = (W - w) / 2;
|
|
119
|
+
const cy = (H - h) / 2;
|
|
120
|
+
switch (align) {
|
|
121
|
+
case 'top-left': return { dx: 0, dy: 0 };
|
|
122
|
+
case 'top': return { dx: cx, dy: 0 };
|
|
123
|
+
case 'top-right': return { dx: W-w, dy: 0 };
|
|
124
|
+
case 'left': return { dx: 0, dy: cy };
|
|
125
|
+
case 'center': return { dx: cx, dy: cy };
|
|
126
|
+
case 'right': return { dx: W-w, dy: cy };
|
|
127
|
+
case 'bottom-left': return { dx: 0, dy: H-h };
|
|
128
|
+
case 'bottom': return { dx: cx, dy: H-h };
|
|
129
|
+
case 'bottom-right': return { dx: W-w, dy: H-h };
|
|
130
|
+
default: return { dx: cx, dy: cy };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
export function buildPathbg(
|
|
137
|
+
ctx: SKRSContext2D,
|
|
138
|
+
x: number,
|
|
139
|
+
y: number,
|
|
140
|
+
width: number,
|
|
141
|
+
height: number,
|
|
142
|
+
borderRadius: number | "circular" = 0,
|
|
143
|
+
borderPosition: string = "all"
|
|
144
|
+
): void {
|
|
145
|
+
ctx.beginPath();
|
|
146
|
+
|
|
147
|
+
if (borderRadius === "circular") {
|
|
148
|
+
const r = Math.min(width, height) / 2;
|
|
149
|
+
ctx.arc(x + width / 2, y + height / 2, r, 0, 2 * Math.PI);
|
|
150
|
+
} else if (typeof borderRadius === "number" && borderRadius > 0) {
|
|
151
|
+
const br = Math.min(borderRadius, width / 2, height / 2);
|
|
152
|
+
const selected = new Set(borderPosition.toLowerCase().split(",").map(s => s.trim()));
|
|
153
|
+
|
|
154
|
+
const roundTL = selected.has("all") || selected.has("top-left") || (selected.has("top") && selected.has("left"));
|
|
155
|
+
const roundTR = selected.has("all") || selected.has("top-right") || (selected.has("top") && selected.has("right"));
|
|
156
|
+
const roundBR = selected.has("all") || selected.has("bottom-right") || (selected.has("bottom") && selected.has("right"));
|
|
157
|
+
const roundBL = selected.has("all") || selected.has("bottom-left") || (selected.has("bottom") && selected.has("left"));
|
|
158
|
+
|
|
159
|
+
const tl = roundTL ? br : 0;
|
|
160
|
+
const tr = roundTR ? br : 0;
|
|
161
|
+
const brR = roundBR ? br : 0;
|
|
162
|
+
const bl = roundBL ? br : 0;
|
|
163
|
+
|
|
164
|
+
ctx.moveTo(x + tl, y);
|
|
165
|
+
ctx.lineTo(x + width - tr, y);
|
|
166
|
+
if (tr) ctx.arcTo(x + width, y, x + width, y + tr, tr);
|
|
167
|
+
ctx.lineTo(x + width, y + height - brR);
|
|
168
|
+
if (brR) ctx.arcTo(x + width, y + height, x + width - brR, y + height, brR);
|
|
169
|
+
ctx.lineTo(x + bl, y + height);
|
|
170
|
+
if (bl) ctx.arcTo(x, y + height, x, y + height - bl, bl);
|
|
171
|
+
ctx.lineTo(x, y + tl);
|
|
172
|
+
if (tl) ctx.arcTo(x, y, x + tl, y, tl);
|
|
173
|
+
} else {
|
|
174
|
+
ctx.rect(x, y, width, height);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
ctx.closePath();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
export function applyNoise(ctx: SKRSContext2D, width: number, height: number, intensity = 0.05) {
|
|
183
|
+
const noiseCanvas = createCanvas(width, height);
|
|
184
|
+
const nctx = noiseCanvas.getContext("2d");
|
|
185
|
+
const imageData = nctx.createImageData(width, height);
|
|
186
|
+
for (let i = 0; i < imageData.data.length; i += 4) {
|
|
187
|
+
const v = (Math.random() * 255) | 0;
|
|
188
|
+
imageData.data[i] = v;
|
|
189
|
+
imageData.data[i+1] = v;
|
|
190
|
+
imageData.data[i+2] = v;
|
|
191
|
+
imageData.data[i+3] = 255 * intensity;
|
|
192
|
+
}
|
|
193
|
+
nctx.putImageData(imageData, 0, 0);
|
|
194
|
+
ctx.drawImage(noiseCanvas, 0, 0);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function drawPattern(
|
|
198
|
+
ctx: SKRSContext2D,
|
|
199
|
+
{ source, repeat = "repeat", opacity = 1 }: { source: string; repeat?: 'repeat'|'repeat-x'|'repeat-y'|'no-repeat'; opacity?: number },
|
|
200
|
+
width: number,
|
|
201
|
+
height: number
|
|
202
|
+
) {
|
|
203
|
+
const img = await loadImage(source);
|
|
204
|
+
const pattern = ctx.createPattern(img, repeat);
|
|
205
|
+
if (!pattern) return;
|
|
206
|
+
ctx.save();
|
|
207
|
+
ctx.globalAlpha = opacity;
|
|
208
|
+
ctx.fillStyle = pattern as any;
|
|
209
|
+
ctx.fillRect(0, 0, width, height);
|
|
210
|
+
ctx.restore();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function rotatePoint(
|
|
214
|
+
x: number, y: number,
|
|
215
|
+
pivotX: number, pivotY: number,
|
|
216
|
+
deg: number
|
|
217
|
+
): [number, number] {
|
|
218
|
+
if (!deg) return [x, y];
|
|
219
|
+
const a = (deg * Math.PI) / 180;
|
|
220
|
+
const dx = x - pivotX, dy = y - pivotY;
|
|
221
|
+
const rx = dx * Math.cos(a) - dy * Math.sin(a);
|
|
222
|
+
const ry = dx * Math.sin(a) + dy * Math.cos(a);
|
|
223
|
+
return [pivotX + rx, pivotY + ry];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function applyCanvasZoom(
|
|
227
|
+
ctx: SKRSContext2D,
|
|
228
|
+
width: number,
|
|
229
|
+
height: number,
|
|
230
|
+
zoom?: { scale?: number; centerX?: number; centerY?: number }
|
|
231
|
+
) {
|
|
232
|
+
if (!zoom) return;
|
|
233
|
+
|
|
234
|
+
const scale = zoom.scale ?? 1;
|
|
235
|
+
if (scale === 1) return; // nothing to do
|
|
236
|
+
|
|
237
|
+
const cx = zoom.centerX ?? width / 2;
|
|
238
|
+
const cy = zoom.centerY ?? height / 2;
|
|
239
|
+
|
|
240
|
+
ctx.translate(cx, cy);
|
|
241
|
+
ctx.scale(scale, scale);
|
|
242
|
+
ctx.translate(-cx, -cy);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
export function buildCanvasGradient(
|
|
247
|
+
ctx: SKRSContext2D,
|
|
248
|
+
cfg: { gradient: gradient; width: number; height: number }
|
|
249
|
+
): CanvasGradient {
|
|
250
|
+
const { gradient, width, height } = cfg;
|
|
251
|
+
|
|
252
|
+
if (gradient.type === 'linear') {
|
|
253
|
+
const {
|
|
254
|
+
startX = 0, startY = 0,
|
|
255
|
+
endX = width, endY = 0,
|
|
256
|
+
rotate = 0,
|
|
257
|
+
pivotX = width/2, pivotY = height/2,
|
|
258
|
+
colors
|
|
259
|
+
} = gradient;
|
|
260
|
+
|
|
261
|
+
const [sx, sy] = rotatePoint(startX, startY, pivotX, pivotY, rotate);
|
|
262
|
+
const [ex, ey] = rotatePoint(endX, endY, pivotX, pivotY, rotate);
|
|
263
|
+
|
|
264
|
+
const g = ctx.createLinearGradient(sx, sy, ex, ey);
|
|
265
|
+
for (const { stop, color } of colors) g.addColorStop(stop, color);
|
|
266
|
+
return g;
|
|
210
267
|
}
|
|
211
|
-
|
|
268
|
+
|
|
269
|
+
// radial
|
|
270
|
+
const {
|
|
271
|
+
startX = width/2, startY = height/2, startRadius = 0,
|
|
272
|
+
endX = width/2, endY = height/2, endRadius = Math.max(width, height)/2,
|
|
273
|
+
rotate = 0,
|
|
274
|
+
pivotX = width/2, pivotY = height/2,
|
|
275
|
+
colors
|
|
276
|
+
} = gradient;
|
|
277
|
+
|
|
278
|
+
// If centers differ, rotation will rotate both centers around pivot.
|
|
279
|
+
const [sx, sy] = rotatePoint(startX, startY, pivotX, pivotY, rotate);
|
|
280
|
+
const [ex, ey] = rotatePoint(endX, endY, pivotX, pivotY, rotate);
|
|
281
|
+
|
|
282
|
+
const g = ctx.createRadialGradient(sx, sy, startRadius, ex, ey, endRadius);
|
|
283
|
+
for (const { stop, color } of colors) g.addColorStop(stop, color);
|
|
284
|
+
return g;
|
|
285
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createGradientFill } from "../Image/imageProperties";
|
|
2
2
|
import { CustomOptions } from "../types";
|
|
3
3
|
|
|
4
4
|
export function customLines(ctx: any, options: CustomOptions[]) {
|
|
@@ -40,7 +40,7 @@ export function customLines(ctx: any, options: CustomOptions[]) {
|
|
|
40
40
|
ctx.lineWidth = appliedStyle?.width || 1;
|
|
41
41
|
|
|
42
42
|
if (appliedStyle?.gradient) {
|
|
43
|
-
ctx.strokeStyle =
|
|
43
|
+
ctx.strokeStyle = createGradientFill(ctx, appliedStyle.gradient, { x: start.x, y: start.y, w: endCoordinates.x - start.x, h: endCoordinates.y - start.y });
|
|
44
44
|
} else {
|
|
45
45
|
ctx.strokeStyle = appliedStyle?.color || 'black';
|
|
46
46
|
}
|
|
@@ -104,7 +104,7 @@ function applyStroke(ctx: any, style: any, start: any, end: any) {
|
|
|
104
104
|
const prevLineCap = ctx.lineCap;
|
|
105
105
|
|
|
106
106
|
if (gradient) {
|
|
107
|
-
ctx.strokeStyle =
|
|
107
|
+
ctx.strokeStyle = createGradientFill(ctx, gradient, { x: start.x, y: start.y, w: end.x - start.x, h: end.y - start.y });
|
|
108
108
|
} else {
|
|
109
109
|
ctx.strokeStyle = color || prevStrokeStyle;
|
|
110
110
|
}
|