@ax-design/reveal-highlight 0.4.0 → 0.4.1

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/src/SvgConfig.ts CHANGED
@@ -1,422 +1,422 @@
1
- import { RevealBoundaryStore } from './RevealBoundryStore.js';
2
-
3
- import type { CachedRevealPath } from './utils/types.js';
4
-
5
- import { BaseConfig } from './BaseConfig.js';
6
-
7
- export class SvgConfig extends BaseConfig<SVGSVGElement> {
8
- protected cachedImg: CachedRevealPath;
9
-
10
- constructor(store: RevealBoundaryStore, $svg: SVGSVGElement, $container: HTMLElement) {
11
- super(store, $svg, $container);
12
- this._store = store;
13
-
14
- const cachedPath = {
15
- borderReveal: $svg.querySelector('#borderPath') as SVGPathElement,
16
- fillReveal: $svg.querySelector('#fillPath') as SVGPathElement,
17
- rippleReveal: $svg.querySelector('#ripplePath') as SVGPathElement,
18
- };
19
-
20
- const cachedGradient = {
21
- borderReveal: $svg.querySelector('#borderGrad') as SVGRadialGradientElement,
22
- fillReveal: $svg.querySelector('#fillGrad') as SVGRadialGradientElement,
23
- rippleReveal: $svg.querySelector('#rippleGrad') as SVGRadialGradientElement,
24
- }
25
-
26
- const cachedGradientStop = {
27
- borderReveal: [
28
- $svg.querySelector('#borderCenter') as SVGStopElement,
29
- $svg.querySelector('#borderOut') as SVGStopElement,
30
- ] as const,
31
- fillReveal: [
32
- $svg.querySelector('#fillCenter') as SVGStopElement,
33
- $svg.querySelector('#fillOut') as SVGStopElement,
34
- ] as const,
35
- rippleReveal: [
36
- $svg.querySelector('#rippleCenter') as SVGStopElement,
37
- $svg.querySelector('#rippleMiddle') as SVGStopElement,
38
- $svg.querySelector('#rippleOut') as SVGStopElement,
39
- ] as const,
40
- }
41
-
42
- this.cachedImg = {
43
- cachedReveal: {
44
- borderReveal: {
45
- radiusFactor: -1,
46
- radius: -1,
47
- color: 'INVALID',
48
- opacity: 0,
49
- pattern: null,
50
- },
51
- fillReveal: {
52
- radiusFactor: -1,
53
- radius: -1,
54
- color: 'INVALID',
55
- opacity: 0,
56
- pattern: null,
57
- },
58
- },
59
- cachedPath,
60
- cachedGradient,
61
- cachedGradientStop,
62
- };
63
- }
64
-
65
- updateCachedBitmap = () => {
66
- }
67
-
68
- updateCachedReveal = () => {
69
- const c = this.cachedStyle;
70
- const stops = this.cachedImg.cachedGradientStop;
71
- stops.borderReveal[0].style.stopColor = c.borderColor;
72
- stops.borderReveal[1].style.stopColor = c.borderColor;
73
- stops.borderReveal[0].style.stopOpacity = c.opacity.toString();
74
- stops.borderReveal[1].style.stopOpacity = '0';
75
-
76
- stops.fillReveal[0].style.stopColor = c.hoverLightColor;
77
- stops.fillReveal[1].style.stopColor = c.hoverLightColor;
78
- stops.fillReveal[0].style.stopOpacity = (c.opacity * 0.5).toString();
79
- stops.fillReveal[1].style.stopOpacity = '0';
80
-
81
- stops.rippleReveal[0].style.stopColor = c.pressAnimationColor;
82
- stops.rippleReveal[1].style.stopColor = c.pressAnimationColor;
83
- stops.rippleReveal[2].style.stopColor = c.pressAnimationColor;
84
-
85
- const grads = this.cachedImg.cachedGradient;
86
- const isAbsoluteFill = c.trueFillRadius[0] === -1;
87
- grads.borderReveal.r.baseVal.value = isAbsoluteFill
88
- ? c.borderFillRadius
89
- : c.borderFillRadius * c.trueFillRadius[1];
90
- grads.fillReveal.r.baseVal.value = isAbsoluteFill
91
- ? c.hoverLightFillRadius
92
- : c.hoverLightFillRadius * c.trueFillRadius[0];
93
-
94
- this.fillDirty = false;
95
- };
96
-
97
- private updateAnimateGrd = (frame: number, x: number, y: number, radius: number) => {
98
- const { opacity } = this.cachedStyle;
99
-
100
- const _innerAlpha = opacity * (0.2 - frame);
101
- const _outerAlpha = opacity * (0.1 - frame * 0.07);
102
- const _outerBorder = 0.1 + frame * 0.8;
103
-
104
- const innerAlpha = _innerAlpha < 0 ? 0 : _innerAlpha;
105
- const outerAlpha = _outerAlpha < 0 ? 0 : _outerAlpha;
106
- let outerBorder = 0;
107
- outerBorder = _outerBorder > 1 ? 1 : _outerBorder;
108
- outerBorder = _outerBorder < 0 ? 0 : _outerBorder;
109
-
110
- const stops = this.cachedImg.cachedGradientStop;
111
- stops.rippleReveal[0].style.stopOpacity = innerAlpha.toString();
112
- stops.rippleReveal[1].style.stopOpacity = outerAlpha.toString();
113
- stops.rippleReveal[2].style.stopOpacity = '0';
114
-
115
- stops.rippleReveal[0].offset.baseVal = 0;
116
- stops.rippleReveal[1].offset.baseVal = outerBorder * 0.55;
117
- stops.rippleReveal[2].offset.baseVal = outerBorder;
118
-
119
- const grads = this.cachedImg.cachedGradient;
120
- grads.rippleReveal.r.baseVal.value = radius;
121
- grads.rippleReveal.cx.baseVal.value = x;
122
- grads.rippleReveal.cy.baseVal.value = y;
123
- };
124
-
125
- private updateGradientPosition = (x: number, y: number) => {
126
- const grads = this.cachedImg.cachedGradient;
127
-
128
- grads.borderReveal.cx.baseVal.value = x;
129
- grads.borderReveal.cy.baseVal.value = y;
130
-
131
- grads.fillReveal.cx.baseVal.value = x;
132
- grads.fillReveal.cy.baseVal.value = y;
133
- }
134
-
135
- private drawShape = (hollow: boolean, $target: SVGPathElement) => {
136
- const b = this.cachedBoundingRect;
137
- const w = b.width;
138
- const h = b.height;
139
-
140
- const c = this.cachedStyle;
141
- const bw = c.borderWidth;
142
-
143
- const tl = c.topLeftBorderDecorationRadius;
144
- const tr = c.topRightBorderDecorationRadius;
145
- const bl = c.bottomLeftBorderDecorationRadius;
146
- const br = c.bottomRightBorderDecorationRadius;
147
-
148
- const wlf = c.withLeftBorderFactor;
149
- const wrf = c.withRightBorderFactor;
150
- const wtf = c.withTopBorderFactor;
151
- const wbf = c.withBottomBorderFactor;
152
-
153
- const rwlf = c.withLeftBorderFactor ? 0 : 1;
154
- const rwrf = c.withRightBorderFactor ? 0 : 1;
155
- const rwtf = c.withTopBorderFactor ? 0 : 1;
156
- const rwbf = c.withBottomBorderFactor ? 0 : 1;
157
-
158
- const ntlc = !c.withTopBorderFactor && !c.withLeftBorderFactor;
159
- const ntrc = !c.withTopBorderFactor && !c.withRightBorderFactor;
160
- const nblc = !c.withBottomBorderFactor && !c.withLeftBorderFactor;
161
- const nbrc = !c.withBottomBorderFactor && !c.withRightBorderFactor;
162
-
163
- const tl2 = bw < tl ? tl - bw : 0;
164
- const tr2 = bw < tr ? tr - bw : 0;
165
- const bl2 = bw < bl ? bl - bw : 0;
166
- const br2 = bw < br ? br - bw : 0;
167
-
168
- let d = '';
169
-
170
- // Step 1: Starting point
171
- d += hollow ? `M ${tl}, 0 ` : `M ${bw}, ${tl2 + bw} `;
172
-
173
- switch (c.borderDecorationType) {
174
- // Oh... Fuck again...
175
- case 'round':
176
- // Step 2: Draw shape
177
- if (hollow) {
178
- // Step 2-1: This is outer path, drawing clockwise
179
- d += `
180
- h ${w - tl - tr}
181
- a ${tr}, ${tr} 0 0 1 ${tr}, ${tr}
182
- v ${h - tr - br}
183
- a ${br}, ${br} 0 0 1 ${-br}, ${br}
184
- h ${-w + bl + br}
185
- a ${bl}, ${bl} 0 0 1 ${-bl}, ${-bl}
186
- v ${-h + tr + br}
187
- a ${tl}, ${tl} 0 0 1 ${tl}, ${-tl}
188
- Z
189
- `;
190
- }
191
- // Step 2-2: This is the inner path, drawing anti-clockwise
192
- d += `
193
- M ${bw * wlf}, ${tl2 + bw}
194
- v ${h - tl2 - bl2 - bw * 2 + rwbf * bw}
195
- a ${bl2}, ${bl2} 0 0 0 ${bl2}, ${bl2}
196
- h ${w - bl2 - br2 - bw * 2 + rwlf * bw + rwrf * bw}
197
- a ${br2}, ${br2} 0 0 0 ${br2}, ${-br2}
198
- v ${-h + tr2 + br2 + bw * 2 - rwtf * bw - rwbf * bw}
199
- a ${tr2}, ${tr2} 0 0 0 ${-tr2}, ${-tr2}
200
- h ${-w + bl2 + br2 + bw * 2 - rwlf * bw - rwrf * bw}
201
- a ${tl2}, ${tl2} 0 0 0 ${-tl2}, ${tl2}
202
- L ${bw * wlf}, ${tl2 + bw}
203
- `;
204
- break;
205
- case 'bevel':
206
- if (hollow) {
207
- // Step 2-1: This is outer path, drawing clockwise
208
- d += `
209
- h ${w - tl - tr}
210
- l ${tr} ${tr}
211
- v ${h - tr - br}
212
- l ${-br} ${br}
213
- h ${-w + tr + br}
214
- l ${-bl} ${-bl}
215
- v ${-h + tr + br}
216
- l ${tl} ${-tl}
217
- Z
218
- `;
219
- }
220
- // // Step 2-2: This is the inner path, drawing anti-clockwise
221
- d += `
222
- M ${bw}, ${tl2 + bw}
223
- ${rwlf ? `h ${-bw}` : ''}
224
- v ${h - tl2 - bl2 - bw * 2}
225
- ${rwlf ? `h ${bw}` : ''}
226
- ${nblc ? `l ${-bw} 0` : ''}
227
- l ${bl2} ${bl2}
228
- ${nblc ? `l ${bw} 0` : ''}
229
- ${rwbf ? `v ${bw}` : ''}
230
- h ${w - bl2 - br2 - bw * 2}
231
- ${rwbf ? `v ${-bw}` : ''}
232
- ${nbrc ? `l ${bw} 0` : ''}
233
- l ${br2} ${-br2}
234
- ${nbrc ? `l ${-bw} 0` : ''}
235
- ${rwrf ? `h ${bw}` : ''}
236
- v ${-h + tl2 + bl2 + bw * 2}
237
- ${rwrf ? `h ${-bw}` : ''}
238
- ${ntrc ? `l 0 ${-bw}` : ''}
239
- l ${-tr2} ${-tr2}
240
- ${ntrc ? `l 0 ${bw}` : ''}
241
- ${rwtf ? `v ${-bw}` : ''}
242
- h ${-w + bl2 + br2 + bw * 2}
243
- ${rwtf ? `v ${bw}` : ''}
244
- ${ntlc ? `l 0 ${-bw}` : ''}
245
- l ${-tl2} ${tl2}
246
- ${ntlc ? `l 0 ${bw}` : ''}
247
- `;
248
- break;
249
- case 'miter':
250
- // Step 2: Draw shape
251
- if (hollow) {
252
- // Step 2-1: This is outer path, drawing clockwise
253
- d += `
254
- M 0, 0
255
- h ${w}
256
- v ${h}
257
- h ${0 - w}
258
- v ${0 - h}
259
- Z
260
- `;
261
- }
262
- // Step 2-2: This is the inner path, drawing anti-clockwise
263
- d += `
264
- M ${bw * wlf}, ${bw * wtf}
265
- v ${h - wtf * bw - wbf * bw}
266
- h ${w - wlf * bw - wrf * bw}
267
- v ${-h + wtf * bw + wbf * bw}
268
- h ${-w + wlf * bw + wrf * bw}
269
- `;
270
- }
271
- // Step 3: Close the path
272
- if (c.borderDecorationType === 'miter') {
273
- d += hollow ? `L ${bw}, ${bw}` : `z`;
274
- } else {
275
- d += hollow ? `L ${tl2 + bw}, ${tl2 + bw}` : `z`;
276
- }
277
-
278
- $target.setAttribute('d', d);
279
- };
280
-
281
- syncSizeToElement = (x: SVGSVGElement) => {
282
- const b = this.cachedBoundingRect;
283
- const width = x.width.baseVal;
284
- const height = x.height.baseVal;
285
-
286
- if (width.value !== b.width || height.value !== b.height) {
287
- width.value = b.width;
288
- height.value = b.height;
289
- }
290
- };
291
-
292
- clear = (forAnimation: boolean) => {
293
- if (!forAnimation) {
294
- this.internalClear();
295
- }
296
- }
297
-
298
- private internalClear = () => {
299
- if (!this.dirty) return false;
300
-
301
- const paths = this.cachedImg.cachedPath;
302
-
303
- paths.borderReveal.style.fill = 'transparent';
304
- paths.fillReveal.style.fill = 'transparent';
305
- paths.rippleReveal.style.fill = 'transparent';
306
-
307
- this.dirty = false;
308
- return true;
309
- }
310
-
311
- paint = (skipSamePointerPositionCheck?: boolean): boolean => {
312
- const store = this._store;
313
-
314
- const animationPlaying = store.animationQueue.includes(this);
315
- const samePosition = store.clientX === store.paintedClientX && store.clientY === store.paintedClientY;
316
-
317
- if (samePosition && !skipSamePointerPositionCheck && !animationPlaying) {
318
- return false;
319
- }
320
-
321
- if (!store.mouseInBoundary && !animationPlaying) {
322
- this.internalClear();
323
- return false;
324
- }
325
-
326
- this.syncSizeToElement(this.element);
327
- this.cacheBoundingRect();
328
-
329
- const b = this.cachedBoundingRect;
330
-
331
- const relativeX = store.clientX - b.left;
332
- const relativeY = store.clientY - b.top;
333
-
334
- if (Number.isNaN(relativeX) || Number.isNaN(relativeY)) {
335
- return false;
336
- }
337
-
338
- const c = this.cachedStyle;
339
- this.getTrueFillRadius(c.trueFillRadius);
340
-
341
- this.paintedWidth = b.width;
342
- this.paintedHeight = b.height;
343
-
344
- const grads = this.cachedImg.cachedGradient;
345
- const maxRadius = Math.max(
346
- grads.borderReveal.r.baseVal.value,
347
- grads.fillReveal.r.baseVal.value
348
- );
349
-
350
- this._store.updateMaxRadius(maxRadius);
351
- this.cacheCanvasPaintingStyle();
352
-
353
- if (this.fillDirty) {
354
- this.updateCachedReveal();
355
- }
356
-
357
- const inLeftBound = relativeX + maxRadius > 0;
358
- const inRightBound = relativeX - maxRadius < b.width;
359
- const inTopBound = relativeY + maxRadius > 0;
360
- const inBottomBound = relativeY - maxRadius < b.height;
361
- const mouseInRenderArea = inLeftBound && inRightBound && inTopBound && inBottomBound;
362
-
363
- if (!mouseInRenderArea && !animationPlaying) {
364
- this.internalClear();
365
- return false;
366
- }
367
-
368
- this.dirty = true;
369
-
370
- this.updateGradientPosition(relativeX, relativeY);
371
-
372
- if (this.shapeDirty) {
373
- this.drawShape(true, this.cachedImg.cachedPath.borderReveal);
374
- this.drawShape(false, this.cachedImg.cachedPath.fillReveal);
375
- this.drawShape(false, this.cachedImg.cachedPath.rippleReveal);
376
- this.shapeDirty = false;
377
- }
378
-
379
- if (store.mouseInBoundary) {
380
- const mouseInElement = relativeX > 0 && relativeY > 0 && relativeX < b.width && relativeY < b.height;
381
-
382
- if (c.hoverLight && mouseInElement) {
383
- // draw fill.
384
- this.cachedImg.cachedPath.fillReveal.style.fill = 'url(#fillGrad)';
385
- } else if (this.cachedImg.cachedPath.fillReveal.style.fill !== 'transparent') {
386
- this.cachedImg.cachedPath.fillReveal.style.fill = 'transparent';
387
- }
388
-
389
- const diffuse = (c.diffuse && mouseInRenderArea) || mouseInElement;
390
- if (c.borderWidth !== 0 && diffuse) {
391
- // Draw border.
392
- this.cachedImg.cachedPath.borderReveal.style.fill = 'url(#borderGrad)';
393
- } else if (this.cachedImg.cachedPath.borderReveal.style.fill !== 'transparent') {
394
- this.cachedImg.cachedPath.borderReveal.style.fill = 'transparent';
395
- }
396
- }
397
-
398
- if (c.pressAnimation && this.mousePressed && this.mouseDownAnimateLogicFrame) {
399
- const grdRadius =
400
- c.pressAnimationFillMode === 'constrained' ? c.trueFillRadius[1] : Math.max(b.width, b.height);
401
-
402
- this.mouseReleased && this.mouseUpClientX && this.mouseUpClientY
403
- ? this.updateAnimateGrd(
404
- this.mouseDownAnimateLogicFrame,
405
- this.mouseUpClientX - b.left,
406
- this.mouseUpClientY - b.top,
407
- grdRadius
408
- )
409
- : this.updateAnimateGrd(
410
- this.mouseDownAnimateLogicFrame,
411
- relativeX,
412
- relativeY,
413
- grdRadius
414
- )
415
- this.cachedImg.cachedPath.rippleReveal.style.fill = 'url(#rippleGrad)';
416
- } else if (this.cachedImg.cachedPath.rippleReveal.style.fill !== 'transparent') {
417
- this.cachedImg.cachedPath.rippleReveal.style.fill = 'transparent';
418
- }
419
-
420
- return true;
421
- };
422
- }
1
+ import { RevealBoundaryStore } from './RevealBoundryStore.js';
2
+
3
+ import type { CachedRevealPath } from './utils/types.js';
4
+
5
+ import { BaseConfig } from './BaseConfig.js';
6
+
7
+ export class SvgConfig extends BaseConfig<SVGSVGElement> {
8
+ protected cachedImg: CachedRevealPath;
9
+
10
+ constructor(store: RevealBoundaryStore, $svg: SVGSVGElement, $container: HTMLElement) {
11
+ super(store, $svg, $container);
12
+ this._store = store;
13
+
14
+ const cachedPath = {
15
+ borderReveal: $svg.querySelector('#borderPath') as SVGPathElement,
16
+ fillReveal: $svg.querySelector('#fillPath') as SVGPathElement,
17
+ rippleReveal: $svg.querySelector('#ripplePath') as SVGPathElement,
18
+ };
19
+
20
+ const cachedGradient = {
21
+ borderReveal: $svg.querySelector('#borderGrad') as SVGRadialGradientElement,
22
+ fillReveal: $svg.querySelector('#fillGrad') as SVGRadialGradientElement,
23
+ rippleReveal: $svg.querySelector('#rippleGrad') as SVGRadialGradientElement,
24
+ }
25
+
26
+ const cachedGradientStop = {
27
+ borderReveal: [
28
+ $svg.querySelector('#borderCenter') as SVGStopElement,
29
+ $svg.querySelector('#borderOut') as SVGStopElement,
30
+ ] as const,
31
+ fillReveal: [
32
+ $svg.querySelector('#fillCenter') as SVGStopElement,
33
+ $svg.querySelector('#fillOut') as SVGStopElement,
34
+ ] as const,
35
+ rippleReveal: [
36
+ $svg.querySelector('#rippleCenter') as SVGStopElement,
37
+ $svg.querySelector('#rippleMiddle') as SVGStopElement,
38
+ $svg.querySelector('#rippleOut') as SVGStopElement,
39
+ ] as const,
40
+ }
41
+
42
+ this.cachedImg = {
43
+ cachedReveal: {
44
+ borderReveal: {
45
+ radiusFactor: -1,
46
+ radius: -1,
47
+ color: 'INVALID',
48
+ opacity: 0,
49
+ pattern: null,
50
+ },
51
+ fillReveal: {
52
+ radiusFactor: -1,
53
+ radius: -1,
54
+ color: 'INVALID',
55
+ opacity: 0,
56
+ pattern: null,
57
+ },
58
+ },
59
+ cachedPath,
60
+ cachedGradient,
61
+ cachedGradientStop,
62
+ };
63
+ }
64
+
65
+ updateCachedBitmap = () => {
66
+ }
67
+
68
+ updateCachedReveal = () => {
69
+ const c = this.cachedStyle;
70
+ const stops = this.cachedImg.cachedGradientStop;
71
+ stops.borderReveal[0].style.stopColor = c.borderColor;
72
+ stops.borderReveal[1].style.stopColor = c.borderColor;
73
+ stops.borderReveal[0].style.stopOpacity = c.opacity.toString();
74
+ stops.borderReveal[1].style.stopOpacity = '0';
75
+
76
+ stops.fillReveal[0].style.stopColor = c.hoverLightColor;
77
+ stops.fillReveal[1].style.stopColor = c.hoverLightColor;
78
+ stops.fillReveal[0].style.stopOpacity = (c.opacity * 0.5).toString();
79
+ stops.fillReveal[1].style.stopOpacity = '0';
80
+
81
+ stops.rippleReveal[0].style.stopColor = c.pressAnimationColor;
82
+ stops.rippleReveal[1].style.stopColor = c.pressAnimationColor;
83
+ stops.rippleReveal[2].style.stopColor = c.pressAnimationColor;
84
+
85
+ const grads = this.cachedImg.cachedGradient;
86
+ const isAbsoluteFill = c.trueFillRadius[0] === -1;
87
+ grads.borderReveal.r.baseVal.value = isAbsoluteFill
88
+ ? c.borderFillRadius
89
+ : c.borderFillRadius * c.trueFillRadius[1];
90
+ grads.fillReveal.r.baseVal.value = isAbsoluteFill
91
+ ? c.hoverLightFillRadius
92
+ : c.hoverLightFillRadius * c.trueFillRadius[0];
93
+
94
+ this.fillDirty = false;
95
+ };
96
+
97
+ private updateAnimateGrd = (frame: number, x: number, y: number, radius: number) => {
98
+ const { opacity } = this.cachedStyle;
99
+
100
+ const _innerAlpha = opacity * (0.2 - frame);
101
+ const _outerAlpha = opacity * (0.1 - frame * 0.07);
102
+ const _outerBorder = 0.1 + frame * 0.8;
103
+
104
+ const innerAlpha = _innerAlpha < 0 ? 0 : _innerAlpha;
105
+ const outerAlpha = _outerAlpha < 0 ? 0 : _outerAlpha;
106
+ let outerBorder = 0;
107
+ outerBorder = _outerBorder > 1 ? 1 : _outerBorder;
108
+ outerBorder = _outerBorder < 0 ? 0 : _outerBorder;
109
+
110
+ const stops = this.cachedImg.cachedGradientStop;
111
+ stops.rippleReveal[0].style.stopOpacity = innerAlpha.toString();
112
+ stops.rippleReveal[1].style.stopOpacity = outerAlpha.toString();
113
+ stops.rippleReveal[2].style.stopOpacity = '0';
114
+
115
+ stops.rippleReveal[0].offset.baseVal = 0;
116
+ stops.rippleReveal[1].offset.baseVal = outerBorder * 0.55;
117
+ stops.rippleReveal[2].offset.baseVal = outerBorder;
118
+
119
+ const grads = this.cachedImg.cachedGradient;
120
+ grads.rippleReveal.r.baseVal.value = radius;
121
+ grads.rippleReveal.cx.baseVal.value = x;
122
+ grads.rippleReveal.cy.baseVal.value = y;
123
+ };
124
+
125
+ private updateGradientPosition = (x: number, y: number) => {
126
+ const grads = this.cachedImg.cachedGradient;
127
+
128
+ grads.borderReveal.cx.baseVal.value = x;
129
+ grads.borderReveal.cy.baseVal.value = y;
130
+
131
+ grads.fillReveal.cx.baseVal.value = x;
132
+ grads.fillReveal.cy.baseVal.value = y;
133
+ }
134
+
135
+ private drawShape = (hollow: boolean, $target: SVGPathElement) => {
136
+ const b = this.cachedBoundingRect;
137
+ const w = b.width;
138
+ const h = b.height;
139
+
140
+ const c = this.cachedStyle;
141
+ const bw = c.borderWidth;
142
+
143
+ const tl = c.topLeftBorderDecorationRadius;
144
+ const tr = c.topRightBorderDecorationRadius;
145
+ const bl = c.bottomLeftBorderDecorationRadius;
146
+ const br = c.bottomRightBorderDecorationRadius;
147
+
148
+ const wlf = c.withLeftBorderFactor;
149
+ const wrf = c.withRightBorderFactor;
150
+ const wtf = c.withTopBorderFactor;
151
+ const wbf = c.withBottomBorderFactor;
152
+
153
+ const rwlf = c.withLeftBorderFactor ? 0 : 1;
154
+ const rwrf = c.withRightBorderFactor ? 0 : 1;
155
+ const rwtf = c.withTopBorderFactor ? 0 : 1;
156
+ const rwbf = c.withBottomBorderFactor ? 0 : 1;
157
+
158
+ const ntlc = !c.withTopBorderFactor && !c.withLeftBorderFactor;
159
+ const ntrc = !c.withTopBorderFactor && !c.withRightBorderFactor;
160
+ const nblc = !c.withBottomBorderFactor && !c.withLeftBorderFactor;
161
+ const nbrc = !c.withBottomBorderFactor && !c.withRightBorderFactor;
162
+
163
+ const tl2 = bw < tl ? tl - bw : 0;
164
+ const tr2 = bw < tr ? tr - bw : 0;
165
+ const bl2 = bw < bl ? bl - bw : 0;
166
+ const br2 = bw < br ? br - bw : 0;
167
+
168
+ let d = '';
169
+
170
+ // Step 1: Starting point
171
+ d += hollow ? `M ${tl}, 0 ` : `M ${bw}, ${tl2 + bw} `;
172
+
173
+ switch (c.borderDecorationType) {
174
+ // Oh... Fuck again...
175
+ case 'round':
176
+ // Step 2: Draw shape
177
+ if (hollow) {
178
+ // Step 2-1: This is outer path, drawing clockwise
179
+ d += `
180
+ h ${w - tl - tr}
181
+ a ${tr}, ${tr} 0 0 1 ${tr}, ${tr}
182
+ v ${h - tr - br}
183
+ a ${br}, ${br} 0 0 1 ${-br}, ${br}
184
+ h ${-w + bl + br}
185
+ a ${bl}, ${bl} 0 0 1 ${-bl}, ${-bl}
186
+ v ${-h + tr + br}
187
+ a ${tl}, ${tl} 0 0 1 ${tl}, ${-tl}
188
+ Z
189
+ `;
190
+ }
191
+ // Step 2-2: This is the inner path, drawing anti-clockwise
192
+ d += `
193
+ M ${bw * wlf}, ${tl2 + bw}
194
+ v ${h - tl2 - bl2 - bw * 2 + rwbf * bw}
195
+ a ${bl2}, ${bl2} 0 0 0 ${bl2}, ${bl2}
196
+ h ${w - bl2 - br2 - bw * 2 + rwlf * bw + rwrf * bw}
197
+ a ${br2}, ${br2} 0 0 0 ${br2}, ${-br2}
198
+ v ${-h + tr2 + br2 + bw * 2 - rwtf * bw - rwbf * bw}
199
+ a ${tr2}, ${tr2} 0 0 0 ${-tr2}, ${-tr2}
200
+ h ${-w + bl2 + br2 + bw * 2 - rwlf * bw - rwrf * bw}
201
+ a ${tl2}, ${tl2} 0 0 0 ${-tl2}, ${tl2}
202
+ L ${bw * wlf}, ${tl2 + bw}
203
+ `;
204
+ break;
205
+ case 'bevel':
206
+ if (hollow) {
207
+ // Step 2-1: This is outer path, drawing clockwise
208
+ d += `
209
+ h ${w - tl - tr}
210
+ l ${tr} ${tr}
211
+ v ${h - tr - br}
212
+ l ${-br} ${br}
213
+ h ${-w + tr + br}
214
+ l ${-bl} ${-bl}
215
+ v ${-h + tr + br}
216
+ l ${tl} ${-tl}
217
+ Z
218
+ `;
219
+ }
220
+ // // Step 2-2: This is the inner path, drawing anti-clockwise
221
+ d += `
222
+ M ${bw}, ${tl2 + bw}
223
+ ${rwlf ? `h ${-bw}` : ''}
224
+ v ${h - tl2 - bl2 - bw * 2}
225
+ ${rwlf ? `h ${bw}` : ''}
226
+ ${nblc ? `l ${-bw} 0` : ''}
227
+ l ${bl2} ${bl2}
228
+ ${nblc ? `l ${bw} 0` : ''}
229
+ ${rwbf ? `v ${bw}` : ''}
230
+ h ${w - bl2 - br2 - bw * 2}
231
+ ${rwbf ? `v ${-bw}` : ''}
232
+ ${nbrc ? `l ${bw} 0` : ''}
233
+ l ${br2} ${-br2}
234
+ ${nbrc ? `l ${-bw} 0` : ''}
235
+ ${rwrf ? `h ${bw}` : ''}
236
+ v ${-h + tl2 + bl2 + bw * 2}
237
+ ${rwrf ? `h ${-bw}` : ''}
238
+ ${ntrc ? `l 0 ${-bw}` : ''}
239
+ l ${-tr2} ${-tr2}
240
+ ${ntrc ? `l 0 ${bw}` : ''}
241
+ ${rwtf ? `v ${-bw}` : ''}
242
+ h ${-w + bl2 + br2 + bw * 2}
243
+ ${rwtf ? `v ${bw}` : ''}
244
+ ${ntlc ? `l 0 ${-bw}` : ''}
245
+ l ${-tl2} ${tl2}
246
+ ${ntlc ? `l 0 ${bw}` : ''}
247
+ `;
248
+ break;
249
+ case 'miter':
250
+ // Step 2: Draw shape
251
+ if (hollow) {
252
+ // Step 2-1: This is outer path, drawing clockwise
253
+ d += `
254
+ M 0, 0
255
+ h ${w}
256
+ v ${h}
257
+ h ${0 - w}
258
+ v ${0 - h}
259
+ Z
260
+ `;
261
+ }
262
+ // Step 2-2: This is the inner path, drawing anti-clockwise
263
+ d += `
264
+ M ${bw * wlf}, ${bw * wtf}
265
+ v ${h - wtf * bw - wbf * bw}
266
+ h ${w - wlf * bw - wrf * bw}
267
+ v ${-h + wtf * bw + wbf * bw}
268
+ h ${-w + wlf * bw + wrf * bw}
269
+ `;
270
+ }
271
+ // Step 3: Close the path
272
+ if (c.borderDecorationType === 'miter') {
273
+ d += hollow ? `L ${bw}, ${bw}` : `z`;
274
+ } else {
275
+ d += hollow ? `L ${tl2 + bw}, ${tl2 + bw}` : `z`;
276
+ }
277
+
278
+ $target.setAttribute('d', d);
279
+ };
280
+
281
+ syncSizeToElement = (x: SVGSVGElement) => {
282
+ const b = this.cachedBoundingRect;
283
+ const width = x.width.baseVal;
284
+ const height = x.height.baseVal;
285
+
286
+ if (width.value !== b.width || height.value !== b.height) {
287
+ width.value = b.width;
288
+ height.value = b.height;
289
+ }
290
+ };
291
+
292
+ clear = (forAnimation: boolean) => {
293
+ if (!forAnimation) {
294
+ this.internalClear();
295
+ }
296
+ }
297
+
298
+ private internalClear = () => {
299
+ if (!this.dirty) return false;
300
+
301
+ const paths = this.cachedImg.cachedPath;
302
+
303
+ paths.borderReveal.style.fill = 'transparent';
304
+ paths.fillReveal.style.fill = 'transparent';
305
+ paths.rippleReveal.style.fill = 'transparent';
306
+
307
+ this.dirty = false;
308
+ return true;
309
+ }
310
+
311
+ paint = (skipSamePointerPositionCheck?: boolean): boolean => {
312
+ const store = this._store;
313
+
314
+ const animationPlaying = store.animationQueue.includes(this);
315
+ const samePosition = store.clientX === store.paintedClientX && store.clientY === store.paintedClientY;
316
+
317
+ if (samePosition && !skipSamePointerPositionCheck && !animationPlaying) {
318
+ return false;
319
+ }
320
+
321
+ if (!store.mouseInBoundary && !animationPlaying) {
322
+ this.internalClear();
323
+ return false;
324
+ }
325
+
326
+ this.syncSizeToElement(this.element);
327
+ this.cacheBoundingRect();
328
+
329
+ const b = this.cachedBoundingRect;
330
+
331
+ const relativeX = store.clientX - b.left;
332
+ const relativeY = store.clientY - b.top;
333
+
334
+ if (Number.isNaN(relativeX) || Number.isNaN(relativeY)) {
335
+ return false;
336
+ }
337
+
338
+ const c = this.cachedStyle;
339
+ this.getTrueFillRadius(c.trueFillRadius);
340
+
341
+ this.paintedWidth = b.width;
342
+ this.paintedHeight = b.height;
343
+
344
+ const grads = this.cachedImg.cachedGradient;
345
+ const maxRadius = Math.max(
346
+ grads.borderReveal.r.baseVal.value,
347
+ grads.fillReveal.r.baseVal.value
348
+ );
349
+
350
+ this._store.updateMaxRadius(maxRadius);
351
+ this.cacheCanvasPaintingStyle();
352
+
353
+ if (this.fillDirty) {
354
+ this.updateCachedReveal();
355
+ }
356
+
357
+ const inLeftBound = relativeX + maxRadius > 0;
358
+ const inRightBound = relativeX - maxRadius < b.width;
359
+ const inTopBound = relativeY + maxRadius > 0;
360
+ const inBottomBound = relativeY - maxRadius < b.height;
361
+ const mouseInRenderArea = inLeftBound && inRightBound && inTopBound && inBottomBound;
362
+
363
+ if (!mouseInRenderArea && !animationPlaying) {
364
+ this.internalClear();
365
+ return false;
366
+ }
367
+
368
+ this.dirty = true;
369
+
370
+ this.updateGradientPosition(relativeX, relativeY);
371
+
372
+ if (this.shapeDirty) {
373
+ this.drawShape(true, this.cachedImg.cachedPath.borderReveal);
374
+ this.drawShape(false, this.cachedImg.cachedPath.fillReveal);
375
+ this.drawShape(false, this.cachedImg.cachedPath.rippleReveal);
376
+ this.shapeDirty = false;
377
+ }
378
+
379
+ if (store.mouseInBoundary) {
380
+ const mouseInElement = relativeX > 0 && relativeY > 0 && relativeX < b.width && relativeY < b.height;
381
+
382
+ if (c.hoverLight && mouseInElement) {
383
+ // draw fill.
384
+ this.cachedImg.cachedPath.fillReveal.style.fill = 'url(#fillGrad)';
385
+ } else if (this.cachedImg.cachedPath.fillReveal.style.fill !== 'transparent') {
386
+ this.cachedImg.cachedPath.fillReveal.style.fill = 'transparent';
387
+ }
388
+
389
+ const diffuse = (c.diffuse && mouseInRenderArea) || mouseInElement;
390
+ if (c.borderWidth !== 0 && diffuse) {
391
+ // Draw border.
392
+ this.cachedImg.cachedPath.borderReveal.style.fill = 'url(#borderGrad)';
393
+ } else if (this.cachedImg.cachedPath.borderReveal.style.fill !== 'transparent') {
394
+ this.cachedImg.cachedPath.borderReveal.style.fill = 'transparent';
395
+ }
396
+ }
397
+
398
+ if (c.pressAnimation && this.mousePressed && this.mouseDownAnimateLogicFrame) {
399
+ const grdRadius =
400
+ c.pressAnimationFillMode === 'constrained' ? c.trueFillRadius[1] : Math.max(b.width, b.height);
401
+
402
+ this.mouseReleased && this.mouseUpClientX && this.mouseUpClientY
403
+ ? this.updateAnimateGrd(
404
+ this.mouseDownAnimateLogicFrame,
405
+ this.mouseUpClientX - b.left,
406
+ this.mouseUpClientY - b.top,
407
+ grdRadius
408
+ )
409
+ : this.updateAnimateGrd(
410
+ this.mouseDownAnimateLogicFrame,
411
+ relativeX,
412
+ relativeY,
413
+ grdRadius
414
+ )
415
+ this.cachedImg.cachedPath.rippleReveal.style.fill = 'url(#rippleGrad)';
416
+ } else if (this.cachedImg.cachedPath.rippleReveal.style.fill !== 'transparent') {
417
+ this.cachedImg.cachedPath.rippleReveal.style.fill = 'transparent';
418
+ }
419
+
420
+ return true;
421
+ };
422
+ }