@ax-design/reveal-highlight 0.4.1 → 0.4.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 -21
- package/README.md +75 -75
- package/build/RevealStateManager.d.ts.map +1 -1
- package/build/main.js +1 -1
- package/es/CustomElements.js +60 -60
- package/es/RevealStateManager.d.ts.map +1 -1
- package/es/RevealStateManager.js +8 -0
- package/es/RevealStateManager.js.map +1 -1
- package/es/SvgConfig.js +70 -70
- package/es/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -8
- package/src/BaseConfig.ts +310 -310
- package/src/CanvasConfig.ts +415 -415
- package/src/ComputedStyleStorage.ts +89 -89
- package/src/CustomElements.ts +272 -272
- package/src/Profiler.ts +44 -44
- package/src/RevealBoundryStore.ts +291 -291
- package/src/RevealStateManager.ts +86 -78
- package/src/SvgConfig.ts +422 -422
- package/src/config.ts +26 -26
- package/src/cssTypedOM.d.ts +52 -52
- package/src/index.ts +219 -219
- package/src/utils/extractRGBValue.ts +58 -58
- package/src/utils/isValidateBorderDecorationType.ts +8 -8
- package/src/utils/types.ts +105 -105
- package/src/variables.ts +1 -1
|
@@ -1,291 +1,291 @@
|
|
|
1
|
-
import { CanvasConfig } from './CanvasConfig.js';
|
|
2
|
-
import { SvgConfig } from './SvgConfig.js';
|
|
3
|
-
|
|
4
|
-
type Config = CanvasConfig | SvgConfig;
|
|
5
|
-
|
|
6
|
-
export class RevealBoundaryStore {
|
|
7
|
-
id: number;
|
|
8
|
-
container: HTMLElement;
|
|
9
|
-
|
|
10
|
-
// The current cursor position relative to window.
|
|
11
|
-
clientX = -1000;
|
|
12
|
-
clientY = -1000;
|
|
13
|
-
|
|
14
|
-
// The cursor position of painted reveal effect.
|
|
15
|
-
paintedClientX = -1000;
|
|
16
|
-
paintedClientY = -1000;
|
|
17
|
-
|
|
18
|
-
mouseInBoundary = false;
|
|
19
|
-
canvasList: Config[] = [];
|
|
20
|
-
|
|
21
|
-
animationQueue: Config[] = [];
|
|
22
|
-
animationFrame: number | null = null;
|
|
23
|
-
|
|
24
|
-
mouseUpClientX = null;
|
|
25
|
-
mouseUpClientY = null;
|
|
26
|
-
mouseDownAnimateStartFrame = null;
|
|
27
|
-
mouseDownAnimateCurrentFrame = null;
|
|
28
|
-
mouseDownAnimateReleasedFrame = null;
|
|
29
|
-
mouseDownAnimateLogicFrame = null;
|
|
30
|
-
mousePressed = false;
|
|
31
|
-
mouseReleased = false;
|
|
32
|
-
|
|
33
|
-
maxRadius = -1;
|
|
34
|
-
|
|
35
|
-
requestedAnimationFrame = false;
|
|
36
|
-
|
|
37
|
-
constructor(id: number, $el: HTMLElement) {
|
|
38
|
-
this.id = id;
|
|
39
|
-
this.container = $el;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
addReveal = ($element: HTMLCanvasElement | SVGElement, $container: HTMLElement) => {
|
|
43
|
-
let config: Config;
|
|
44
|
-
const elementType = Object.prototype.toString.call($element);
|
|
45
|
-
|
|
46
|
-
if (elementType === '[object HTMLCanvasElement]') {
|
|
47
|
-
config = new CanvasConfig(this, $element as HTMLCanvasElement, $container);
|
|
48
|
-
} else if (elementType === '[object SVGSVGElement]') {
|
|
49
|
-
config = new SvgConfig(this, $element as SVGSVGElement, $container);
|
|
50
|
-
} else {
|
|
51
|
-
throw new TypeError(`Unknown element type, got: ${elementType}, Canvas or SVG element expected.`)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
config.cacheBoundingRect();
|
|
55
|
-
config.cacheCanvasPaintingStyle();
|
|
56
|
-
config.updateCachedBitmap();
|
|
57
|
-
|
|
58
|
-
this.canvasList.push(config);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
removeReveal = ($el: HTMLCanvasElement | SVGElement) => {
|
|
62
|
-
this.canvasList = this.canvasList.filter((config) => {
|
|
63
|
-
return config && config.element !== $el;
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
updateMaxRadius = (x: number) => {
|
|
68
|
-
this.maxRadius = Math.max(x, this.maxRadius);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
requestPaintAll = (cleanup?: boolean) => {
|
|
72
|
-
if (this.requestedAnimationFrame) return;
|
|
73
|
-
this.requestedAnimationFrame = true;
|
|
74
|
-
|
|
75
|
-
this.animationFrame = window.requestAnimationFrame((frame) => {
|
|
76
|
-
this.requestedAnimationFrame = false;
|
|
77
|
-
this.paintAll(cleanup ? -1 : frame, true);
|
|
78
|
-
});
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
updatePointerPosition = (x: number, y: number) => {
|
|
82
|
-
this.clientX = x;
|
|
83
|
-
this.clientY = y;
|
|
84
|
-
|
|
85
|
-
const boundingBox = this.container.getBoundingClientRect();
|
|
86
|
-
|
|
87
|
-
let mouseInBoundary = true;
|
|
88
|
-
|
|
89
|
-
if (x < boundingBox.left - this.maxRadius) mouseInBoundary = false;
|
|
90
|
-
if (x > boundingBox.right + this.maxRadius) mouseInBoundary = false;
|
|
91
|
-
if (y < boundingBox.top - this.maxRadius) mouseInBoundary = false;
|
|
92
|
-
if (y > boundingBox.bottom + this.maxRadius) mouseInBoundary = false;
|
|
93
|
-
|
|
94
|
-
this.mouseInBoundary = mouseInBoundary;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
onPointerMoveOnScreen = (x: number, y: number) => {
|
|
98
|
-
this.updatePointerPosition(x, y);
|
|
99
|
-
if (!this.mouseInBoundary) return;
|
|
100
|
-
this.requestPaintAll();
|
|
101
|
-
return true;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Repaint all canvas while user's pointer has entered the boundary.
|
|
106
|
-
*/
|
|
107
|
-
onPointerEnterBoundary = () => {
|
|
108
|
-
this.mouseInBoundary = true;
|
|
109
|
-
|
|
110
|
-
if (!this.animationFrame) {
|
|
111
|
-
this.requestPaintAll();
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Clear all canvas while user's pointer leave the bounary.
|
|
117
|
-
*/
|
|
118
|
-
onPointerLeaveBoundary = () => {
|
|
119
|
-
this.mouseInBoundary = false;
|
|
120
|
-
this.requestPaintAll(true);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* The painting method that draw everything on your screen.
|
|
125
|
-
* @param frame current frame index, if this value is 0, it means all canvas
|
|
126
|
-
* should be cleaned.
|
|
127
|
-
* @param skipSamePointerPositionCheck Literally.
|
|
128
|
-
*/
|
|
129
|
-
private paintAll = (frame: number, skipSamePointerPositionCheck?: boolean) => {
|
|
130
|
-
if (!skipSamePointerPositionCheck && !this.mouseInBoundary && this.animationQueue.length === 0) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Animation duration checkup
|
|
135
|
-
for (let i = 0; i < this.animationQueue.length; i++) {
|
|
136
|
-
const config = this.animationQueue[i];
|
|
137
|
-
|
|
138
|
-
if (!frame || config.currentFrameId === frame) {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
config.currentFrameId = frame;
|
|
143
|
-
|
|
144
|
-
// Initialize the animation parameter
|
|
145
|
-
if (config.mouseDownAnimateStartFrame === null) {
|
|
146
|
-
config.mouseDownAnimateStartFrame = frame;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Calculate how long the animation has been playing
|
|
150
|
-
const relativeFrame = frame - config.mouseDownAnimateStartFrame;
|
|
151
|
-
config.mouseDownAnimateCurrentFrame = relativeFrame;
|
|
152
|
-
|
|
153
|
-
const speed = config.cachedStyle.pressAnimationSpeed;
|
|
154
|
-
const accelerateRate = config.cachedStyle.releaseAnimationAccelerateRate;
|
|
155
|
-
|
|
156
|
-
// We assume that Ripple is a one-way linear process from
|
|
157
|
-
// no diffusion to fully diffused,
|
|
158
|
-
// and Logic Frame represents the progress of the animation
|
|
159
|
-
// in this process.
|
|
160
|
-
let unitLogicFrame = relativeFrame;
|
|
161
|
-
if (config.mouseReleased && config.mouseDownAnimateReleasedFrame) {
|
|
162
|
-
unitLogicFrame += (relativeFrame - config.mouseDownAnimateReleasedFrame) * accelerateRate;
|
|
163
|
-
}
|
|
164
|
-
config.mouseDownAnimateLogicFrame = unitLogicFrame / speed;
|
|
165
|
-
|
|
166
|
-
if (config.mouseDownAnimateLogicFrame > 1) {
|
|
167
|
-
// It will be cleaned later
|
|
168
|
-
config.cleanedUpForAnimation = true;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Paint the canvas one by one
|
|
173
|
-
for (let i = 0; i < this.canvasList.length; i++) {
|
|
174
|
-
const config = this.canvasList[i];
|
|
175
|
-
config.currentFrameId = frame;
|
|
176
|
-
|
|
177
|
-
if (config.cleanedUpForAnimation) {
|
|
178
|
-
this.cleanUpAnimationUnit(config, false);
|
|
179
|
-
config.cleanedUpForAnimation = false;
|
|
180
|
-
} else {
|
|
181
|
-
const isPlayingAnimation = this.animationQueue.indexOf(config) === -1;
|
|
182
|
-
|
|
183
|
-
if (this.mouseInBoundary || isPlayingAnimation) {
|
|
184
|
-
config.paint(skipSamePointerPositionCheck);
|
|
185
|
-
} else {
|
|
186
|
-
config.clear(false);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.paintedClientX = this.clientX;
|
|
192
|
-
this.paintedClientY = this.clientY;
|
|
193
|
-
|
|
194
|
-
if (this.mouseInBoundary || this.animationQueue.length !== 0) {
|
|
195
|
-
this.requestPaintAll();
|
|
196
|
-
} else {
|
|
197
|
-
this.animationFrame = null;
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Clear all canvas.
|
|
203
|
-
*/
|
|
204
|
-
clearAll = (forAnimation: boolean) => {
|
|
205
|
-
this.cleanupAnimation(forAnimation, true);
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* This is the effect while user pressed on the reveal highlight,
|
|
210
|
-
* the ripple start to spread slowly.
|
|
211
|
-
*/
|
|
212
|
-
initializeAnimation = () => {
|
|
213
|
-
const config = this.canvasList.find((x) => x.mouseInCanvas());
|
|
214
|
-
|
|
215
|
-
if (!config) return;
|
|
216
|
-
|
|
217
|
-
if (!this.animationQueue.includes(config)) {
|
|
218
|
-
this.animationQueue.push(config);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
config.mouseDownAnimateStartFrame = null;
|
|
222
|
-
config.mousePressed = true;
|
|
223
|
-
config.mouseReleased = false;
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* This is the effect while user released it's pointer, the spreading
|
|
228
|
-
* ripple animation will start to accelerate.
|
|
229
|
-
*/
|
|
230
|
-
switchAnimation = () => {
|
|
231
|
-
for (let i = 0; i < this.animationQueue.length; i++) {
|
|
232
|
-
const config = this.animationQueue[i];
|
|
233
|
-
|
|
234
|
-
if (!config.mouseReleased) {
|
|
235
|
-
config.mouseReleased = true;
|
|
236
|
-
config.mouseDownAnimateReleasedFrame = config.mouseDownAnimateCurrentFrame;
|
|
237
|
-
config.mouseUpClientX = this.clientX;
|
|
238
|
-
config.mouseUpClientY = this.clientY;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Cleanup all animations.
|
|
245
|
-
* @param skipPaint Only cleanup data or also clean up the canvas
|
|
246
|
-
* @param forceClean Clean the canvas or not
|
|
247
|
-
*/
|
|
248
|
-
cleanupAnimation = (skipPaint?: boolean, forceClean?: boolean) => {
|
|
249
|
-
for (let i = 0; i < this.animationQueue.length; i++) {
|
|
250
|
-
const config = this.animationQueue[i];
|
|
251
|
-
|
|
252
|
-
this.cleanUpAnimationUnit(config, skipPaint, forceClean);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Clean up the config of an animation, but the canvas won't cleaned up.
|
|
258
|
-
* @param config The config to be cleaned up.
|
|
259
|
-
* @param skipPaint Skip painting.
|
|
260
|
-
* @param forceClean Force clear the painting.
|
|
261
|
-
*/
|
|
262
|
-
cleanUpAnimationUnit = (config: Config, skipPaint?: boolean, forceClean?: boolean) => {
|
|
263
|
-
config.mouseUpClientX = null;
|
|
264
|
-
config.mouseUpClientY = null;
|
|
265
|
-
config.mouseDownAnimateStartFrame = null;
|
|
266
|
-
config.mouseDownAnimateCurrentFrame = null;
|
|
267
|
-
config.mouseDownAnimateReleasedFrame = null;
|
|
268
|
-
config.mouseDownAnimateLogicFrame = null;
|
|
269
|
-
config.mousePressed = false;
|
|
270
|
-
config.mouseReleased = false;
|
|
271
|
-
|
|
272
|
-
const configIdx = this.animationQueue.indexOf(config);
|
|
273
|
-
if (configIdx > -1) {
|
|
274
|
-
this.animationQueue.splice(configIdx, 1);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (!skipPaint) {
|
|
278
|
-
if (forceClean) {
|
|
279
|
-
config.clear(false);
|
|
280
|
-
} else if (this.mouseInBoundary) {
|
|
281
|
-
config.paint(true);
|
|
282
|
-
} else {
|
|
283
|
-
config.clear(true);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
getRevealAnimationConfig = () => {
|
|
289
|
-
return this.canvasList.find((x) => x.mouseInCanvas()) || null;
|
|
290
|
-
};
|
|
291
|
-
}
|
|
1
|
+
import { CanvasConfig } from './CanvasConfig.js';
|
|
2
|
+
import { SvgConfig } from './SvgConfig.js';
|
|
3
|
+
|
|
4
|
+
type Config = CanvasConfig | SvgConfig;
|
|
5
|
+
|
|
6
|
+
export class RevealBoundaryStore {
|
|
7
|
+
id: number;
|
|
8
|
+
container: HTMLElement;
|
|
9
|
+
|
|
10
|
+
// The current cursor position relative to window.
|
|
11
|
+
clientX = -1000;
|
|
12
|
+
clientY = -1000;
|
|
13
|
+
|
|
14
|
+
// The cursor position of painted reveal effect.
|
|
15
|
+
paintedClientX = -1000;
|
|
16
|
+
paintedClientY = -1000;
|
|
17
|
+
|
|
18
|
+
mouseInBoundary = false;
|
|
19
|
+
canvasList: Config[] = [];
|
|
20
|
+
|
|
21
|
+
animationQueue: Config[] = [];
|
|
22
|
+
animationFrame: number | null = null;
|
|
23
|
+
|
|
24
|
+
mouseUpClientX = null;
|
|
25
|
+
mouseUpClientY = null;
|
|
26
|
+
mouseDownAnimateStartFrame = null;
|
|
27
|
+
mouseDownAnimateCurrentFrame = null;
|
|
28
|
+
mouseDownAnimateReleasedFrame = null;
|
|
29
|
+
mouseDownAnimateLogicFrame = null;
|
|
30
|
+
mousePressed = false;
|
|
31
|
+
mouseReleased = false;
|
|
32
|
+
|
|
33
|
+
maxRadius = -1;
|
|
34
|
+
|
|
35
|
+
requestedAnimationFrame = false;
|
|
36
|
+
|
|
37
|
+
constructor(id: number, $el: HTMLElement) {
|
|
38
|
+
this.id = id;
|
|
39
|
+
this.container = $el;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
addReveal = ($element: HTMLCanvasElement | SVGElement, $container: HTMLElement) => {
|
|
43
|
+
let config: Config;
|
|
44
|
+
const elementType = Object.prototype.toString.call($element);
|
|
45
|
+
|
|
46
|
+
if (elementType === '[object HTMLCanvasElement]') {
|
|
47
|
+
config = new CanvasConfig(this, $element as HTMLCanvasElement, $container);
|
|
48
|
+
} else if (elementType === '[object SVGSVGElement]') {
|
|
49
|
+
config = new SvgConfig(this, $element as SVGSVGElement, $container);
|
|
50
|
+
} else {
|
|
51
|
+
throw new TypeError(`Unknown element type, got: ${elementType}, Canvas or SVG element expected.`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
config.cacheBoundingRect();
|
|
55
|
+
config.cacheCanvasPaintingStyle();
|
|
56
|
+
config.updateCachedBitmap();
|
|
57
|
+
|
|
58
|
+
this.canvasList.push(config);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
removeReveal = ($el: HTMLCanvasElement | SVGElement) => {
|
|
62
|
+
this.canvasList = this.canvasList.filter((config) => {
|
|
63
|
+
return config && config.element !== $el;
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
updateMaxRadius = (x: number) => {
|
|
68
|
+
this.maxRadius = Math.max(x, this.maxRadius);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
requestPaintAll = (cleanup?: boolean) => {
|
|
72
|
+
if (this.requestedAnimationFrame) return;
|
|
73
|
+
this.requestedAnimationFrame = true;
|
|
74
|
+
|
|
75
|
+
this.animationFrame = window.requestAnimationFrame((frame) => {
|
|
76
|
+
this.requestedAnimationFrame = false;
|
|
77
|
+
this.paintAll(cleanup ? -1 : frame, true);
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
updatePointerPosition = (x: number, y: number) => {
|
|
82
|
+
this.clientX = x;
|
|
83
|
+
this.clientY = y;
|
|
84
|
+
|
|
85
|
+
const boundingBox = this.container.getBoundingClientRect();
|
|
86
|
+
|
|
87
|
+
let mouseInBoundary = true;
|
|
88
|
+
|
|
89
|
+
if (x < boundingBox.left - this.maxRadius) mouseInBoundary = false;
|
|
90
|
+
if (x > boundingBox.right + this.maxRadius) mouseInBoundary = false;
|
|
91
|
+
if (y < boundingBox.top - this.maxRadius) mouseInBoundary = false;
|
|
92
|
+
if (y > boundingBox.bottom + this.maxRadius) mouseInBoundary = false;
|
|
93
|
+
|
|
94
|
+
this.mouseInBoundary = mouseInBoundary;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
onPointerMoveOnScreen = (x: number, y: number) => {
|
|
98
|
+
this.updatePointerPosition(x, y);
|
|
99
|
+
if (!this.mouseInBoundary) return;
|
|
100
|
+
this.requestPaintAll();
|
|
101
|
+
return true;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Repaint all canvas while user's pointer has entered the boundary.
|
|
106
|
+
*/
|
|
107
|
+
onPointerEnterBoundary = () => {
|
|
108
|
+
this.mouseInBoundary = true;
|
|
109
|
+
|
|
110
|
+
if (!this.animationFrame) {
|
|
111
|
+
this.requestPaintAll();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Clear all canvas while user's pointer leave the bounary.
|
|
117
|
+
*/
|
|
118
|
+
onPointerLeaveBoundary = () => {
|
|
119
|
+
this.mouseInBoundary = false;
|
|
120
|
+
this.requestPaintAll(true);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The painting method that draw everything on your screen.
|
|
125
|
+
* @param frame current frame index, if this value is 0, it means all canvas
|
|
126
|
+
* should be cleaned.
|
|
127
|
+
* @param skipSamePointerPositionCheck Literally.
|
|
128
|
+
*/
|
|
129
|
+
private paintAll = (frame: number, skipSamePointerPositionCheck?: boolean) => {
|
|
130
|
+
if (!skipSamePointerPositionCheck && !this.mouseInBoundary && this.animationQueue.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Animation duration checkup
|
|
135
|
+
for (let i = 0; i < this.animationQueue.length; i++) {
|
|
136
|
+
const config = this.animationQueue[i];
|
|
137
|
+
|
|
138
|
+
if (!frame || config.currentFrameId === frame) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
config.currentFrameId = frame;
|
|
143
|
+
|
|
144
|
+
// Initialize the animation parameter
|
|
145
|
+
if (config.mouseDownAnimateStartFrame === null) {
|
|
146
|
+
config.mouseDownAnimateStartFrame = frame;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Calculate how long the animation has been playing
|
|
150
|
+
const relativeFrame = frame - config.mouseDownAnimateStartFrame;
|
|
151
|
+
config.mouseDownAnimateCurrentFrame = relativeFrame;
|
|
152
|
+
|
|
153
|
+
const speed = config.cachedStyle.pressAnimationSpeed;
|
|
154
|
+
const accelerateRate = config.cachedStyle.releaseAnimationAccelerateRate;
|
|
155
|
+
|
|
156
|
+
// We assume that Ripple is a one-way linear process from
|
|
157
|
+
// no diffusion to fully diffused,
|
|
158
|
+
// and Logic Frame represents the progress of the animation
|
|
159
|
+
// in this process.
|
|
160
|
+
let unitLogicFrame = relativeFrame;
|
|
161
|
+
if (config.mouseReleased && config.mouseDownAnimateReleasedFrame) {
|
|
162
|
+
unitLogicFrame += (relativeFrame - config.mouseDownAnimateReleasedFrame) * accelerateRate;
|
|
163
|
+
}
|
|
164
|
+
config.mouseDownAnimateLogicFrame = unitLogicFrame / speed;
|
|
165
|
+
|
|
166
|
+
if (config.mouseDownAnimateLogicFrame > 1) {
|
|
167
|
+
// It will be cleaned later
|
|
168
|
+
config.cleanedUpForAnimation = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Paint the canvas one by one
|
|
173
|
+
for (let i = 0; i < this.canvasList.length; i++) {
|
|
174
|
+
const config = this.canvasList[i];
|
|
175
|
+
config.currentFrameId = frame;
|
|
176
|
+
|
|
177
|
+
if (config.cleanedUpForAnimation) {
|
|
178
|
+
this.cleanUpAnimationUnit(config, false);
|
|
179
|
+
config.cleanedUpForAnimation = false;
|
|
180
|
+
} else {
|
|
181
|
+
const isPlayingAnimation = this.animationQueue.indexOf(config) === -1;
|
|
182
|
+
|
|
183
|
+
if (this.mouseInBoundary || isPlayingAnimation) {
|
|
184
|
+
config.paint(skipSamePointerPositionCheck);
|
|
185
|
+
} else {
|
|
186
|
+
config.clear(false);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.paintedClientX = this.clientX;
|
|
192
|
+
this.paintedClientY = this.clientY;
|
|
193
|
+
|
|
194
|
+
if (this.mouseInBoundary || this.animationQueue.length !== 0) {
|
|
195
|
+
this.requestPaintAll();
|
|
196
|
+
} else {
|
|
197
|
+
this.animationFrame = null;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Clear all canvas.
|
|
203
|
+
*/
|
|
204
|
+
clearAll = (forAnimation: boolean) => {
|
|
205
|
+
this.cleanupAnimation(forAnimation, true);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* This is the effect while user pressed on the reveal highlight,
|
|
210
|
+
* the ripple start to spread slowly.
|
|
211
|
+
*/
|
|
212
|
+
initializeAnimation = () => {
|
|
213
|
+
const config = this.canvasList.find((x) => x.mouseInCanvas());
|
|
214
|
+
|
|
215
|
+
if (!config) return;
|
|
216
|
+
|
|
217
|
+
if (!this.animationQueue.includes(config)) {
|
|
218
|
+
this.animationQueue.push(config);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
config.mouseDownAnimateStartFrame = null;
|
|
222
|
+
config.mousePressed = true;
|
|
223
|
+
config.mouseReleased = false;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* This is the effect while user released it's pointer, the spreading
|
|
228
|
+
* ripple animation will start to accelerate.
|
|
229
|
+
*/
|
|
230
|
+
switchAnimation = () => {
|
|
231
|
+
for (let i = 0; i < this.animationQueue.length; i++) {
|
|
232
|
+
const config = this.animationQueue[i];
|
|
233
|
+
|
|
234
|
+
if (!config.mouseReleased) {
|
|
235
|
+
config.mouseReleased = true;
|
|
236
|
+
config.mouseDownAnimateReleasedFrame = config.mouseDownAnimateCurrentFrame;
|
|
237
|
+
config.mouseUpClientX = this.clientX;
|
|
238
|
+
config.mouseUpClientY = this.clientY;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Cleanup all animations.
|
|
245
|
+
* @param skipPaint Only cleanup data or also clean up the canvas
|
|
246
|
+
* @param forceClean Clean the canvas or not
|
|
247
|
+
*/
|
|
248
|
+
cleanupAnimation = (skipPaint?: boolean, forceClean?: boolean) => {
|
|
249
|
+
for (let i = 0; i < this.animationQueue.length; i++) {
|
|
250
|
+
const config = this.animationQueue[i];
|
|
251
|
+
|
|
252
|
+
this.cleanUpAnimationUnit(config, skipPaint, forceClean);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Clean up the config of an animation, but the canvas won't cleaned up.
|
|
258
|
+
* @param config The config to be cleaned up.
|
|
259
|
+
* @param skipPaint Skip painting.
|
|
260
|
+
* @param forceClean Force clear the painting.
|
|
261
|
+
*/
|
|
262
|
+
cleanUpAnimationUnit = (config: Config, skipPaint?: boolean, forceClean?: boolean) => {
|
|
263
|
+
config.mouseUpClientX = null;
|
|
264
|
+
config.mouseUpClientY = null;
|
|
265
|
+
config.mouseDownAnimateStartFrame = null;
|
|
266
|
+
config.mouseDownAnimateCurrentFrame = null;
|
|
267
|
+
config.mouseDownAnimateReleasedFrame = null;
|
|
268
|
+
config.mouseDownAnimateLogicFrame = null;
|
|
269
|
+
config.mousePressed = false;
|
|
270
|
+
config.mouseReleased = false;
|
|
271
|
+
|
|
272
|
+
const configIdx = this.animationQueue.indexOf(config);
|
|
273
|
+
if (configIdx > -1) {
|
|
274
|
+
this.animationQueue.splice(configIdx, 1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!skipPaint) {
|
|
278
|
+
if (forceClean) {
|
|
279
|
+
config.clear(false);
|
|
280
|
+
} else if (this.mouseInBoundary) {
|
|
281
|
+
config.paint(true);
|
|
282
|
+
} else {
|
|
283
|
+
config.clear(true);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
getRevealAnimationConfig = () => {
|
|
289
|
+
return this.canvasList.find((x) => x.mouseInCanvas()) || null;
|
|
290
|
+
};
|
|
291
|
+
}
|