@ax-design/reveal-highlight 0.4.1 → 0.5.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.
@@ -1,415 +1,415 @@
1
- import { RevealBoundaryStore } from './RevealBoundryStore.js';
2
-
3
- import type { CachedRevealBitmap } from './utils/types.js';
4
-
5
- import { BaseConfig } from './BaseConfig.js';
6
-
7
- const createEmptyCanvas = () => document.createElement('canvas') as HTMLCanvasElement;
8
-
9
- export class CanvasConfig extends BaseConfig<HTMLCanvasElement> {
10
- ctx: CanvasRenderingContext2D | null;
11
-
12
- protected cachedImg: CachedRevealBitmap;
13
-
14
- constructor(store: RevealBoundaryStore, $canvas: HTMLCanvasElement, $container: HTMLElement) {
15
- super(store, $canvas, $container);
16
-
17
- this.ctx = $canvas.getContext('2d');
18
-
19
- const cachedCanvas = {
20
- borderReveal: createEmptyCanvas(),
21
- fillReveal: createEmptyCanvas(),
22
- };
23
-
24
- const cachedCtx = {
25
- borderReveal: cachedCanvas.borderReveal.getContext('2d'),
26
- fillReveal: cachedCanvas.fillReveal.getContext('2d'),
27
- };
28
-
29
- this.cachedImg = {
30
- cachedReveal: {
31
- borderReveal: {
32
- radiusFactor: -1,
33
- radius: -1,
34
- color: 'INVALID',
35
- opacity: 0,
36
- pattern: null,
37
- },
38
- fillReveal: {
39
- radiusFactor: -1,
40
- radius: -1,
41
- color: 'INVALID',
42
- opacity: 0,
43
- pattern: null,
44
- },
45
- },
46
- cachedCanvas,
47
- cachedCtx,
48
- };
49
- }
50
-
51
- updateCachedReveal = () => {
52
- if (!this.fillDirty) return;
53
-
54
- const c = this.cachedStyle;
55
- const radius = c.trueFillRadius[1] * this.pxRatio;
56
- const size = radius * 2;
57
-
58
- const lastStyle = this.cachedImg.cachedReveal;
59
-
60
- const { borderReveal, fillReveal } = this.cachedImg.cachedCanvas;
61
- const { borderReveal: borderRevealCtx, fillReveal: fillRevealCtx } = this.cachedImg.cachedCtx;
62
-
63
- this._store.updateMaxRadius(c.trueFillRadius[1] * c.borderFillRadius);
64
-
65
- for (let i = 0; i < 2; i++) {
66
- // 0 means border, 1 means hover light.
67
- //-----------------------------------------
68
- // Last rendered style.
69
- const ls = i === 0 ? lastStyle.borderReveal : lastStyle.fillReveal;
70
- const color = i === 0 ? c.borderColor : c.hoverLightColor;
71
- const fillRadiusFactor = i === 0 ? c.borderFillRadius : c.hoverLightFillRadius;
72
-
73
- const doNotNeedUpdateReveal =
74
- color === ls.color && c.opacity === ls.opacity && fillRadiusFactor === ls.radiusFactor;
75
-
76
- if (doNotNeedUpdateReveal) continue;
77
-
78
- const reveal = i === 0 ? borderReveal : fillReveal;
79
- const ctx = i === 0 ? borderRevealCtx : fillRevealCtx;
80
-
81
- this.syncSizeToRevealRadius(reveal, fillRadiusFactor);
82
-
83
- if (!ctx) continue;
84
-
85
- ctx.clearRect(0, 0, reveal.width, reveal.height);
86
-
87
- const fillAlpha = i === 0 ? c.opacity : c.opacity * 0.5;
88
- const trueRadius = radius * fillRadiusFactor;
89
- const grd = ctx.createRadialGradient(trueRadius, trueRadius, 0, trueRadius, trueRadius, trueRadius);
90
-
91
- grd.addColorStop(0, `rgba(${color}, ${fillAlpha})`);
92
- grd.addColorStop(1, `rgba(${color}, 0.0)`);
93
-
94
- const trueSize = size * fillRadiusFactor;
95
- ctx.fillStyle = grd;
96
- ctx.clearRect(0, 0, trueSize, trueSize);
97
- ctx.fillRect(0, 0, trueSize, trueSize);
98
-
99
- ls.radius = trueRadius;
100
- ls.radiusFactor = fillRadiusFactor;
101
- ls.color = color;
102
- ls.opacity = c.opacity;
103
-
104
- if (!this.ctx) continue;
105
- ls.pattern = this.ctx.createPattern(reveal, 'no-repeat');
106
- }
107
-
108
- this.fillDirty = false;
109
- };
110
-
111
- updateCachedBitmap = () => {
112
- if (!this.ctx) return;
113
- this.updateCachedReveal();
114
- };
115
-
116
- private updateAnimateGrd = (frame: number, grd: CanvasGradient) => {
117
- const { pressAnimationColor, opacity } = this.cachedStyle;
118
-
119
- const _innerAlpha = opacity * (0.2 - frame);
120
- const _outerAlpha = opacity * (0.1 - frame * 0.07);
121
- const _outerBorder = 0.1 + frame * 0.8;
122
-
123
- const innerAlpha = _innerAlpha < 0 ? 0 : _innerAlpha;
124
- const outerAlpha = _outerAlpha < 0 ? 0 : _outerAlpha;
125
- let outerBorder = 0;
126
- outerBorder = _outerBorder > 1 ? 1 : _outerBorder;
127
- outerBorder = _outerBorder < 0 ? 0 : _outerBorder;
128
-
129
- grd.addColorStop(0, `rgba(${pressAnimationColor},${innerAlpha})`);
130
- grd.addColorStop(outerBorder * 0.55, `rgba(${pressAnimationColor},${outerAlpha})`);
131
- grd.addColorStop(outerBorder, `rgba(${pressAnimationColor}, 0)`);
132
- };
133
-
134
- private drawShape = (hollow: boolean) => {
135
- if (!this.ctx) return;
136
-
137
- const b = this.cachedBoundingRect;
138
- const w = b.width * this.pxRatio;
139
- const h = b.height * this.pxRatio;
140
-
141
- const c = this.cachedStyle;
142
- const bw = c.borderWidth * this.pxRatio;
143
-
144
- const tl = c.topLeftBorderDecorationRadius * this.pxRatio;
145
- const tr = c.topRightBorderDecorationRadius * this.pxRatio;
146
- const bl = c.bottomLeftBorderDecorationRadius * this.pxRatio;
147
- const br = c.bottomRightBorderDecorationRadius * this.pxRatio;
148
-
149
- const wlf = c.withLeftBorderFactor;
150
- const wrf = c.withRightBorderFactor;
151
- const wtf = c.withTopBorderFactor;
152
- const wbf = c.withBottomBorderFactor;
153
-
154
- const tl2 = bw < tl ? tl - bw : 0;
155
- const tr2 = bw < tr ? tr - bw : 0;
156
- const bl2 = bw < bl ? bl - bw : 0;
157
- const br2 = bw < br ? br - bw : 0;
158
-
159
- this.ctx.beginPath();
160
-
161
- switch (c.borderDecorationType) {
162
- // Oh... Fuck... It's painful...
163
- case 'round':
164
- // outer
165
- this.ctx.moveTo(tl, 0);
166
- this.ctx.arcTo(w, 0, w, h, tr);
167
- this.ctx.lineTo(w, h - br);
168
- this.ctx.arcTo(w, h, br, h, br);
169
- this.ctx.lineTo(bl, h);
170
- this.ctx.arcTo(0, h, 0, br, bl);
171
- this.ctx.lineTo(0, tl);
172
- this.ctx.arcTo(0, 0, tl, 0, tl);
173
-
174
- if (hollow) {
175
- /* prettier-ignore-start */
176
- // inner
177
- /**
178
- +-①----⑧-+
179
- ② ⑦
180
- | |
181
- ③ ⑥
182
- +-④----⑤-+
183
- */
184
- // x y r p
185
- this.ctx.moveTo(tl2, bw * wtf); // ①
186
- this.ctx.arcTo(bw * wlf, bw * wtf, bw * wlf, tl2 + bw * wtf, tl2); // ②
187
- this.ctx.lineTo(bw * wlf, h - bw * wbf - bl2); // ③
188
- this.ctx.arcTo(bw * wlf, h - bw * wbf, bw * wlf + bl2, h - bw * wbf, bl2); // ④
189
- this.ctx.lineTo(w - bw * wrf - br2, h - bw * wbf); // ⑤
190
- this.ctx.arcTo(w - bw * wrf, h - bw * wbf, w - bw * wrf, h - bw * wbf - br2, br2); // ⑥
191
- this.ctx.lineTo(w - bw * wrf, bw * wtf + tr2); // ⑦
192
- this.ctx.arcTo(w - bw * wrf, bw * wtf, w - bw * wrf - tr2, bw * wtf, tr2); // ⑧
193
- this.ctx.lineTo(tl2, bw * wtf); // ①
194
- /* prettier-ignore-end */
195
- }
196
- break;
197
- case 'bevel':
198
- // outer
199
- this.ctx.moveTo(tl, 0);
200
- this.ctx.lineTo(w - tr, 0);
201
- this.ctx.lineTo(w, tr);
202
- this.ctx.lineTo(w, h - br);
203
- this.ctx.lineTo(w - br, h);
204
- this.ctx.lineTo(bl, h);
205
- this.ctx.lineTo(0, h - bl);
206
- this.ctx.lineTo(0, tl);
207
- this.ctx.lineTo(tl, 0);
208
- this.ctx.lineTo(w - tr, 0);
209
-
210
- if (hollow) {
211
- // inner
212
- /**
213
- +-①----⑧-+
214
- ② ⑦
215
- | |
216
- ③ ⑥
217
- +-④----⑤-+
218
- */
219
- // x y p
220
- this.ctx.moveTo(tl2 + bw * wlf, bw * wtf); // ①
221
- this.ctx.lineTo(bw * wlf, tl2 + bw * wtf); // ②
222
- this.ctx.lineTo(bw * wlf, h - bl2 - bw * wbf); // ③
223
- this.ctx.lineTo(bw * wlf + bl2, h - bw * wbf); // ④
224
- this.ctx.lineTo(w - br2 - bw * wrf, h - bw * wbf); // ⑤
225
- this.ctx.lineTo(w - bw * wrf, h - br2 - bw * wbf); // ⑥
226
- this.ctx.lineTo(w - bw * wrf, tr2 + bw * wtf); // ⑦
227
- this.ctx.lineTo(w - tr2 - bw * wrf, bw * wtf); // ⑧
228
- this.ctx.lineTo(tl2 + bw * wrf, bw * wtf); // ①
229
- }
230
- break;
231
- case 'miter':
232
- // outer
233
- this.ctx.moveTo(0, 0);
234
- this.ctx.lineTo(w, 0);
235
- this.ctx.lineTo(w, h);
236
- this.ctx.lineTo(0, h);
237
- this.ctx.lineTo(0, 0);
238
-
239
- if (hollow) {
240
- // inner
241
- /**
242
- ①--------④
243
- | |
244
- | |
245
- | |
246
- ②--------③
247
- */
248
- // x y p
249
- this.ctx.moveTo(bw * wlf, bw * wtf); // ①
250
- this.ctx.lineTo(bw * wlf, h - bw * wbf); // ②
251
- this.ctx.lineTo(w - bw * wrf, h - bw * wbf); // ③
252
- this.ctx.lineTo(w - bw * wrf, bw * wtf); // ④
253
- this.ctx.lineTo(bw * wlf, bw * wtf); // ①
254
- }
255
- break;
256
- default:
257
- }
258
-
259
- this.ctx.closePath();
260
- };
261
-
262
- syncSizeToElement = (x: HTMLCanvasElement) => {
263
- const b = this.cachedBoundingRect;
264
-
265
- if (x.width !== b.width || x.height !== b.height) {
266
- x.width = b.width * this.pxRatio;
267
- x.height = b.height * this.pxRatio;
268
- x.style.width = `${b.width}px`;
269
- x.style.height = `${b.height}px`;
270
- }
271
- };
272
-
273
- clear = () => {
274
- if (!this.ctx) return false;
275
- if (!this.dirty) return false;
276
-
277
- const { width, height } = this.cachedBoundingRect;
278
-
279
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
280
- this.ctx.clearRect(
281
- 0,
282
- 0,
283
- width * window.devicePixelRatio,
284
- height * window.devicePixelRatio,
285
- );
286
- this.dirty = false;
287
- return true;
288
- }
289
-
290
- paint = (skipSamePointerPositionCheck?: boolean): boolean => {
291
- const store = this._store;
292
-
293
- const animationPlaying = store.animationQueue.includes(this);
294
- const samePosition = store.clientX === store.paintedClientX && store.clientY === store.paintedClientY;
295
-
296
- if (samePosition && !skipSamePointerPositionCheck && !animationPlaying) {
297
- return false;
298
- }
299
-
300
- if (!this.ctx) return false;
301
-
302
- this.dirty = true;
303
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
304
- this.ctx.clearRect(
305
- 0, 0,
306
- this.paintedWidth,
307
- this.paintedHeight
308
- );
309
-
310
- if (!store.mouseInBoundary && !animationPlaying) {
311
- return false;
312
- }
313
-
314
- if (!this.cachedImg.cachedReveal) {
315
- return false;
316
- }
317
-
318
- this.syncSizeToElement(this.element);
319
- this.cacheBoundingRect();
320
-
321
- const b = this.cachedBoundingRect;
322
-
323
- const relativeX = store.clientX - b.left;
324
- const relativeY = store.clientY - b.top;
325
-
326
- if (Number.isNaN(relativeX) || Number.isNaN(relativeY)) {
327
- return false;
328
- }
329
-
330
- const c = this.cachedStyle;
331
- this.getTrueFillRadius(c.trueFillRadius);
332
-
333
- this.paintedWidth = b.width * this.pxRatio;
334
- this.paintedHeight = b.height * this.pxRatio;
335
-
336
- const maxRadius = c.trueFillRadius[1];
337
-
338
- const inLeftBound = relativeX + maxRadius > 0;
339
- const inRightBound = relativeX - maxRadius < b.width;
340
- const inTopBound = relativeY + maxRadius > 0;
341
- const inBottomBound = relativeY - maxRadius < b.height;
342
-
343
- const mouseInRenderArea = inLeftBound && inRightBound && inTopBound && inBottomBound;
344
-
345
- if (!mouseInRenderArea && !animationPlaying) {
346
- return false;
347
- }
348
-
349
- this.cacheCanvasPaintingStyle();
350
- this.updateCachedBitmap();
351
-
352
- if (store.mouseInBoundary) {
353
- const mouseInCanvas = relativeX > 0 && relativeY > 0 && relativeX < b.width && relativeY < b.height;
354
-
355
- const fillPattern = this.cachedImg.cachedReveal.fillReveal.pattern;
356
- if (c.hoverLight && mouseInCanvas && fillPattern) {
357
- // draw fill.
358
- const radius = this.cachedImg.cachedReveal.fillReveal.radius;
359
- const putX = Math.floor(relativeX * this.pxRatio - radius);
360
- const putY = Math.floor(relativeY * this.pxRatio - radius);
361
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
362
- this.drawShape(false);
363
- this.ctx.translate(putX, putY);
364
- this.ctx.fillStyle = fillPattern;
365
- this.ctx.fill();
366
- }
367
-
368
- const diffuse = (c.diffuse && mouseInRenderArea) || mouseInCanvas;
369
- const borderPattern = this.cachedImg.cachedReveal.borderReveal.pattern;
370
- if (c.borderWidth !== 0 && borderPattern && diffuse) {
371
- // Draw border.
372
- const radius = this.cachedImg.cachedReveal.borderReveal.radius;
373
- const putX = Math.floor(relativeX * this.pxRatio - radius);
374
- const putY = Math.floor(relativeY * this.pxRatio - radius);
375
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
376
- this.drawShape(true);
377
- this.ctx.translate(putX, putY);
378
- this.ctx.fillStyle = borderPattern;
379
- this.ctx.fill();
380
- }
381
- }
382
-
383
- if (c.pressAnimation && this.mousePressed && this.mouseDownAnimateLogicFrame) {
384
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
385
- const grdRadius =
386
- c.pressAnimationFillMode === 'constrained' ? c.trueFillRadius[1] : Math.max(b.width, b.height);
387
- // prettier-ignore
388
- const animateGrd =
389
- this.mouseReleased && this.mouseUpClientX && this.mouseUpClientY
390
- ? this.ctx.createRadialGradient(
391
- (this.mouseUpClientX - b.left) * this.pxRatio,
392
- (this.mouseUpClientY - b.top) * this.pxRatio,
393
- 0,
394
- (this.mouseUpClientX - b.left) * this.pxRatio,
395
- (this.mouseUpClientY - b.top) * this.pxRatio,
396
- grdRadius * this.pxRatio
397
- )
398
- : this.ctx.createRadialGradient(
399
- relativeX * this.pxRatio,
400
- relativeY * this.pxRatio,
401
- 0,
402
- relativeX * this.pxRatio,
403
- relativeY * this.pxRatio,
404
- grdRadius * this.pxRatio
405
- );
406
- this.updateAnimateGrd(this.mouseDownAnimateLogicFrame, animateGrd);
407
-
408
- this.drawShape(false);
409
- this.ctx.fillStyle = animateGrd;
410
- this.ctx.fill();
411
- }
412
-
413
- return true;
414
- };
415
- }
1
+ import { RevealBoundaryStore } from './RevealBoundryStore.js';
2
+
3
+ import type { CachedRevealBitmap } from './utils/types.js';
4
+
5
+ import { BaseConfig } from './BaseConfig.js';
6
+
7
+ const createEmptyCanvas = () => document.createElement('canvas') as HTMLCanvasElement;
8
+
9
+ export class CanvasConfig extends BaseConfig<HTMLCanvasElement> {
10
+ ctx: CanvasRenderingContext2D | null;
11
+
12
+ protected cachedImg: CachedRevealBitmap;
13
+
14
+ constructor(store: RevealBoundaryStore, $canvas: HTMLCanvasElement, $container: HTMLElement) {
15
+ super(store, $canvas, $container);
16
+
17
+ this.ctx = $canvas.getContext('2d');
18
+
19
+ const cachedCanvas = {
20
+ borderReveal: createEmptyCanvas(),
21
+ fillReveal: createEmptyCanvas(),
22
+ };
23
+
24
+ const cachedCtx = {
25
+ borderReveal: cachedCanvas.borderReveal.getContext('2d'),
26
+ fillReveal: cachedCanvas.fillReveal.getContext('2d'),
27
+ };
28
+
29
+ this.cachedImg = {
30
+ cachedReveal: {
31
+ borderReveal: {
32
+ radiusFactor: -1,
33
+ radius: -1,
34
+ color: 'INVALID',
35
+ opacity: 0,
36
+ pattern: null,
37
+ },
38
+ fillReveal: {
39
+ radiusFactor: -1,
40
+ radius: -1,
41
+ color: 'INVALID',
42
+ opacity: 0,
43
+ pattern: null,
44
+ },
45
+ },
46
+ cachedCanvas,
47
+ cachedCtx,
48
+ };
49
+ }
50
+
51
+ updateCachedReveal = () => {
52
+ if (!this.fillDirty) return;
53
+
54
+ const c = this.cachedStyle;
55
+ const radius = c.trueFillRadius[1] * this.pxRatio;
56
+ const size = radius * 2;
57
+
58
+ const lastStyle = this.cachedImg.cachedReveal;
59
+
60
+ const { borderReveal, fillReveal } = this.cachedImg.cachedCanvas;
61
+ const { borderReveal: borderRevealCtx, fillReveal: fillRevealCtx } = this.cachedImg.cachedCtx;
62
+
63
+ this._store.updateMaxRadius(c.trueFillRadius[1] * c.borderFillRadius);
64
+
65
+ for (let i = 0; i < 2; i++) {
66
+ // 0 means border, 1 means hover light.
67
+ //-----------------------------------------
68
+ // Last rendered style.
69
+ const ls = i === 0 ? lastStyle.borderReveal : lastStyle.fillReveal;
70
+ const color = i === 0 ? c.borderColor : c.hoverLightColor;
71
+ const fillRadiusFactor = i === 0 ? c.borderFillRadius : c.hoverLightFillRadius;
72
+
73
+ const doNotNeedUpdateReveal =
74
+ color === ls.color && c.opacity === ls.opacity && fillRadiusFactor === ls.radiusFactor;
75
+
76
+ if (doNotNeedUpdateReveal) continue;
77
+
78
+ const reveal = i === 0 ? borderReveal : fillReveal;
79
+ const ctx = i === 0 ? borderRevealCtx : fillRevealCtx;
80
+
81
+ this.syncSizeToRevealRadius(reveal, fillRadiusFactor);
82
+
83
+ if (!ctx) continue;
84
+
85
+ ctx.clearRect(0, 0, reveal.width, reveal.height);
86
+
87
+ const fillAlpha = i === 0 ? c.opacity : c.opacity * 0.5;
88
+ const trueRadius = radius * fillRadiusFactor;
89
+ const grd = ctx.createRadialGradient(trueRadius, trueRadius, 0, trueRadius, trueRadius, trueRadius);
90
+
91
+ grd.addColorStop(0, `rgba(${color}, ${fillAlpha})`);
92
+ grd.addColorStop(1, `rgba(${color}, 0.0)`);
93
+
94
+ const trueSize = size * fillRadiusFactor;
95
+ ctx.fillStyle = grd;
96
+ ctx.clearRect(0, 0, trueSize, trueSize);
97
+ ctx.fillRect(0, 0, trueSize, trueSize);
98
+
99
+ ls.radius = trueRadius;
100
+ ls.radiusFactor = fillRadiusFactor;
101
+ ls.color = color;
102
+ ls.opacity = c.opacity;
103
+
104
+ if (!this.ctx) continue;
105
+ ls.pattern = this.ctx.createPattern(reveal, 'no-repeat');
106
+ }
107
+
108
+ this.fillDirty = false;
109
+ };
110
+
111
+ updateCachedBitmap = () => {
112
+ if (!this.ctx) return;
113
+ this.updateCachedReveal();
114
+ };
115
+
116
+ private updateAnimateGrd = (frame: number, grd: CanvasGradient) => {
117
+ const { pressAnimationColor, opacity } = this.cachedStyle;
118
+
119
+ const _innerAlpha = opacity * (0.2 - frame);
120
+ const _outerAlpha = opacity * (0.1 - frame * 0.07);
121
+ const _outerBorder = 0.1 + frame * 0.8;
122
+
123
+ const innerAlpha = _innerAlpha < 0 ? 0 : _innerAlpha;
124
+ const outerAlpha = _outerAlpha < 0 ? 0 : _outerAlpha;
125
+ let outerBorder = 0;
126
+ outerBorder = _outerBorder > 1 ? 1 : _outerBorder;
127
+ outerBorder = _outerBorder < 0 ? 0 : _outerBorder;
128
+
129
+ grd.addColorStop(0, `rgba(${pressAnimationColor},${innerAlpha})`);
130
+ grd.addColorStop(outerBorder * 0.55, `rgba(${pressAnimationColor},${outerAlpha})`);
131
+ grd.addColorStop(outerBorder, `rgba(${pressAnimationColor}, 0)`);
132
+ };
133
+
134
+ private drawShape = (hollow: boolean) => {
135
+ if (!this.ctx) return;
136
+
137
+ const b = this.cachedBoundingRect;
138
+ const w = b.width * this.pxRatio;
139
+ const h = b.height * this.pxRatio;
140
+
141
+ const c = this.cachedStyle;
142
+ const bw = c.borderWidth * this.pxRatio;
143
+
144
+ const tl = c.topLeftBorderDecorationRadius * this.pxRatio;
145
+ const tr = c.topRightBorderDecorationRadius * this.pxRatio;
146
+ const bl = c.bottomLeftBorderDecorationRadius * this.pxRatio;
147
+ const br = c.bottomRightBorderDecorationRadius * this.pxRatio;
148
+
149
+ const wlf = c.withLeftBorderFactor;
150
+ const wrf = c.withRightBorderFactor;
151
+ const wtf = c.withTopBorderFactor;
152
+ const wbf = c.withBottomBorderFactor;
153
+
154
+ const tl2 = bw < tl ? tl - bw : 0;
155
+ const tr2 = bw < tr ? tr - bw : 0;
156
+ const bl2 = bw < bl ? bl - bw : 0;
157
+ const br2 = bw < br ? br - bw : 0;
158
+
159
+ this.ctx.beginPath();
160
+
161
+ switch (c.borderDecorationType) {
162
+ // Oh... Fuck... It's painful...
163
+ case 'round':
164
+ // outer
165
+ this.ctx.moveTo(tl, 0);
166
+ this.ctx.arcTo(w, 0, w, h, tr);
167
+ this.ctx.lineTo(w, h - br);
168
+ this.ctx.arcTo(w, h, br, h, br);
169
+ this.ctx.lineTo(bl, h);
170
+ this.ctx.arcTo(0, h, 0, br, bl);
171
+ this.ctx.lineTo(0, tl);
172
+ this.ctx.arcTo(0, 0, tl, 0, tl);
173
+
174
+ if (hollow) {
175
+ /* prettier-ignore-start */
176
+ // inner
177
+ /**
178
+ +-①----⑧-+
179
+ ② ⑦
180
+ | |
181
+ ③ ⑥
182
+ +-④----⑤-+
183
+ */
184
+ // x y r p
185
+ this.ctx.moveTo(tl2, bw * wtf); // ①
186
+ this.ctx.arcTo(bw * wlf, bw * wtf, bw * wlf, tl2 + bw * wtf, tl2); // ②
187
+ this.ctx.lineTo(bw * wlf, h - bw * wbf - bl2); // ③
188
+ this.ctx.arcTo(bw * wlf, h - bw * wbf, bw * wlf + bl2, h - bw * wbf, bl2); // ④
189
+ this.ctx.lineTo(w - bw * wrf - br2, h - bw * wbf); // ⑤
190
+ this.ctx.arcTo(w - bw * wrf, h - bw * wbf, w - bw * wrf, h - bw * wbf - br2, br2); // ⑥
191
+ this.ctx.lineTo(w - bw * wrf, bw * wtf + tr2); // ⑦
192
+ this.ctx.arcTo(w - bw * wrf, bw * wtf, w - bw * wrf - tr2, bw * wtf, tr2); // ⑧
193
+ this.ctx.lineTo(tl2, bw * wtf); // ①
194
+ /* prettier-ignore-end */
195
+ }
196
+ break;
197
+ case 'bevel':
198
+ // outer
199
+ this.ctx.moveTo(tl, 0);
200
+ this.ctx.lineTo(w - tr, 0);
201
+ this.ctx.lineTo(w, tr);
202
+ this.ctx.lineTo(w, h - br);
203
+ this.ctx.lineTo(w - br, h);
204
+ this.ctx.lineTo(bl, h);
205
+ this.ctx.lineTo(0, h - bl);
206
+ this.ctx.lineTo(0, tl);
207
+ this.ctx.lineTo(tl, 0);
208
+ this.ctx.lineTo(w - tr, 0);
209
+
210
+ if (hollow) {
211
+ // inner
212
+ /**
213
+ +-①----⑧-+
214
+ ② ⑦
215
+ | |
216
+ ③ ⑥
217
+ +-④----⑤-+
218
+ */
219
+ // x y p
220
+ this.ctx.moveTo(tl2 + bw * wlf, bw * wtf); // ①
221
+ this.ctx.lineTo(bw * wlf, tl2 + bw * wtf); // ②
222
+ this.ctx.lineTo(bw * wlf, h - bl2 - bw * wbf); // ③
223
+ this.ctx.lineTo(bw * wlf + bl2, h - bw * wbf); // ④
224
+ this.ctx.lineTo(w - br2 - bw * wrf, h - bw * wbf); // ⑤
225
+ this.ctx.lineTo(w - bw * wrf, h - br2 - bw * wbf); // ⑥
226
+ this.ctx.lineTo(w - bw * wrf, tr2 + bw * wtf); // ⑦
227
+ this.ctx.lineTo(w - tr2 - bw * wrf, bw * wtf); // ⑧
228
+ this.ctx.lineTo(tl2 + bw * wrf, bw * wtf); // ①
229
+ }
230
+ break;
231
+ case 'miter':
232
+ // outer
233
+ this.ctx.moveTo(0, 0);
234
+ this.ctx.lineTo(w, 0);
235
+ this.ctx.lineTo(w, h);
236
+ this.ctx.lineTo(0, h);
237
+ this.ctx.lineTo(0, 0);
238
+
239
+ if (hollow) {
240
+ // inner
241
+ /**
242
+ ①--------④
243
+ | |
244
+ | |
245
+ | |
246
+ ②--------③
247
+ */
248
+ // x y p
249
+ this.ctx.moveTo(bw * wlf, bw * wtf); // ①
250
+ this.ctx.lineTo(bw * wlf, h - bw * wbf); // ②
251
+ this.ctx.lineTo(w - bw * wrf, h - bw * wbf); // ③
252
+ this.ctx.lineTo(w - bw * wrf, bw * wtf); // ④
253
+ this.ctx.lineTo(bw * wlf, bw * wtf); // ①
254
+ }
255
+ break;
256
+ default:
257
+ }
258
+
259
+ this.ctx.closePath();
260
+ };
261
+
262
+ syncSizeToElement = (x: HTMLCanvasElement) => {
263
+ const b = this.cachedBoundingRect;
264
+
265
+ if (x.width !== b.width || x.height !== b.height) {
266
+ x.width = b.width * this.pxRatio;
267
+ x.height = b.height * this.pxRatio;
268
+ x.style.width = `${b.width}px`;
269
+ x.style.height = `${b.height}px`;
270
+ }
271
+ };
272
+
273
+ clear = () => {
274
+ if (!this.ctx) return false;
275
+ if (!this.dirty) return false;
276
+
277
+ const { width, height } = this.cachedBoundingRect;
278
+
279
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
280
+ this.ctx.clearRect(
281
+ 0,
282
+ 0,
283
+ width * window.devicePixelRatio,
284
+ height * window.devicePixelRatio,
285
+ );
286
+ this.dirty = false;
287
+ return true;
288
+ }
289
+
290
+ paint = (skipSamePointerPositionCheck?: boolean): boolean => {
291
+ const store = this._store;
292
+
293
+ const animationPlaying = store.animationQueue.includes(this);
294
+ const samePosition = store.clientX === store.paintedClientX && store.clientY === store.paintedClientY;
295
+
296
+ if (samePosition && !skipSamePointerPositionCheck && !animationPlaying) {
297
+ return false;
298
+ }
299
+
300
+ if (!this.ctx) return false;
301
+
302
+ this.dirty = true;
303
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
304
+ this.ctx.clearRect(
305
+ 0, 0,
306
+ this.paintedWidth,
307
+ this.paintedHeight
308
+ );
309
+
310
+ if (!store.mouseInBoundary && !animationPlaying) {
311
+ return false;
312
+ }
313
+
314
+ if (!this.cachedImg.cachedReveal) {
315
+ return false;
316
+ }
317
+
318
+ this.syncSizeToElement(this.element);
319
+ this.cacheBoundingRect();
320
+
321
+ const b = this.cachedBoundingRect;
322
+
323
+ const relativeX = store.clientX - b.left;
324
+ const relativeY = store.clientY - b.top;
325
+
326
+ if (Number.isNaN(relativeX) || Number.isNaN(relativeY)) {
327
+ return false;
328
+ }
329
+
330
+ const c = this.cachedStyle;
331
+ this.getTrueFillRadius(c.trueFillRadius);
332
+
333
+ this.paintedWidth = b.width * this.pxRatio;
334
+ this.paintedHeight = b.height * this.pxRatio;
335
+
336
+ const maxRadius = c.trueFillRadius[1];
337
+
338
+ const inLeftBound = relativeX + maxRadius > 0;
339
+ const inRightBound = relativeX - maxRadius < b.width;
340
+ const inTopBound = relativeY + maxRadius > 0;
341
+ const inBottomBound = relativeY - maxRadius < b.height;
342
+
343
+ const mouseInRenderArea = inLeftBound && inRightBound && inTopBound && inBottomBound;
344
+
345
+ if (!mouseInRenderArea && !animationPlaying) {
346
+ return false;
347
+ }
348
+
349
+ this.cacheCanvasPaintingStyle();
350
+ this.updateCachedBitmap();
351
+
352
+ if (store.mouseInBoundary) {
353
+ const mouseInCanvas = relativeX > 0 && relativeY > 0 && relativeX < b.width && relativeY < b.height;
354
+
355
+ const fillPattern = this.cachedImg.cachedReveal.fillReveal.pattern;
356
+ if (c.hoverLight && mouseInCanvas && fillPattern) {
357
+ // draw fill.
358
+ const radius = this.cachedImg.cachedReveal.fillReveal.radius;
359
+ const putX = Math.floor(relativeX * this.pxRatio - radius);
360
+ const putY = Math.floor(relativeY * this.pxRatio - radius);
361
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
362
+ this.drawShape(false);
363
+ this.ctx.translate(putX, putY);
364
+ this.ctx.fillStyle = fillPattern;
365
+ this.ctx.fill();
366
+ }
367
+
368
+ const diffuse = (c.diffuse && mouseInRenderArea) || mouseInCanvas;
369
+ const borderPattern = this.cachedImg.cachedReveal.borderReveal.pattern;
370
+ if (c.borderWidth !== 0 && borderPattern && diffuse) {
371
+ // Draw border.
372
+ const radius = this.cachedImg.cachedReveal.borderReveal.radius;
373
+ const putX = Math.floor(relativeX * this.pxRatio - radius);
374
+ const putY = Math.floor(relativeY * this.pxRatio - radius);
375
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
376
+ this.drawShape(true);
377
+ this.ctx.translate(putX, putY);
378
+ this.ctx.fillStyle = borderPattern;
379
+ this.ctx.fill();
380
+ }
381
+ }
382
+
383
+ if (c.pressAnimation && this.mousePressed && this.mouseDownAnimateLogicFrame) {
384
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
385
+ const grdRadius =
386
+ c.pressAnimationFillMode === 'constrained' ? c.trueFillRadius[1] : Math.max(b.width, b.height);
387
+ // prettier-ignore
388
+ const animateGrd =
389
+ this.mouseReleased && this.mouseUpClientX && this.mouseUpClientY
390
+ ? this.ctx.createRadialGradient(
391
+ (this.mouseUpClientX - b.left) * this.pxRatio,
392
+ (this.mouseUpClientY - b.top) * this.pxRatio,
393
+ 0,
394
+ (this.mouseUpClientX - b.left) * this.pxRatio,
395
+ (this.mouseUpClientY - b.top) * this.pxRatio,
396
+ grdRadius * this.pxRatio
397
+ )
398
+ : this.ctx.createRadialGradient(
399
+ relativeX * this.pxRatio,
400
+ relativeY * this.pxRatio,
401
+ 0,
402
+ relativeX * this.pxRatio,
403
+ relativeY * this.pxRatio,
404
+ grdRadius * this.pxRatio
405
+ );
406
+ this.updateAnimateGrd(this.mouseDownAnimateLogicFrame, animateGrd);
407
+
408
+ this.drawShape(false);
409
+ this.ctx.fillStyle = animateGrd;
410
+ this.ctx.fill();
411
+ }
412
+
413
+ return true;
414
+ };
415
+ }