|
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