|
17 | 17 | mag,
|
18 | 18 | addLocalListener,
|
19 | 19 | subVec,
|
20 |
| - type Animation |
| 20 | + type Animation, |
| 21 | + ZERO_VEC2, |
| 22 | + copy, |
| 23 | + copyObject |
21 | 24 | } from 'aninest';
|
| 25 | + import { getUpdateLayer } from '@aninest/extensions'; |
22 | 26 | import { onMount } from 'svelte';
|
23 | 27 |
|
24 | 28 | type Color = { r: number; g: number; b: number };
|
|
33 | 37 |
|
34 | 38 | const WHITE: Color = { r: 255, g: 255, b: 255 };
|
35 | 39 | onMount(() => {
|
| 40 | + const updateLayer = getUpdateLayer(requestAnimationFrame); |
36 | 41 | const createLine = (p1: Vec2, p2: Vec2, color: Color = WHITE) => {
|
| 42 | + const shapeCache: { p1: Vec2; p2: Vec2 } = { p1: copy(ZERO_VEC2), p2: copy(ZERO_VEC2) }; |
| 43 | + const colorCache: Color = copyObject(WHITE); |
37 | 44 | // set global interp function to slerp(1s)
|
38 |
| - const animInfo = createAnimation<Line>({ shape: { p1, p2 }, color }, getSlerp(1)); |
| 45 | + const anim = createAnimation<Line>({ shape: { p1, p2 }, color }, getSlerp(1)); |
| 46 | + updateLayer.mount(anim); |
39 | 47 | // set interp function of color to linearInterp(0.5)
|
40 |
| - changeInterpFunction(animInfo, getLinearInterp(0.5), { shape: false }); |
| 48 | + changeInterpFunction(anim, getLinearInterp(0.5), { shape: false }); |
41 | 49 | const setColor = (ctx: CanvasRenderingContext2D) => {
|
42 |
| - const color = getLocalState(animInfo.children.color); |
| 50 | + const color = getLocalState(anim.children.color, colorCache); |
43 | 51 | ctx.strokeStyle = `rgb(${color.r} ${color.g} ${color.b})`;
|
44 | 52 | };
|
45 | 53 | const drawLine = (ctx: CanvasRenderingContext2D) => {
|
46 |
| - const { p1, p2 } = getStateTree(animInfo.children.shape); |
| 54 | + getStateTree(anim.children.shape, shapeCache); |
47 | 55 | ctx.beginPath();
|
48 |
| - ctx.moveTo(p1.x, p1.y); |
49 |
| - ctx.lineTo(p2.x, p2.y); |
| 56 | + ctx.moveTo(shapeCache.p1.x, shapeCache.p1.y); |
| 57 | + ctx.lineTo(shapeCache.p2.x, shapeCache.p2.y); |
50 | 58 | ctx.stroke();
|
51 | 59 | };
|
52 | 60 | const draw = (ctx: CanvasRenderingContext2D) => {
|
53 | 61 | setColor(ctx);
|
54 | 62 | drawLine(ctx);
|
55 | 63 | };
|
56 | 64 | const animLoop = (dt: number) => {
|
57 |
| - return updateAnimation(animInfo, dt); |
| 65 | + return updateAnimation(anim, dt); |
58 | 66 | };
|
59 | 67 | const onPointChange = (animInfo: Animation<Vec2>) => {
|
60 | 68 | const oldPt = getLocalInterpingTo(animInfo);
|
|
63 | 71 | const diff = Math.max(mag(subVec(newPt, oldPt)), 1);
|
64 | 72 |
|
65 | 73 | const screenMag = mag(newVec2(canvas.width, canvas.height));
|
66 |
| -
|
67 |
| - const sinceLastClick = Math.log((performance.now() - lastClicked) / 1000 + 1); |
| 74 | + const innerDt = (performance.now() - lastClicked) / 1000; |
| 75 | + const sinceLastClick = innerDt > 0.64 ? Math.log(innerDt + 1) : 0; |
68 | 76 |
|
69 | 77 | changeInterpFunction(
|
70 | 78 | animInfo,
|
|
76 | 84 | )
|
77 | 85 | );
|
78 | 86 | };
|
79 |
| - addLocalListener(animInfo.children.shape.children.p1, 'start', () => |
80 |
| - onPointChange(animInfo.children.shape.children.p1) |
| 87 | + addLocalListener(anim.children.shape.children.p1, 'start', () => |
| 88 | + onPointChange(anim.children.shape.children.p1) |
81 | 89 | );
|
82 |
| - addLocalListener(animInfo.children.shape.children.p2, 'start', () => |
83 |
| - onPointChange(animInfo.children.shape.children.p2) |
| 90 | + addLocalListener(anim.children.shape.children.p2, 'start', () => |
| 91 | + onPointChange(anim.children.shape.children.p2) |
84 | 92 | );
|
85 | 93 |
|
86 | 94 | return {
|
87 | 95 | setP1(p1: Vec2) {
|
88 |
| - modifyTo(animInfo.children.shape, { p1 }); |
| 96 | + modifyTo(anim.children.shape, { p1 }); |
| 97 | + }, |
| 98 | + setPoints(points: { p1: Vec2; p2: Vec2 }) { |
| 99 | + modifyTo(anim.children.shape, points); |
89 | 100 | },
|
90 | 101 | setP2(p2: Vec2) {
|
91 |
| - modifyTo(animInfo.children.shape, { p2 }); |
| 102 | + modifyTo(anim.children.shape, { p2 }); |
92 | 103 | },
|
93 | 104 | setColor(color: Color) {
|
94 |
| - return modifyTo(animInfo, { color }); |
| 105 | + return modifyTo(anim, { color }); |
95 | 106 | },
|
96 | 107 | update: animLoop,
|
97 |
| - draw: draw, |
98 |
| - addStartListener(listener: () => void) { |
99 |
| - addRecursiveListener(animInfo, 'start', listener); |
100 |
| - }, |
101 |
| - removeStartListener(listener: () => void) { |
102 |
| - removeRecursiveListener(animInfo, 'start', listener); |
103 |
| - } |
| 108 | + draw: draw |
104 | 109 | };
|
105 | 110 | };
|
106 | 111 | const canvas: HTMLCanvasElement = document.querySelector('#porky-canvas')!;
|
|
110 | 115 | const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
111 | 116 | type DrawableLine = ReturnType<typeof createLine>;
|
112 | 117 | const lines: ReturnType<typeof createLine>[] = [];
|
113 |
| -
|
| 118 | + updateLayer.subscribe('updateWithDeltaTime', (dt) => { |
| 119 | + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
| 120 | + lines.forEach((line) => line.draw(ctx)); |
| 121 | + }); |
114 | 122 | const randColor = () => {
|
115 | 123 | const sinceLastClick = (performance.now() - lastClicked) / 1000;
|
116 | 124 | const inv = 1 / (sinceLastClick + 1);
|
|
154 | 162 | for (let line of lines) {
|
155 | 163 | setTimeout(
|
156 | 164 | () => randomizeLine(line, withTimeout),
|
157 |
| - increasinglySlower(10000 * Math.random() * sinceLastClick + 1000 * sinceLastClick) |
| 165 | + increasinglySlower(10000 * Math.random() * sinceLastClick + 1000 * sinceLastClick + 50) |
158 | 166 | );
|
159 | 167 | }
|
160 | 168 | };
|
|
166 | 174 | };
|
167 | 175 |
|
168 | 176 | let downCt = 0;
|
169 |
| -
|
170 | 177 | const onMove = (e: PointerEvent) => {
|
171 | 178 | if (downCt === 0) return;
|
172 | 179 | const x = e.clientX * devicePixelRatio;
|
173 | 180 | const y = e.clientY * devicePixelRatio;
|
174 | 181 | const p = newVec2(x, y);
|
| 182 | + const points = { p1: p, p2: p }; |
175 | 183 | lines.forEach((line) => {
|
176 |
| - line.setP1(p); |
177 |
| - line.setP2(p); |
| 184 | + line.setPoints(points); |
178 | 185 | });
|
179 | 186 | lastClicked = performance.now();
|
180 | 187 | };
|
|
203 | 210 | setTimeout(() => {
|
204 | 211 | onResize();
|
205 | 212 | const canvasMag = mag(newVec2(canvas.width, canvas.height));
|
206 |
| - for (let i = 0; i < canvasMag * 2; i++) { |
| 213 | + for (let i = 0; i < canvasMag; i++) { |
207 | 214 | const canvasCenter = newVec2(canvas.width / 2, canvas.height / 2);
|
208 | 215 | lines.push(createLine(canvasCenter, canvasCenter));
|
209 | 216 | }
|
210 | 217 |
|
211 | 218 | // even though the interval in between each refresh is randomized
|
212 | 219 | // the animation always moves smoothly regardless
|
213 | 220 | randomizeLines();
|
214 |
| - animLoop(0); |
215 |
| -
|
216 |
| - for (let line of lines) { |
217 |
| - line.addStartListener(() => { |
218 |
| - if (running) return; |
219 |
| - console.log('resuming'); |
220 |
| - running = true; |
221 |
| - lastTime = performance.now(); |
222 |
| - requestAnimationFrame(animLoop); |
223 |
| - }); |
224 |
| - } |
225 | 221 | }, 0);
|
226 | 222 | window.addEventListener('resize', onResize);
|
227 |
| - window.addEventListener('orientationchange', () => { |
228 |
| - setTimeout(() => onResize(), 500); |
229 |
| - }); |
230 | 223 | // also call when the device is rotated or the pixel ratio changes
|
231 | 224 | window.addEventListener('orientationchange', onResize);
|
232 | 225 | window.addEventListener('devicePixelRatio', onResize);
|
|
236 | 229 | canvas.addEventListener('pointerdown', onDown);
|
237 | 230 | canvas.addEventListener('pointermove', onMove);
|
238 | 231 | // get the canvas magnitudes
|
239 |
| -
|
240 |
| - let lastTime: number | undefined = undefined; |
241 |
| - let running = false; |
242 |
| - const animLoop = (time: number) => { |
243 |
| - const dt = lastTime ? (time - lastTime) / 1000 : 0; |
244 |
| - lastTime = time; |
245 |
| - let updateAgain = lines.reduce((needsUpdate, line) => { |
246 |
| - return line.update(dt) || needsUpdate; |
247 |
| - }, false); |
248 |
| - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
249 |
| - lines.forEach((line) => line.draw(ctx)); |
250 |
| - if (updateAgain) requestAnimationFrame(animLoop); |
251 |
| - else { |
252 |
| - lastTime = undefined; |
253 |
| - running = false; |
254 |
| - } |
255 |
| - }; |
256 | 232 | });
|
257 | 233 | </script>
|
258 | 234 |
|
|
0 commit comments