Files
pihkaal-me/app/components/Screen.vue

79 lines
1.8 KiB
Vue

<script lang="ts" setup>
const canvas = useTemplateRef("canvas");
const updateCallbacks = new Set<UpdateCallback>();
const renderCallbacks = new Set<RenderCallback>();
let ctx: CanvasRenderingContext2D | null = null;
let animationFrameId: number | null = null;
let lastFrameTime = 0;
let lastRealFrameTime = 0;
const registerUpdateCallback = (callback: UpdateCallback) => {
updateCallbacks.add(callback);
return () => updateCallbacks.delete(callback);
};
const registerRenderCallback = (callback: RenderCallback) => {
renderCallbacks.add(callback);
return () => renderCallbacks.delete(callback);
};
const renderFrame = (timestamp: number) => {
if (!ctx) return;
const deltaTime = timestamp - lastFrameTime;
lastFrameTime = timestamp;
const start = Date.now();
// update
for (const callback of updateCallbacks) {
callback(deltaTime, lastRealFrameTime);
}
// render
ctx.clearRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
for (const callback of renderCallbacks) {
callback(ctx);
}
lastRealFrameTime = Date.now() - start;
animationFrameId = requestAnimationFrame(renderFrame);
};
onMounted(() => {
if (!canvas.value) throw new Error("Missing canvas");
ctx = canvas.value.getContext("2d");
if (!ctx) throw new Error("Missing 2d context");
provide("registerUpdateCallback", registerUpdateCallback);
provide("registerRenderCallback", registerRenderCallback);
animationFrameId = requestAnimationFrame(renderFrame);
});
onUnmounted(() => {
if (animationFrameId !== null) {
cancelAnimationFrame(animationFrameId);
}
});
</script>
<template>
<canvas
ref="canvas"
:width="SCREEN_WIDTH"
:height="SCREEN_HEIGHT"
:style="{
margin: '0',
border: '1px solid red',
}"
/>
<slot v-if="canvas" />
</template>