@twick/2d 0.13.0 → 0.14.2
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/LICENSE +21 -0
- package/editor/editor/tsconfig.build.tsbuildinfo +1 -1
- package/lib/components/Audio.js +3 -3
- package/lib/components/Img.js +23 -23
- package/lib/components/Line.js +31 -31
- package/lib/components/Media.d.ts +1 -1
- package/lib/components/Media.d.ts.map +1 -1
- package/lib/components/Media.js +26 -26
- package/lib/components/Spline.js +25 -25
- package/lib/components/Video.js +3 -3
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/src/editor/NodeInspectorConfig.tsx +76 -76
- package/src/editor/PreviewOverlayConfig.tsx +67 -67
- package/src/editor/Provider.tsx +93 -93
- package/src/editor/SceneGraphTabConfig.tsx +81 -81
- package/src/editor/icons/CircleIcon.tsx +7 -7
- package/src/editor/icons/CodeBlockIcon.tsx +8 -8
- package/src/editor/icons/CurveIcon.tsx +7 -7
- package/src/editor/icons/GridIcon.tsx +7 -7
- package/src/editor/icons/IconMap.ts +35 -35
- package/src/editor/icons/ImgIcon.tsx +8 -8
- package/src/editor/icons/LayoutIcon.tsx +9 -9
- package/src/editor/icons/LineIcon.tsx +7 -7
- package/src/editor/icons/NodeIcon.tsx +7 -7
- package/src/editor/icons/RayIcon.tsx +7 -7
- package/src/editor/icons/RectIcon.tsx +7 -7
- package/src/editor/icons/ShapeIcon.tsx +7 -7
- package/src/editor/icons/TxtIcon.tsx +8 -8
- package/src/editor/icons/VideoIcon.tsx +7 -7
- package/src/editor/icons/View2DIcon.tsx +10 -10
- package/src/editor/index.ts +17 -17
- package/src/editor/tree/DetachedRoot.tsx +23 -23
- package/src/editor/tree/NodeElement.tsx +74 -74
- package/src/editor/tree/TreeElement.tsx +72 -72
- package/src/editor/tree/TreeRoot.tsx +10 -10
- package/src/editor/tree/ViewRoot.tsx +20 -20
- package/src/editor/tree/index.module.scss +38 -38
- package/src/editor/tree/index.ts +3 -3
- package/src/editor/tsconfig.build.json +5 -5
- package/src/editor/tsconfig.json +12 -12
- package/src/editor/tsdoc.json +4 -4
- package/src/editor/vite-env.d.ts +1 -1
- package/src/lib/code/CodeCursor.ts +445 -445
- package/src/lib/code/CodeDiffer.ts +78 -78
- package/src/lib/code/CodeFragment.ts +97 -97
- package/src/lib/code/CodeHighlighter.ts +75 -75
- package/src/lib/code/CodeMetrics.ts +47 -47
- package/src/lib/code/CodeRange.test.ts +74 -74
- package/src/lib/code/CodeRange.ts +216 -216
- package/src/lib/code/CodeScope.ts +101 -101
- package/src/lib/code/CodeSelection.ts +24 -24
- package/src/lib/code/CodeSignal.ts +327 -327
- package/src/lib/code/CodeTokenizer.ts +54 -54
- package/src/lib/code/DefaultHighlightStyle.ts +98 -98
- package/src/lib/code/LezerHighlighter.ts +113 -113
- package/src/lib/code/diff.test.ts +311 -311
- package/src/lib/code/diff.ts +319 -319
- package/src/lib/code/extractRange.ts +126 -126
- package/src/lib/code/index.ts +13 -13
- package/src/lib/components/Audio.ts +168 -168
- package/src/lib/components/Bezier.ts +105 -105
- package/src/lib/components/Circle.ts +266 -266
- package/src/lib/components/Code.ts +526 -526
- package/src/lib/components/CodeBlock.ts +576 -576
- package/src/lib/components/CubicBezier.ts +112 -112
- package/src/lib/components/Curve.ts +455 -455
- package/src/lib/components/Grid.ts +135 -135
- package/src/lib/components/Icon.ts +96 -96
- package/src/lib/components/Img.ts +319 -319
- package/src/lib/components/Knot.ts +157 -157
- package/src/lib/components/Latex.ts +122 -122
- package/src/lib/components/Layout.ts +1092 -1092
- package/src/lib/components/Line.ts +429 -429
- package/src/lib/components/Media.ts +576 -576
- package/src/lib/components/Node.ts +1940 -1940
- package/src/lib/components/Path.ts +137 -137
- package/src/lib/components/Polygon.ts +171 -171
- package/src/lib/components/QuadBezier.ts +100 -100
- package/src/lib/components/Ray.ts +125 -125
- package/src/lib/components/Rect.ts +187 -187
- package/src/lib/components/Rive.ts +156 -156
- package/src/lib/components/SVG.ts +797 -797
- package/src/lib/components/Shape.ts +143 -143
- package/src/lib/components/Spline.ts +344 -344
- package/src/lib/components/Txt.test.tsx +81 -81
- package/src/lib/components/Txt.ts +203 -203
- package/src/lib/components/TxtLeaf.ts +205 -205
- package/src/lib/components/Video.ts +461 -461
- package/src/lib/components/View2D.ts +98 -98
- package/src/lib/components/__tests__/children.test.tsx +142 -142
- package/src/lib/components/__tests__/clone.test.tsx +126 -126
- package/src/lib/components/__tests__/generatorTest.ts +28 -28
- package/src/lib/components/__tests__/mockScene2D.ts +45 -45
- package/src/lib/components/__tests__/query.test.tsx +122 -122
- package/src/lib/components/__tests__/state.test.tsx +60 -60
- package/src/lib/components/index.ts +28 -28
- package/src/lib/components/types.ts +35 -35
- package/src/lib/curves/ArcSegment.ts +159 -159
- package/src/lib/curves/CircleSegment.ts +77 -77
- package/src/lib/curves/CubicBezierSegment.ts +78 -78
- package/src/lib/curves/CurveDrawingInfo.ts +11 -11
- package/src/lib/curves/CurvePoint.ts +15 -15
- package/src/lib/curves/CurveProfile.ts +7 -7
- package/src/lib/curves/KnotInfo.ts +10 -10
- package/src/lib/curves/LineSegment.ts +62 -62
- package/src/lib/curves/Polynomial.ts +355 -355
- package/src/lib/curves/Polynomial2D.ts +62 -62
- package/src/lib/curves/PolynomialSegment.ts +124 -124
- package/src/lib/curves/QuadBezierSegment.ts +64 -64
- package/src/lib/curves/Segment.ts +17 -17
- package/src/lib/curves/UniformPolynomialCurveSampler.ts +94 -94
- package/src/lib/curves/createCurveProfileLerp.ts +471 -471
- package/src/lib/curves/getBezierSplineProfile.ts +223 -223
- package/src/lib/curves/getCircleProfile.ts +86 -86
- package/src/lib/curves/getPathProfile.ts +178 -178
- package/src/lib/curves/getPointAtDistance.ts +21 -21
- package/src/lib/curves/getPolylineProfile.test.ts +21 -21
- package/src/lib/curves/getPolylineProfile.ts +89 -89
- package/src/lib/curves/getRectProfile.ts +139 -139
- package/src/lib/curves/index.ts +16 -16
- package/src/lib/decorators/canvasStyleSignal.ts +16 -16
- package/src/lib/decorators/colorSignal.ts +9 -9
- package/src/lib/decorators/compound.ts +72 -72
- package/src/lib/decorators/computed.ts +18 -18
- package/src/lib/decorators/defaultStyle.ts +18 -18
- package/src/lib/decorators/filtersSignal.ts +136 -136
- package/src/lib/decorators/index.ts +10 -10
- package/src/lib/decorators/initializers.ts +32 -32
- package/src/lib/decorators/nodeName.ts +13 -13
- package/src/lib/decorators/signal.test.ts +90 -90
- package/src/lib/decorators/signal.ts +345 -345
- package/src/lib/decorators/spacingSignal.ts +15 -15
- package/src/lib/decorators/vector2Signal.ts +30 -30
- package/src/lib/globals.d.ts +2 -2
- package/src/lib/index.ts +8 -8
- package/src/lib/jsx-dev-runtime.ts +2 -2
- package/src/lib/jsx-runtime.ts +46 -46
- package/src/lib/parse-svg-path.d.ts +14 -14
- package/src/lib/partials/Filter.ts +180 -180
- package/src/lib/partials/Gradient.ts +102 -102
- package/src/lib/partials/Pattern.ts +34 -34
- package/src/lib/partials/ShaderConfig.ts +117 -117
- package/src/lib/partials/index.ts +4 -4
- package/src/lib/partials/types.ts +58 -58
- package/src/lib/scenes/Scene2D.ts +242 -242
- package/src/lib/scenes/index.ts +3 -3
- package/src/lib/scenes/makeScene2D.ts +16 -16
- package/src/lib/scenes/useScene2D.ts +6 -6
- package/src/lib/tsconfig.build.json +5 -5
- package/src/lib/tsconfig.json +10 -10
- package/src/lib/tsdoc.json +4 -4
- package/src/lib/utils/CanvasUtils.ts +306 -306
- package/src/lib/utils/diff.test.ts +453 -453
- package/src/lib/utils/diff.ts +148 -148
- package/src/lib/utils/index.ts +2 -2
- package/src/lib/utils/is.ts +11 -11
- package/src/lib/utils/makeSignalExtensions.ts +30 -30
- package/src/lib/utils/video/declarations.d.ts +1 -1
- package/src/lib/utils/video/ffmpeg-client.ts +50 -50
- package/src/lib/utils/video/mp4-parser-manager.ts +72 -72
- package/src/lib/utils/video/parser/index.ts +1 -1
- package/src/lib/utils/video/parser/parser.ts +257 -257
- package/src/lib/utils/video/parser/sampler.ts +72 -72
- package/src/lib/utils/video/parser/segment.ts +302 -302
- package/src/lib/utils/video/parser/sink.ts +29 -29
- package/src/lib/utils/video/parser/utils.ts +31 -31
- package/src/tsconfig.base.json +19 -19
- package/src/tsconfig.build.json +8 -8
- package/src/tsconfig.json +5 -5
- package/tsconfig.project.json +7 -7
|
@@ -1,205 +1,205 @@
|
|
|
1
|
-
import type {SignalValue, SimpleSignal} from '@twick/core';
|
|
2
|
-
import {BBox, capitalize, lazy, textLerp} from '@twick/core';
|
|
3
|
-
import {
|
|
4
|
-
computed,
|
|
5
|
-
initial,
|
|
6
|
-
interpolation,
|
|
7
|
-
nodeName,
|
|
8
|
-
signal,
|
|
9
|
-
} from '../decorators';
|
|
10
|
-
import type {ShapeProps} from './Shape';
|
|
11
|
-
import {Shape} from './Shape';
|
|
12
|
-
|
|
13
|
-
export interface TxtLeafProps extends ShapeProps {
|
|
14
|
-
children?: string;
|
|
15
|
-
text?: SignalValue<string>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const TXT_TYPE = Symbol('Txt');
|
|
19
|
-
|
|
20
|
-
@nodeName('TxtLeaf')
|
|
21
|
-
export class TxtLeaf extends Shape {
|
|
22
|
-
@lazy(() => {
|
|
23
|
-
try {
|
|
24
|
-
return new (Intl as any).Segmenter(undefined, {
|
|
25
|
-
granularity: 'grapheme',
|
|
26
|
-
});
|
|
27
|
-
} catch (e) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
protected static readonly segmenter: any;
|
|
32
|
-
|
|
33
|
-
@initial('')
|
|
34
|
-
@interpolation(textLerp)
|
|
35
|
-
@signal()
|
|
36
|
-
public declare readonly text: SimpleSignal<string, this>;
|
|
37
|
-
|
|
38
|
-
public constructor({children, ...rest}: TxtLeafProps) {
|
|
39
|
-
super(rest);
|
|
40
|
-
if (children) {
|
|
41
|
-
this.text(children);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@computed()
|
|
46
|
-
protected parentTxt() {
|
|
47
|
-
const parent = this.parent();
|
|
48
|
-
if (!parent) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!(TXT_TYPE in parent)) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return parent;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
protected override async draw(context: CanvasRenderingContext2D) {
|
|
60
|
-
this.requestFontUpdate();
|
|
61
|
-
this.applyStyle(context);
|
|
62
|
-
await document.fonts?.ready;
|
|
63
|
-
this.applyText(context);
|
|
64
|
-
context.font = this.styles.font;
|
|
65
|
-
context.textBaseline = 'bottom';
|
|
66
|
-
if ('letterSpacing' in context) {
|
|
67
|
-
context.letterSpacing = `${this.letterSpacing()}px`;
|
|
68
|
-
}
|
|
69
|
-
const fontOffset = context.measureText('').fontBoundingBoxAscent;
|
|
70
|
-
|
|
71
|
-
const parentRect = this.element.getBoundingClientRect();
|
|
72
|
-
const {width, height} = this.size();
|
|
73
|
-
const range = document.createRange();
|
|
74
|
-
let line = '';
|
|
75
|
-
const lineRect = new BBox();
|
|
76
|
-
for (const childNode of this.element.childNodes) {
|
|
77
|
-
if (!childNode.textContent) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
range.selectNodeContents(childNode);
|
|
82
|
-
const rangeRect = range.getBoundingClientRect();
|
|
83
|
-
|
|
84
|
-
const x = width / -2 + rangeRect.left - parentRect.left;
|
|
85
|
-
const y = height / -2 + rangeRect.top - parentRect.top + fontOffset;
|
|
86
|
-
|
|
87
|
-
if (lineRect.y === y) {
|
|
88
|
-
lineRect.width += rangeRect.width;
|
|
89
|
-
line += childNode.textContent;
|
|
90
|
-
} else {
|
|
91
|
-
await this.drawText(context, line, lineRect);
|
|
92
|
-
lineRect.x = x;
|
|
93
|
-
lineRect.y = y;
|
|
94
|
-
lineRect.width = rangeRect.width;
|
|
95
|
-
lineRect.height = rangeRect.height;
|
|
96
|
-
line = childNode.textContent;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await this.drawText(context, line, lineRect);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
protected async drawText(
|
|
104
|
-
context: CanvasRenderingContext2D,
|
|
105
|
-
text: string,
|
|
106
|
-
box: BBox,
|
|
107
|
-
) {
|
|
108
|
-
const y = box.y;
|
|
109
|
-
text = text.replace(/\s+/g, ' ');
|
|
110
|
-
if (this.lineWidth() <= 0) {
|
|
111
|
-
context.fillText(text, box.x, y);
|
|
112
|
-
} else if (this.strokeFirst()) {
|
|
113
|
-
context.strokeText(text, box.x, y);
|
|
114
|
-
context.fillText(text, box.x, y);
|
|
115
|
-
} else {
|
|
116
|
-
context.fillText(text, box.x, y);
|
|
117
|
-
context.strokeText(text, box.x, y);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
protected override getCacheBBox(): BBox {
|
|
122
|
-
const size = this.computedSize();
|
|
123
|
-
const range = document.createRange();
|
|
124
|
-
range.selectNodeContents(this.element);
|
|
125
|
-
const bbox = range.getBoundingClientRect();
|
|
126
|
-
|
|
127
|
-
const lineWidth = this.lineWidth();
|
|
128
|
-
// We take the default value of the miterLimit as 10.
|
|
129
|
-
const miterLimitCoefficient = this.lineJoin() === 'miter' ? 0.5 * 10 : 0.5;
|
|
130
|
-
|
|
131
|
-
return new BBox(-size.width / 2, -size.height / 2, bbox.width, bbox.height)
|
|
132
|
-
.expand([0, this.fontSize() * 0.5])
|
|
133
|
-
.expand(lineWidth * miterLimitCoefficient);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
protected override applyFlex() {
|
|
137
|
-
super.applyFlex();
|
|
138
|
-
this.element.style.display = 'inline';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
protected override updateLayout() {
|
|
142
|
-
this.applyFont();
|
|
143
|
-
this.applyFlex();
|
|
144
|
-
|
|
145
|
-
// Make sure the text is aligned correctly even if the text is smaller than
|
|
146
|
-
// the container.
|
|
147
|
-
if (this.justifyContent.isInitial()) {
|
|
148
|
-
this.element.style.justifyContent =
|
|
149
|
-
this.styles.getPropertyValue('text-align');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const wrap =
|
|
153
|
-
this.styles.whiteSpace !== 'nowrap' && this.styles.whiteSpace !== 'pre';
|
|
154
|
-
|
|
155
|
-
if (wrap) {
|
|
156
|
-
this.element.innerText = '';
|
|
157
|
-
|
|
158
|
-
if (TxtLeaf.segmenter) {
|
|
159
|
-
for (const word of TxtLeaf.segmenter.segment(this.text())) {
|
|
160
|
-
this.element.appendChild(document.createTextNode(word.segment));
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
for (const word of this.text().split('')) {
|
|
164
|
-
this.element.appendChild(document.createTextNode(word));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
} else if (this.styles.whiteSpace === 'pre') {
|
|
168
|
-
this.element.innerText = '';
|
|
169
|
-
for (const line of this.text().split('\n')) {
|
|
170
|
-
this.element.appendChild(document.createTextNode(line + '\n'));
|
|
171
|
-
}
|
|
172
|
-
} else {
|
|
173
|
-
this.element.innerText = this.text();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Overwrite all getters for signal values to return the parent value if it
|
|
180
|
-
* exists.
|
|
181
|
-
*
|
|
182
|
-
* The getters on the TxtLeaf class are used by the `@signal` decorators and
|
|
183
|
-
* are not used by the class or its consumers directly.
|
|
184
|
-
*
|
|
185
|
-
* Check out 2d/src/lib/utils/makeSignalExtensions.ts if this is confusing.
|
|
186
|
-
*/
|
|
187
|
-
[
|
|
188
|
-
'fill',
|
|
189
|
-
'stroke',
|
|
190
|
-
'lineWidth',
|
|
191
|
-
'strokeFirst',
|
|
192
|
-
'lineCap',
|
|
193
|
-
'lineJoin',
|
|
194
|
-
'lineDash',
|
|
195
|
-
'lineDashOffset',
|
|
196
|
-
].forEach(prop => {
|
|
197
|
-
(TxtLeaf.prototype as any)[`get${capitalize(prop)}`] = function (
|
|
198
|
-
this: TxtLeaf,
|
|
199
|
-
) {
|
|
200
|
-
return (
|
|
201
|
-
(this.parentTxt() as any)?.[prop]() ??
|
|
202
|
-
(this as any)[prop].context.getInitial()
|
|
203
|
-
);
|
|
204
|
-
};
|
|
205
|
-
});
|
|
1
|
+
import type {SignalValue, SimpleSignal} from '@twick/core';
|
|
2
|
+
import {BBox, capitalize, lazy, textLerp} from '@twick/core';
|
|
3
|
+
import {
|
|
4
|
+
computed,
|
|
5
|
+
initial,
|
|
6
|
+
interpolation,
|
|
7
|
+
nodeName,
|
|
8
|
+
signal,
|
|
9
|
+
} from '../decorators';
|
|
10
|
+
import type {ShapeProps} from './Shape';
|
|
11
|
+
import {Shape} from './Shape';
|
|
12
|
+
|
|
13
|
+
export interface TxtLeafProps extends ShapeProps {
|
|
14
|
+
children?: string;
|
|
15
|
+
text?: SignalValue<string>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const TXT_TYPE = Symbol('Txt');
|
|
19
|
+
|
|
20
|
+
@nodeName('TxtLeaf')
|
|
21
|
+
export class TxtLeaf extends Shape {
|
|
22
|
+
@lazy(() => {
|
|
23
|
+
try {
|
|
24
|
+
return new (Intl as any).Segmenter(undefined, {
|
|
25
|
+
granularity: 'grapheme',
|
|
26
|
+
});
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
protected static readonly segmenter: any;
|
|
32
|
+
|
|
33
|
+
@initial('')
|
|
34
|
+
@interpolation(textLerp)
|
|
35
|
+
@signal()
|
|
36
|
+
public declare readonly text: SimpleSignal<string, this>;
|
|
37
|
+
|
|
38
|
+
public constructor({children, ...rest}: TxtLeafProps) {
|
|
39
|
+
super(rest);
|
|
40
|
+
if (children) {
|
|
41
|
+
this.text(children);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@computed()
|
|
46
|
+
protected parentTxt() {
|
|
47
|
+
const parent = this.parent();
|
|
48
|
+
if (!parent) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!(TXT_TYPE in parent)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return parent;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected override async draw(context: CanvasRenderingContext2D) {
|
|
60
|
+
this.requestFontUpdate();
|
|
61
|
+
this.applyStyle(context);
|
|
62
|
+
await document.fonts?.ready;
|
|
63
|
+
this.applyText(context);
|
|
64
|
+
context.font = this.styles.font;
|
|
65
|
+
context.textBaseline = 'bottom';
|
|
66
|
+
if ('letterSpacing' in context) {
|
|
67
|
+
context.letterSpacing = `${this.letterSpacing()}px`;
|
|
68
|
+
}
|
|
69
|
+
const fontOffset = context.measureText('').fontBoundingBoxAscent;
|
|
70
|
+
|
|
71
|
+
const parentRect = this.element.getBoundingClientRect();
|
|
72
|
+
const {width, height} = this.size();
|
|
73
|
+
const range = document.createRange();
|
|
74
|
+
let line = '';
|
|
75
|
+
const lineRect = new BBox();
|
|
76
|
+
for (const childNode of this.element.childNodes) {
|
|
77
|
+
if (!childNode.textContent) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
range.selectNodeContents(childNode);
|
|
82
|
+
const rangeRect = range.getBoundingClientRect();
|
|
83
|
+
|
|
84
|
+
const x = width / -2 + rangeRect.left - parentRect.left;
|
|
85
|
+
const y = height / -2 + rangeRect.top - parentRect.top + fontOffset;
|
|
86
|
+
|
|
87
|
+
if (lineRect.y === y) {
|
|
88
|
+
lineRect.width += rangeRect.width;
|
|
89
|
+
line += childNode.textContent;
|
|
90
|
+
} else {
|
|
91
|
+
await this.drawText(context, line, lineRect);
|
|
92
|
+
lineRect.x = x;
|
|
93
|
+
lineRect.y = y;
|
|
94
|
+
lineRect.width = rangeRect.width;
|
|
95
|
+
lineRect.height = rangeRect.height;
|
|
96
|
+
line = childNode.textContent;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await this.drawText(context, line, lineRect);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected async drawText(
|
|
104
|
+
context: CanvasRenderingContext2D,
|
|
105
|
+
text: string,
|
|
106
|
+
box: BBox,
|
|
107
|
+
) {
|
|
108
|
+
const y = box.y;
|
|
109
|
+
text = text.replace(/\s+/g, ' ');
|
|
110
|
+
if (this.lineWidth() <= 0) {
|
|
111
|
+
context.fillText(text, box.x, y);
|
|
112
|
+
} else if (this.strokeFirst()) {
|
|
113
|
+
context.strokeText(text, box.x, y);
|
|
114
|
+
context.fillText(text, box.x, y);
|
|
115
|
+
} else {
|
|
116
|
+
context.fillText(text, box.x, y);
|
|
117
|
+
context.strokeText(text, box.x, y);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
protected override getCacheBBox(): BBox {
|
|
122
|
+
const size = this.computedSize();
|
|
123
|
+
const range = document.createRange();
|
|
124
|
+
range.selectNodeContents(this.element);
|
|
125
|
+
const bbox = range.getBoundingClientRect();
|
|
126
|
+
|
|
127
|
+
const lineWidth = this.lineWidth();
|
|
128
|
+
// We take the default value of the miterLimit as 10.
|
|
129
|
+
const miterLimitCoefficient = this.lineJoin() === 'miter' ? 0.5 * 10 : 0.5;
|
|
130
|
+
|
|
131
|
+
return new BBox(-size.width / 2, -size.height / 2, bbox.width, bbox.height)
|
|
132
|
+
.expand([0, this.fontSize() * 0.5])
|
|
133
|
+
.expand(lineWidth * miterLimitCoefficient);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
protected override applyFlex() {
|
|
137
|
+
super.applyFlex();
|
|
138
|
+
this.element.style.display = 'inline';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected override updateLayout() {
|
|
142
|
+
this.applyFont();
|
|
143
|
+
this.applyFlex();
|
|
144
|
+
|
|
145
|
+
// Make sure the text is aligned correctly even if the text is smaller than
|
|
146
|
+
// the container.
|
|
147
|
+
if (this.justifyContent.isInitial()) {
|
|
148
|
+
this.element.style.justifyContent =
|
|
149
|
+
this.styles.getPropertyValue('text-align');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const wrap =
|
|
153
|
+
this.styles.whiteSpace !== 'nowrap' && this.styles.whiteSpace !== 'pre';
|
|
154
|
+
|
|
155
|
+
if (wrap) {
|
|
156
|
+
this.element.innerText = '';
|
|
157
|
+
|
|
158
|
+
if (TxtLeaf.segmenter) {
|
|
159
|
+
for (const word of TxtLeaf.segmenter.segment(this.text())) {
|
|
160
|
+
this.element.appendChild(document.createTextNode(word.segment));
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
for (const word of this.text().split('')) {
|
|
164
|
+
this.element.appendChild(document.createTextNode(word));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} else if (this.styles.whiteSpace === 'pre') {
|
|
168
|
+
this.element.innerText = '';
|
|
169
|
+
for (const line of this.text().split('\n')) {
|
|
170
|
+
this.element.appendChild(document.createTextNode(line + '\n'));
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
this.element.innerText = this.text();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Overwrite all getters for signal values to return the parent value if it
|
|
180
|
+
* exists.
|
|
181
|
+
*
|
|
182
|
+
* The getters on the TxtLeaf class are used by the `@signal` decorators and
|
|
183
|
+
* are not used by the class or its consumers directly.
|
|
184
|
+
*
|
|
185
|
+
* Check out 2d/src/lib/utils/makeSignalExtensions.ts if this is confusing.
|
|
186
|
+
*/
|
|
187
|
+
[
|
|
188
|
+
'fill',
|
|
189
|
+
'stroke',
|
|
190
|
+
'lineWidth',
|
|
191
|
+
'strokeFirst',
|
|
192
|
+
'lineCap',
|
|
193
|
+
'lineJoin',
|
|
194
|
+
'lineDash',
|
|
195
|
+
'lineDashOffset',
|
|
196
|
+
].forEach(prop => {
|
|
197
|
+
(TxtLeaf.prototype as any)[`get${capitalize(prop)}`] = function (
|
|
198
|
+
this: TxtLeaf,
|
|
199
|
+
) {
|
|
200
|
+
return (
|
|
201
|
+
(this.parentTxt() as any)?.[prop]() ??
|
|
202
|
+
(this as any)[prop].context.getInitial()
|
|
203
|
+
);
|
|
204
|
+
};
|
|
205
|
+
});
|