Skip to content

Commit a112e04

Browse files
committed
improved porcupine perf
1 parent 7bf2f2a commit a112e04

File tree

4 files changed

+44
-73
lines changed

4 files changed

+44
-73
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
"type": "module",
3535
"dependencies": {
36-
"@aninest/extensions": "^1.8.0",
36+
"@aninest/extensions": "^1.8.2",
3737
"aninest": "^4.11.0"
3838
}
3939
}

pnpm-lock.yaml

Lines changed: 5 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/routes/porcupine/+layout.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
</script>
2525

2626
<svelte:head>
27+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
2728
{@html webManifestLink}
2829
</svelte:head>
2930
<slot />

src/routes/porcupine/+page.svelte

Lines changed: 37 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
mag,
1818
addLocalListener,
1919
subVec,
20-
type Animation
20+
type Animation,
21+
ZERO_VEC2,
22+
copy,
23+
copyObject
2124
} from 'aninest';
25+
import { getUpdateLayer } from '@aninest/extensions';
2226
import { onMount } from 'svelte';
2327
2428
type Color = { r: number; g: number; b: number };
@@ -33,28 +37,32 @@
3337
3438
const WHITE: Color = { r: 255, g: 255, b: 255 };
3539
onMount(() => {
40+
const updateLayer = getUpdateLayer(requestAnimationFrame);
3641
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);
3744
// 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);
3947
// 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 });
4149
const setColor = (ctx: CanvasRenderingContext2D) => {
42-
const color = getLocalState(animInfo.children.color);
50+
const color = getLocalState(anim.children.color, colorCache);
4351
ctx.strokeStyle = `rgb(${color.r} ${color.g} ${color.b})`;
4452
};
4553
const drawLine = (ctx: CanvasRenderingContext2D) => {
46-
const { p1, p2 } = getStateTree(animInfo.children.shape);
54+
getStateTree(anim.children.shape, shapeCache);
4755
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);
5058
ctx.stroke();
5159
};
5260
const draw = (ctx: CanvasRenderingContext2D) => {
5361
setColor(ctx);
5462
drawLine(ctx);
5563
};
5664
const animLoop = (dt: number) => {
57-
return updateAnimation(animInfo, dt);
65+
return updateAnimation(anim, dt);
5866
};
5967
const onPointChange = (animInfo: Animation<Vec2>) => {
6068
const oldPt = getLocalInterpingTo(animInfo);
@@ -63,8 +71,8 @@
6371
const diff = Math.max(mag(subVec(newPt, oldPt)), 1);
6472
6573
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;
6876
6977
changeInterpFunction(
7078
animInfo,
@@ -76,31 +84,28 @@
7684
)
7785
);
7886
};
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)
8189
);
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)
8492
);
8593
8694
return {
8795
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);
89100
},
90101
setP2(p2: Vec2) {
91-
modifyTo(animInfo.children.shape, { p2 });
102+
modifyTo(anim.children.shape, { p2 });
92103
},
93104
setColor(color: Color) {
94-
return modifyTo(animInfo, { color });
105+
return modifyTo(anim, { color });
95106
},
96107
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
104109
};
105110
};
106111
const canvas: HTMLCanvasElement = document.querySelector('#porky-canvas')!;
@@ -110,7 +115,10 @@
110115
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
111116
type DrawableLine = ReturnType<typeof createLine>;
112117
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+
});
114122
const randColor = () => {
115123
const sinceLastClick = (performance.now() - lastClicked) / 1000;
116124
const inv = 1 / (sinceLastClick + 1);
@@ -154,7 +162,7 @@
154162
for (let line of lines) {
155163
setTimeout(
156164
() => randomizeLine(line, withTimeout),
157-
increasinglySlower(10000 * Math.random() * sinceLastClick + 1000 * sinceLastClick)
165+
increasinglySlower(10000 * Math.random() * sinceLastClick + 1000 * sinceLastClick + 50)
158166
);
159167
}
160168
};
@@ -166,15 +174,14 @@
166174
};
167175
168176
let downCt = 0;
169-
170177
const onMove = (e: PointerEvent) => {
171178
if (downCt === 0) return;
172179
const x = e.clientX * devicePixelRatio;
173180
const y = e.clientY * devicePixelRatio;
174181
const p = newVec2(x, y);
182+
const points = { p1: p, p2: p };
175183
lines.forEach((line) => {
176-
line.setP1(p);
177-
line.setP2(p);
184+
line.setPoints(points);
178185
});
179186
lastClicked = performance.now();
180187
};
@@ -203,30 +210,16 @@
203210
setTimeout(() => {
204211
onResize();
205212
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++) {
207214
const canvasCenter = newVec2(canvas.width / 2, canvas.height / 2);
208215
lines.push(createLine(canvasCenter, canvasCenter));
209216
}
210217
211218
// even though the interval in between each refresh is randomized
212219
// the animation always moves smoothly regardless
213220
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-
}
225221
}, 0);
226222
window.addEventListener('resize', onResize);
227-
window.addEventListener('orientationchange', () => {
228-
setTimeout(() => onResize(), 500);
229-
});
230223
// also call when the device is rotated or the pixel ratio changes
231224
window.addEventListener('orientationchange', onResize);
232225
window.addEventListener('devicePixelRatio', onResize);
@@ -236,23 +229,6 @@
236229
canvas.addEventListener('pointerdown', onDown);
237230
canvas.addEventListener('pointermove', onMove);
238231
// 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-
};
256232
});
257233
</script>
258234

0 commit comments

Comments
 (0)