@nmmty/lazycanvas 0.6.1 → 0.6.3
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/dist/structures/components/BaseLayer.d.ts +1 -9
- package/dist/structures/components/BaseLayer.js +0 -2
- package/dist/structures/components/BezierLayer.d.ts +8 -0
- package/dist/structures/components/BezierLayer.js +2 -0
- package/dist/structures/components/ImageLayer.d.ts +3 -3
- package/dist/structures/components/LineLayer.d.ts +8 -0
- package/dist/structures/components/LineLayer.js +2 -0
- package/dist/structures/components/MorphLayer.d.ts +11 -3
- package/dist/structures/components/MorphLayer.js +2 -0
- package/dist/structures/components/Path2DLayer.d.ts +17 -0
- package/dist/structures/components/Path2DLayer.js +4 -0
- package/dist/structures/components/QuadraticLayer.d.ts +8 -0
- package/dist/structures/components/QuadraticLayer.js +2 -0
- package/dist/structures/components/TextLayer.d.ts +20 -7
- package/dist/structures/components/TextLayer.js +132 -36
- package/dist/types/types.d.ts +7 -1
- package/dist/utils/utils.d.ts +4 -4
- package/dist/utils/utils.js +8 -2
- package/package.json +59 -59
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScaleType, AnyCentring, AnyGlobalCompositeOperation,
|
|
1
|
+
import { ScaleType, AnyCentring, AnyGlobalCompositeOperation, Transform, AnyFilter, LayerType } from "../../types";
|
|
2
2
|
/**
|
|
3
3
|
* Interface representing the base structure of a layer.
|
|
4
4
|
*/
|
|
@@ -48,14 +48,6 @@ export interface IBaseLayerProps {
|
|
|
48
48
|
* The opacity of the layer, ranging from 0 to 1.
|
|
49
49
|
*/
|
|
50
50
|
opacity: number;
|
|
51
|
-
/**
|
|
52
|
-
* Whether the layer is filled.
|
|
53
|
-
*/
|
|
54
|
-
filled: boolean;
|
|
55
|
-
/**
|
|
56
|
-
* The fill style (color or pattern) of the layer.
|
|
57
|
-
*/
|
|
58
|
-
fillStyle: ColorType;
|
|
59
51
|
/**
|
|
60
52
|
* The shadow properties of the layer.
|
|
61
53
|
*/
|
|
@@ -206,8 +206,6 @@ class BaseLayer {
|
|
|
206
206
|
centring: data.centring || types_1.Centring.Center,
|
|
207
207
|
filter: data.filter || '',
|
|
208
208
|
opacity: data.opacity || 1,
|
|
209
|
-
filled: data.filled || true,
|
|
210
|
-
fillStyle: data.fillStyle || '#000000',
|
|
211
209
|
transform: data.transform || {},
|
|
212
210
|
globalComposite: data.globalComposite || 'source-over',
|
|
213
211
|
};
|
|
@@ -27,6 +27,14 @@ export interface IBezierLayerProps extends IBaseLayerProps {
|
|
|
27
27
|
* The end point of the Bézier curve.
|
|
28
28
|
*/
|
|
29
29
|
endPoint: Point;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the layer is filled.
|
|
32
|
+
*/
|
|
33
|
+
filled: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The fill style (color or pattern) of the layer.
|
|
36
|
+
*/
|
|
37
|
+
fillStyle: ColorType;
|
|
30
38
|
/**
|
|
31
39
|
* The stroke properties of the Bézier curve.
|
|
32
40
|
*/
|
|
@@ -179,6 +179,8 @@ class BezierLayer extends BaseLayer_1.BaseLayer {
|
|
|
179
179
|
validateProps(data) {
|
|
180
180
|
return {
|
|
181
181
|
...super.validateProps(data),
|
|
182
|
+
filled: data.filled || false,
|
|
183
|
+
fillStyle: data.fillStyle || '#000000',
|
|
182
184
|
centring: data.centring || types_1.Centring.None,
|
|
183
185
|
controlPoints: data.controlPoints || [{ x: 0, y: 0 }, { x: 0, y: 0 }],
|
|
184
186
|
endPoint: data.endPoint || { x: 0, y: 0 },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseLayer, IBaseLayer, IBaseLayerMisc, IBaseLayerProps } from "./BaseLayer";
|
|
2
|
-
import { ScaleType, LayerType,
|
|
2
|
+
import { ScaleType, LayerType, RadiusCorner } from "../../types";
|
|
3
3
|
import { Canvas, SKRSContext2D, SvgCanvas } from "@napi-rs/canvas";
|
|
4
4
|
import { LayersManager } from "../managers";
|
|
5
5
|
/**
|
|
@@ -39,7 +39,7 @@ export interface IImageLayerProps extends IBaseLayerProps {
|
|
|
39
39
|
* The radius of the image.
|
|
40
40
|
*/
|
|
41
41
|
radius: {
|
|
42
|
-
[corner in
|
|
42
|
+
[corner in RadiusCorner]?: ScaleType;
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
45
|
}
|
|
@@ -72,7 +72,7 @@ export declare class ImageLayer extends BaseLayer<IImageLayerProps> {
|
|
|
72
72
|
* @returns {this} The current instance for chaining.
|
|
73
73
|
*/
|
|
74
74
|
setSize(width: ScaleType, height: ScaleType, radius?: {
|
|
75
|
-
[corner in
|
|
75
|
+
[corner in RadiusCorner]?: ScaleType;
|
|
76
76
|
}): this;
|
|
77
77
|
/**
|
|
78
78
|
* Draws the Image Layer on the canvas.
|
|
@@ -32,6 +32,14 @@ export interface ILineLayerProps extends IBaseLayerProps {
|
|
|
32
32
|
*/
|
|
33
33
|
y: ScaleType;
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Whether the layer is filled.
|
|
37
|
+
*/
|
|
38
|
+
filled: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The fill style (color or pattern) of the layer.
|
|
41
|
+
*/
|
|
42
|
+
fillStyle: ColorType;
|
|
35
43
|
/**
|
|
36
44
|
* The stroke properties of the line.
|
|
37
45
|
*/
|
|
@@ -148,6 +148,8 @@ class LineLayer extends BaseLayer_1.BaseLayer {
|
|
|
148
148
|
validateProps(data) {
|
|
149
149
|
return {
|
|
150
150
|
...super.validateProps(data),
|
|
151
|
+
filled: data.filled || false,
|
|
152
|
+
fillStyle: data.fillStyle || '#000000',
|
|
151
153
|
centring: data.centring || types_1.Centring.None,
|
|
152
154
|
endPoint: {
|
|
153
155
|
x: data.endPoint?.x || 0,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseLayer, IBaseLayer, IBaseLayerMisc, IBaseLayerProps } from "./BaseLayer";
|
|
2
|
-
import { ColorType, ScaleType, LayerType,
|
|
2
|
+
import { ColorType, ScaleType, LayerType, RadiusCorner } from "../../types";
|
|
3
3
|
import { Canvas, SKRSContext2D, SvgCanvas } from "@napi-rs/canvas";
|
|
4
4
|
import { LayersManager } from "../managers";
|
|
5
5
|
/**
|
|
@@ -35,9 +35,17 @@ export interface IMorphLayerProps extends IBaseLayerProps {
|
|
|
35
35
|
* The radius of the Morph Layer.
|
|
36
36
|
*/
|
|
37
37
|
radius: {
|
|
38
|
-
[corner in
|
|
38
|
+
[corner in RadiusCorner]?: ScaleType;
|
|
39
39
|
};
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Whether the layer is filled.
|
|
43
|
+
*/
|
|
44
|
+
filled: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The fill style (color or pattern) of the layer.
|
|
47
|
+
*/
|
|
48
|
+
fillStyle: ColorType;
|
|
41
49
|
/**
|
|
42
50
|
* The stroke properties of the morph.
|
|
43
51
|
*/
|
|
@@ -90,7 +98,7 @@ export declare class MorphLayer extends BaseLayer<IMorphLayerProps> {
|
|
|
90
98
|
* @returns {this} The current instance for chaining.
|
|
91
99
|
*/
|
|
92
100
|
setSize(width: ScaleType, height: ScaleType, radius?: {
|
|
93
|
-
[corner in
|
|
101
|
+
[corner in RadiusCorner]?: ScaleType;
|
|
94
102
|
}): this;
|
|
95
103
|
/**
|
|
96
104
|
* Sets the color of the Morph Layer.
|
|
@@ -156,6 +156,8 @@ class MorphLayer extends BaseLayer_1.BaseLayer {
|
|
|
156
156
|
validateProps(data) {
|
|
157
157
|
return {
|
|
158
158
|
...super.validateProps(data),
|
|
159
|
+
filled: data.filled || true,
|
|
160
|
+
fillStyle: data.fillStyle || '#000000',
|
|
159
161
|
size: {
|
|
160
162
|
width: data.size?.width || 100,
|
|
161
163
|
height: data.size?.height || 100,
|
|
@@ -3,11 +3,28 @@ import { ColorType, LayerType } from "../../types";
|
|
|
3
3
|
import { BaseLayer, IBaseLayer, IBaseLayerMisc, IBaseLayerProps } from "./BaseLayer";
|
|
4
4
|
import { LayersManager } from "../managers";
|
|
5
5
|
export interface IPath2DLayer extends IBaseLayer {
|
|
6
|
+
/**
|
|
7
|
+
* The type of the layer, which is `Path`.
|
|
8
|
+
*/
|
|
6
9
|
type: LayerType.Path;
|
|
10
|
+
/**
|
|
11
|
+
* The properties specific to the Path2D Layer.
|
|
12
|
+
*/
|
|
7
13
|
props: IPath2DLayerProps;
|
|
8
14
|
}
|
|
9
15
|
export interface IPath2DLayerProps extends IBaseLayerProps {
|
|
16
|
+
/**
|
|
17
|
+
* The Path2D object representing the shape of the layer.
|
|
18
|
+
*/
|
|
10
19
|
path2D: Path2D;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the layer is filled.
|
|
22
|
+
*/
|
|
23
|
+
filled: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The fill style (color or pattern) of the layer.
|
|
26
|
+
*/
|
|
27
|
+
fillStyle: ColorType;
|
|
11
28
|
/**
|
|
12
29
|
* The stroke properties of the Path2D.
|
|
13
30
|
*/
|
|
@@ -141,7 +141,9 @@ class Path2DLayer extends BaseLayer_1.BaseLayer {
|
|
|
141
141
|
ctx.save();
|
|
142
142
|
let fillStyle = await (0, utils_1.parseFillStyle)(ctx, this.props.fillStyle, { debug, manager });
|
|
143
143
|
(0, utils_1.transform)(ctx, this.props.transform, { width: 0, height: 0, x: 0, y: 0, type: this.type });
|
|
144
|
+
(0, utils_1.drawShadow)(ctx, this.props.shadow);
|
|
144
145
|
(0, utils_1.opacity)(ctx, this.props.opacity);
|
|
146
|
+
(0, utils_1.filters)(ctx, this.props.filter);
|
|
145
147
|
ctx.globalCompositeOperation = this.props.globalComposite;
|
|
146
148
|
if (this.props.clipPath) {
|
|
147
149
|
ctx.clip(this.props.path2D);
|
|
@@ -184,6 +186,8 @@ class Path2DLayer extends BaseLayer_1.BaseLayer {
|
|
|
184
186
|
validateProps(data) {
|
|
185
187
|
return {
|
|
186
188
|
...super.validateProps(data),
|
|
189
|
+
filled: data.filled || true,
|
|
190
|
+
fillStyle: data.fillStyle || '#000000',
|
|
187
191
|
path2D: data.path2D || new canvas_1.Path2D(),
|
|
188
192
|
stroke: {
|
|
189
193
|
width: data.stroke?.width || 1,
|
|
@@ -27,6 +27,14 @@ export interface IQuadraticLayerProps extends IBaseLayerProps {
|
|
|
27
27
|
* The end point of the quadratic curve, including x and y coordinates.
|
|
28
28
|
*/
|
|
29
29
|
endPoint: Point;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the layer is filled.
|
|
32
|
+
*/
|
|
33
|
+
filled: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The fill style (color or pattern) of the layer.
|
|
36
|
+
*/
|
|
37
|
+
fillStyle: ColorType;
|
|
30
38
|
/**
|
|
31
39
|
* The stroke properties of the quadratic curve.
|
|
32
40
|
*/
|
|
@@ -160,6 +160,8 @@ class QuadraticLayer extends BaseLayer_1.BaseLayer {
|
|
|
160
160
|
validateProps(data) {
|
|
161
161
|
return {
|
|
162
162
|
...super.validateProps(data),
|
|
163
|
+
filled: data.filled || false,
|
|
164
|
+
fillStyle: data.fillStyle || '#000000',
|
|
163
165
|
centring: data.centring || types_1.Centring.None,
|
|
164
166
|
controlPoints: data.controlPoints || [{ x: 0, y: 0 }],
|
|
165
167
|
endPoint: data.endPoint || { x: 0, y: 0 },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseLayer, IBaseLayer, IBaseLayerMisc, IBaseLayerProps } from "./BaseLayer";
|
|
2
|
-
import { ScaleType, ColorType, AnyWeight, AnyTextAlign, AnyTextBaseline, AnyTextDirection, LineCap, LineJoin, LayerType } from "../../types";
|
|
2
|
+
import { ScaleType, ColorType, AnyWeight, AnyTextAlign, AnyTextBaseline, AnyTextDirection, LineCap, LineJoin, LayerType, SubStringColor } from "../../types";
|
|
3
3
|
import { Canvas, SKRSContext2D, SvgCanvas } from "@napi-rs/canvas";
|
|
4
4
|
import { LayersManager } from "../managers";
|
|
5
5
|
/**
|
|
@@ -23,6 +23,18 @@ export interface ITextLayerProps extends IBaseLayerProps {
|
|
|
23
23
|
* The text content of the layer.
|
|
24
24
|
*/
|
|
25
25
|
text: string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the layer is filled.
|
|
28
|
+
*/
|
|
29
|
+
filled: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The fill style (color or pattern) of the layer.
|
|
32
|
+
*/
|
|
33
|
+
fillStyle: ColorType;
|
|
34
|
+
/**
|
|
35
|
+
* Array of substring color configurations for partial text coloring.
|
|
36
|
+
*/
|
|
37
|
+
subStringColors?: SubStringColor[];
|
|
26
38
|
/**
|
|
27
39
|
* The font configuration for the text.
|
|
28
40
|
*/
|
|
@@ -151,20 +163,20 @@ export declare class TextLayer extends BaseLayer<ITextLayerProps> {
|
|
|
151
163
|
}, size?: number, weight?: AnyWeight): this;
|
|
152
164
|
/**
|
|
153
165
|
* Configures the multiline properties of the text layer.
|
|
154
|
-
* @param {boolean} [enabled] - Whether multiline is enabled.
|
|
155
166
|
* @param {ScaleType} [width] - The width of the multiline text area.
|
|
156
167
|
* @param {ScaleType} [height] - The height of the multiline text area.
|
|
157
168
|
* @param {number} [spacing] - The spacing between lines (optional).
|
|
158
169
|
* @returns {this} The current instance for chaining.
|
|
159
170
|
*/
|
|
160
|
-
setMultiline(
|
|
171
|
+
setMultiline(width: ScaleType, height: ScaleType, spacing?: number): this;
|
|
161
172
|
/**
|
|
162
173
|
* Sets the color of the text layer.
|
|
163
|
-
* @param {ColorType} [color] - The color of the text.
|
|
174
|
+
* @param {ColorType} [color] - The base color of the text.
|
|
175
|
+
* @param {SubStringColor[]} [sub] - Optional substring colors for partial text coloring.
|
|
164
176
|
* @returns {this} The current instance for chaining.
|
|
165
177
|
* @throws {LazyError} If the color is not provided or invalid.
|
|
166
178
|
*/
|
|
167
|
-
setColor(color: ColorType): this;
|
|
179
|
+
setColor(color: ColorType, ...sub: SubStringColor[]): this;
|
|
168
180
|
/**
|
|
169
181
|
* Sets the alignment of the text layer.
|
|
170
182
|
* @param {AnyTextAlign} [align] - The alignment of the text.
|
|
@@ -233,6 +245,7 @@ export declare class TextLayer extends BaseLayer<ITextLayerProps> {
|
|
|
233
245
|
* @param {number} [x] - The x-coordinate of the text.
|
|
234
246
|
* @param {number} [y] - The y-coordinate of the text.
|
|
235
247
|
* @param {number} [w] - The width of the text area.
|
|
248
|
+
* @param {number} [textOffset] - The offset of this text segment in the original full text (for multiline support).
|
|
236
249
|
*/
|
|
237
250
|
private drawText;
|
|
238
251
|
/**
|
|
@@ -242,8 +255,8 @@ export declare class TextLayer extends BaseLayer<ITextLayerProps> {
|
|
|
242
255
|
toJSON(): ITextLayer;
|
|
243
256
|
/**
|
|
244
257
|
* Validates the properties of the Text Layer.
|
|
245
|
-
* @param {ITextLayerProps} [
|
|
258
|
+
* @param {ITextLayerProps} [data] - The properties to validate.
|
|
246
259
|
* @returns {ITextLayerProps} The validated properties.
|
|
247
260
|
*/
|
|
248
|
-
protected validateProps(
|
|
261
|
+
protected validateProps(data: ITextLayerProps): ITextLayerProps;
|
|
249
262
|
}
|
|
@@ -63,15 +63,14 @@ class TextLayer extends BaseLayer_1.BaseLayer {
|
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Configures the multiline properties of the text layer.
|
|
66
|
-
* @param {boolean} [enabled] - Whether multiline is enabled.
|
|
67
66
|
* @param {ScaleType} [width] - The width of the multiline text area.
|
|
68
67
|
* @param {ScaleType} [height] - The height of the multiline text area.
|
|
69
68
|
* @param {number} [spacing] - The spacing between lines (optional).
|
|
70
69
|
* @returns {this} The current instance for chaining.
|
|
71
70
|
*/
|
|
72
|
-
setMultiline(
|
|
71
|
+
setMultiline(width, height, spacing) {
|
|
73
72
|
this.props.multiline = {
|
|
74
|
-
enabled:
|
|
73
|
+
enabled: true,
|
|
75
74
|
spacing: spacing || 1.1,
|
|
76
75
|
};
|
|
77
76
|
this.props.size = {
|
|
@@ -82,16 +81,20 @@ class TextLayer extends BaseLayer_1.BaseLayer {
|
|
|
82
81
|
}
|
|
83
82
|
/**
|
|
84
83
|
* Sets the color of the text layer.
|
|
85
|
-
* @param {ColorType} [color] - The color of the text.
|
|
84
|
+
* @param {ColorType} [color] - The base color of the text.
|
|
85
|
+
* @param {SubStringColor[]} [sub] - Optional substring colors for partial text coloring.
|
|
86
86
|
* @returns {this} The current instance for chaining.
|
|
87
87
|
* @throws {LazyError} If the color is not provided or invalid.
|
|
88
88
|
*/
|
|
89
|
-
setColor(color) {
|
|
89
|
+
setColor(color, ...sub) {
|
|
90
90
|
if (!color)
|
|
91
91
|
throw new LazyUtil_1.LazyError('The color of the layer must be provided');
|
|
92
92
|
if (!(0, utils_1.isColor)(color))
|
|
93
93
|
throw new LazyUtil_1.LazyError('The color of the layer must be a valid color');
|
|
94
94
|
this.props.fillStyle = color;
|
|
95
|
+
if (sub && sub.length > 0) {
|
|
96
|
+
this.props.subStringColors = sub;
|
|
97
|
+
}
|
|
95
98
|
return this;
|
|
96
99
|
}
|
|
97
100
|
/**
|
|
@@ -222,10 +225,12 @@ class TextLayer extends BaseLayer_1.BaseLayer {
|
|
|
222
225
|
let ym = y;
|
|
223
226
|
lines = [];
|
|
224
227
|
let line = '';
|
|
228
|
+
let charOffset = 0; // Track position in original text
|
|
225
229
|
for (let word of words) {
|
|
226
230
|
let linePlus = line + word + ' ';
|
|
227
231
|
if (ctx.measureText(linePlus).width > w) {
|
|
228
|
-
lines.push({ text: line, x: xm, y: ym });
|
|
232
|
+
lines.push({ text: line, x: xm, y: ym, startOffset: charOffset });
|
|
233
|
+
charOffset += line.length;
|
|
229
234
|
line = word + ' ';
|
|
230
235
|
ym += lineHeight;
|
|
231
236
|
}
|
|
@@ -233,17 +238,17 @@ class TextLayer extends BaseLayer_1.BaseLayer {
|
|
|
233
238
|
line = linePlus;
|
|
234
239
|
}
|
|
235
240
|
}
|
|
236
|
-
lines.push({ text: line, x: xm, y: ym });
|
|
241
|
+
lines.push({ text: line, x: xm, y: ym, startOffset: charOffset });
|
|
237
242
|
if (ym > ym + h)
|
|
238
243
|
break;
|
|
239
244
|
}
|
|
240
245
|
for (let line of lines) {
|
|
241
|
-
this.drawText(this.props, ctx, fillStyle, line.text, line.x, line.y, w);
|
|
246
|
+
this.drawText(this.props, ctx, fillStyle, line.text, line.x, line.y, w, line.startOffset);
|
|
242
247
|
}
|
|
243
248
|
}
|
|
244
249
|
else {
|
|
245
250
|
ctx.font = `${this.props.font.weight} ${this.props.font.size}px ${this.props.font.family}`;
|
|
246
|
-
this.drawText(this.props, ctx, fillStyle, this.props.text, x, y, w);
|
|
251
|
+
this.drawText(this.props, ctx, fillStyle, this.props.text, x, y, w, 0);
|
|
247
252
|
}
|
|
248
253
|
ctx.closePath();
|
|
249
254
|
ctx.restore();
|
|
@@ -257,22 +262,113 @@ class TextLayer extends BaseLayer_1.BaseLayer {
|
|
|
257
262
|
* @param {number} [x] - The x-coordinate of the text.
|
|
258
263
|
* @param {number} [y] - The y-coordinate of the text.
|
|
259
264
|
* @param {number} [w] - The width of the text area.
|
|
265
|
+
* @param {number} [textOffset] - The offset of this text segment in the original full text (for multiline support).
|
|
260
266
|
*/
|
|
261
|
-
drawText(props, ctx, fillStyle, text, x, y, w) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
267
|
+
drawText(props, ctx, fillStyle, text, x, y, w, textOffset = 0) {
|
|
268
|
+
// If no substring colors are defined, draw normally
|
|
269
|
+
if (!props.subStringColors || props.subStringColors.length === 0) {
|
|
270
|
+
if (props.filled) {
|
|
271
|
+
ctx.fillStyle = fillStyle;
|
|
272
|
+
ctx.fillText(text, x, y, w);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
ctx.strokeStyle = fillStyle;
|
|
276
|
+
ctx.lineWidth = props.stroke?.width || 1;
|
|
277
|
+
ctx.lineCap = props.stroke?.cap || 'butt';
|
|
278
|
+
ctx.lineJoin = props.stroke?.join || 'miter';
|
|
279
|
+
ctx.miterLimit = props.stroke?.miterLimit || 10;
|
|
280
|
+
ctx.lineDashOffset = props.stroke?.dashOffset || 0;
|
|
281
|
+
ctx.setLineDash(props.stroke?.dash || []);
|
|
282
|
+
ctx.strokeText(text, x, y, w);
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
265
285
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
286
|
+
// Draw text with substring colors
|
|
287
|
+
const textLength = text.length;
|
|
288
|
+
let currentX = x;
|
|
289
|
+
// Save original text alignment and set to left for manual positioning
|
|
290
|
+
const originalAlign = ctx.textAlign;
|
|
291
|
+
ctx.textAlign = 'left';
|
|
292
|
+
// Adjust starting X based on text alignment
|
|
293
|
+
const alignValue = props.align;
|
|
294
|
+
if (alignValue === types_1.TextAlign.Center || alignValue === 'center') {
|
|
295
|
+
const totalWidth = ctx.measureText(text).width;
|
|
296
|
+
currentX = x - totalWidth / 2;
|
|
297
|
+
}
|
|
298
|
+
else if (alignValue === types_1.TextAlign.Right || alignValue === 'right' || alignValue === types_1.TextAlign.End || alignValue === 'end') {
|
|
299
|
+
const totalWidth = ctx.measureText(text).width;
|
|
300
|
+
currentX = x - totalWidth;
|
|
301
|
+
}
|
|
302
|
+
// Create segments based on substring colors
|
|
303
|
+
const segments = [];
|
|
304
|
+
// Sort substring colors by start position
|
|
305
|
+
const sortedColors = [...props.subStringColors].sort((a, b) => a.start - b.start);
|
|
306
|
+
let currentIndex = 0;
|
|
307
|
+
for (const subColor of sortedColors) {
|
|
308
|
+
// Adjust positions based on textOffset (for multiline support)
|
|
309
|
+
const globalStart = subColor.start;
|
|
310
|
+
const globalEnd = subColor.end;
|
|
311
|
+
const lineStart = textOffset;
|
|
312
|
+
const lineEnd = textOffset + textLength;
|
|
313
|
+
// Skip if this color segment doesn't overlap with current line
|
|
314
|
+
if (globalEnd <= lineStart || globalStart >= lineEnd) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
// Calculate local positions within this line
|
|
318
|
+
const localStart = Math.max(0, globalStart - lineStart);
|
|
319
|
+
const localEnd = Math.min(textLength, globalEnd - lineStart);
|
|
320
|
+
// Add base color segment before this substring color
|
|
321
|
+
if (currentIndex < localStart) {
|
|
322
|
+
segments.push({
|
|
323
|
+
text: text.substring(currentIndex, localStart),
|
|
324
|
+
color: fillStyle,
|
|
325
|
+
start: currentIndex,
|
|
326
|
+
end: localStart
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// Add colored substring
|
|
330
|
+
if (localStart < localEnd) {
|
|
331
|
+
segments.push({
|
|
332
|
+
text: text.substring(localStart, localEnd),
|
|
333
|
+
color: subColor.color,
|
|
334
|
+
start: localStart,
|
|
335
|
+
end: localEnd
|
|
336
|
+
});
|
|
337
|
+
currentIndex = localEnd;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Add remaining text with base color
|
|
341
|
+
if (currentIndex < textLength) {
|
|
342
|
+
segments.push({
|
|
343
|
+
text: text.substring(currentIndex),
|
|
344
|
+
color: fillStyle,
|
|
345
|
+
start: currentIndex,
|
|
346
|
+
end: textLength
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// Draw each segment
|
|
350
|
+
for (const segment of segments) {
|
|
351
|
+
if (segment.text.length === 0)
|
|
352
|
+
continue;
|
|
353
|
+
const segmentWidth = ctx.measureText(segment.text).width;
|
|
354
|
+
if (props.filled) {
|
|
355
|
+
ctx.fillStyle = segment.color;
|
|
356
|
+
ctx.fillText(segment.text, currentX, y);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
ctx.strokeStyle = segment.color;
|
|
360
|
+
ctx.lineWidth = props.stroke?.width || 1;
|
|
361
|
+
ctx.lineCap = props.stroke?.cap || 'butt';
|
|
362
|
+
ctx.lineJoin = props.stroke?.join || 'miter';
|
|
363
|
+
ctx.miterLimit = props.stroke?.miterLimit || 10;
|
|
364
|
+
ctx.lineDashOffset = props.stroke?.dashOffset || 0;
|
|
365
|
+
ctx.setLineDash(props.stroke?.dash || []);
|
|
366
|
+
ctx.strokeText(segment.text, currentX, y);
|
|
367
|
+
}
|
|
368
|
+
currentX += segmentWidth;
|
|
275
369
|
}
|
|
370
|
+
// Restore original text alignment
|
|
371
|
+
ctx.textAlign = originalAlign;
|
|
276
372
|
}
|
|
277
373
|
/**
|
|
278
374
|
* Converts the Text Layer to a JSON representation.
|
|
@@ -290,29 +386,29 @@ class TextLayer extends BaseLayer_1.BaseLayer {
|
|
|
290
386
|
}
|
|
291
387
|
/**
|
|
292
388
|
* Validates the properties of the Text Layer.
|
|
293
|
-
* @param {ITextLayerProps} [
|
|
389
|
+
* @param {ITextLayerProps} [data] - The properties to validate.
|
|
294
390
|
* @returns {ITextLayerProps} The validated properties.
|
|
295
391
|
*/
|
|
296
|
-
validateProps(
|
|
392
|
+
validateProps(data) {
|
|
297
393
|
return {
|
|
298
|
-
...super.validateProps(
|
|
299
|
-
|
|
394
|
+
...super.validateProps(data),
|
|
395
|
+
filled: data.filled || true,
|
|
396
|
+
fillStyle: data.fillStyle || '#000000',
|
|
397
|
+
text: data.text || "",
|
|
300
398
|
font: {
|
|
301
|
-
family:
|
|
302
|
-
size:
|
|
303
|
-
weight:
|
|
399
|
+
family: data.font?.family || "Arial",
|
|
400
|
+
size: data.font?.size || 16,
|
|
401
|
+
weight: data.font?.weight || types_1.FontWeight.Regular,
|
|
304
402
|
},
|
|
305
403
|
multiline: {
|
|
306
|
-
enabled:
|
|
307
|
-
spacing:
|
|
404
|
+
enabled: data.multiline?.enabled || false,
|
|
405
|
+
spacing: data.multiline?.spacing || 1.1,
|
|
308
406
|
},
|
|
309
407
|
size: {
|
|
310
|
-
width:
|
|
311
|
-
height:
|
|
408
|
+
width: data.size?.width || "vw",
|
|
409
|
+
height: data.size?.height || 0,
|
|
312
410
|
},
|
|
313
|
-
align:
|
|
314
|
-
fillStyle: props.fillStyle || "#000000",
|
|
315
|
-
filled: props.filled !== undefined ? props.filled : true,
|
|
411
|
+
align: data.align || types_1.TextAlign.Left,
|
|
316
412
|
};
|
|
317
413
|
}
|
|
318
414
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -96,4 +96,10 @@ export interface Transform {
|
|
|
96
96
|
matrix: DOMMatrix2DInit;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
export type
|
|
99
|
+
export type RadiusCorner = 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | 'all';
|
|
100
|
+
|
|
101
|
+
export type SubStringColor = {
|
|
102
|
+
color: StringColorType;
|
|
103
|
+
start: number;
|
|
104
|
+
end: number;
|
|
105
|
+
}
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { AnyCentring, AnyLayer, AnyTextAlign, ColorType, PointNumber, ScaleType, Transform } from "../types";
|
|
1
|
+
import type { AnyCentring, AnyLayer, AnyTextAlign, ColorType, PointNumber, ScaleType, SubStringColor, Transform } from "../types";
|
|
2
2
|
import { LayerType } from "../types";
|
|
3
3
|
import { Canvas, SKRSContext2D, SvgCanvas } from "@napi-rs/canvas";
|
|
4
4
|
import { LayersManager } from "../structures/managers";
|
|
5
5
|
import { Group } from "../structures/components";
|
|
6
6
|
export declare function generateID(type: string): string;
|
|
7
|
-
export declare function isColor(v: ColorType): "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
|
|
7
|
+
export declare function isColor(v: ColorType | SubStringColor): "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
|
|
8
8
|
export declare function parseToNormal(v: ScaleType, ctx: SKRSContext2D, canvas: Canvas | SvgCanvas, layer?: {
|
|
9
9
|
width: number;
|
|
10
10
|
height: number;
|
|
@@ -35,7 +35,7 @@ export declare function parser(ctx: SKRSContext2D, canvas: Canvas | SvgCanvas, m
|
|
|
35
35
|
export declare function drawShadow(ctx: SKRSContext2D, shadow: any): void;
|
|
36
36
|
export declare function opacity(ctx: SKRSContext2D, opacity?: number): void;
|
|
37
37
|
export declare function filters(ctx: SKRSContext2D, filters: string | null | undefined): void;
|
|
38
|
-
export declare function parseFillStyle(ctx: SKRSContext2D, color: ColorType, opts: {
|
|
38
|
+
export declare function parseFillStyle(ctx: SKRSContext2D, color: ColorType | SubStringColor, opts: {
|
|
39
39
|
debug?: boolean;
|
|
40
40
|
layer?: {
|
|
41
41
|
width: number;
|
|
@@ -45,7 +45,7 @@ export declare function parseFillStyle(ctx: SKRSContext2D, color: ColorType, opt
|
|
|
45
45
|
align: AnyCentring;
|
|
46
46
|
};
|
|
47
47
|
manager?: LayersManager;
|
|
48
|
-
}): string | Promise<CanvasPattern
|
|
48
|
+
}): string | CanvasGradient | Promise<CanvasPattern>;
|
|
49
49
|
export declare function transform(ctx: SKRSContext2D, transform: Transform, layer?: {
|
|
50
50
|
width: number;
|
|
51
51
|
height: number;
|
package/dist/utils/utils.js
CHANGED
|
@@ -33,7 +33,7 @@ let rgbaReg = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*(0|0?\.\d+|1(\.0)?)\)$/;
|
|
|
33
33
|
let hslReg = /^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/;
|
|
34
34
|
let hslaReg = /^hsla\((\d+),\s*(\d+)%,\s*(\d+)%,\s*(0|0?\.\d+|1(\.0)?)\)$/;
|
|
35
35
|
function isColor(v) {
|
|
36
|
-
return typeof (v === 'string' && (hexReg.test(v) || rgbReg.test(v) || rgbaReg.test(v) || hslReg.test(v) || hslaReg.test(v))) || v instanceof helpers_1.Gradient || v instanceof helpers_1.Pattern;
|
|
36
|
+
return typeof (v === 'string' && (hexReg.test(v) || rgbReg.test(v) || rgbaReg.test(v) || hslReg.test(v) || hslaReg.test(v))) || v instanceof helpers_1.Gradient || v instanceof helpers_1.Pattern || (typeof v === 'object' && (v.start && v.end && v.color));
|
|
37
37
|
}
|
|
38
38
|
function parseToNormal(v, ctx, canvas, layer = { width: 0, height: 0 }, options = { vertical: false, layer: false }, manager) {
|
|
39
39
|
if (typeof v === 'number')
|
|
@@ -149,7 +149,13 @@ function parseFillStyle(ctx, color, opts) {
|
|
|
149
149
|
if (color instanceof helpers_1.Gradient || color instanceof helpers_1.Pattern) {
|
|
150
150
|
return color.draw(ctx, opts);
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
if (typeof color === 'object' && color.start && color.end && color.color) {
|
|
153
|
+
return color.color;
|
|
154
|
+
}
|
|
155
|
+
else if (typeof color === 'string') {
|
|
156
|
+
return color;
|
|
157
|
+
}
|
|
158
|
+
return '#000000';
|
|
153
159
|
}
|
|
154
160
|
function transform(ctx, transform, layer = { width: 0, height: 0, x: 0, y: 0, type: types_1.LayerType.Morph }, extra = { text: '', textAlign: types_1.TextAlign.Left, fontSize: 0, multiline: false }) {
|
|
155
161
|
if (transform) {
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@nmmty/lazycanvas",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "A simple way to interact with @napi-rs/canvas in an advanced way!",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"node
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
},
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@nmmty/lazycanvas",
|
|
3
|
+
"version": "0.6.3",
|
|
4
|
+
"description": "A simple way to interact with @napi-rs/canvas in an advanced way!",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "tsc ./test/test.ts && node ./test/test.js",
|
|
9
|
+
"centring": "tsc ./test/centring.ts && node ./test/centring.js",
|
|
10
|
+
"logo": "tsc ./test/logo.ts && node ./test/logo.js",
|
|
11
|
+
"text": "tsc ./test/text.ts && node ./test/text.js",
|
|
12
|
+
"animation": "tsc ./test/animation.ts && node ./test/animation.js",
|
|
13
|
+
"iotest": "tsc ./test/iotest.ts && node ./test/iotest.js",
|
|
14
|
+
"gradient": "tsc ./test/gradient.ts && node ./test/gradient.js",
|
|
15
|
+
"docgen": "tsx docgen.ts && tsx ./scripts/post-docgen.ts",
|
|
16
|
+
"lint": "eslint ./src --ext .ts",
|
|
17
|
+
"lint:fix": "eslint ./src --ext .ts --fix",
|
|
18
|
+
"font": "tsx ./scripts/font-gen.ts",
|
|
19
|
+
"build": "tsc && tsx ./scripts/post-build.ts"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/NMMTY/LazyCanvas.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"canvas",
|
|
27
|
+
"@napi-rs/canvas",
|
|
28
|
+
"node-canvas",
|
|
29
|
+
"easy",
|
|
30
|
+
"simple"
|
|
31
|
+
],
|
|
32
|
+
"author": "NMMTY",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/NMMTY/LazyCanvas/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/NMMTY/LazyCanvas#readme",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@napi-rs/canvas": "^0.1.72",
|
|
40
|
+
"gifenc": "^1.0.3",
|
|
41
|
+
"js-yaml": "^4.1.0",
|
|
42
|
+
"path": "^0.12.7",
|
|
43
|
+
"svgson": "^5.3.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@hitomihiumi/colors.ts": "^1.0.3",
|
|
47
|
+
"@hitomihiumi/micro-docgen": "workspace:*",
|
|
48
|
+
"@types/js-yaml": "^4.0.9",
|
|
49
|
+
"@types/node": "^22.10.2",
|
|
50
|
+
"@typescript-eslint/utils": "^8.39.1",
|
|
51
|
+
"ava": "^6.2.0",
|
|
52
|
+
"eslint": "^9.23.0",
|
|
53
|
+
"eslint-config-neon": "^0.2.7",
|
|
54
|
+
"lodash.merge": "^4.6.2",
|
|
55
|
+
"tslib": "^2.8.1",
|
|
56
|
+
"tsx": "^4.19.2",
|
|
57
|
+
"typescript": "^5.4.5"
|
|
58
|
+
}
|
|
59
|
+
}
|