Skip to content

Commit

Permalink
refactor frame rendering and input capture
Browse files Browse the repository at this point in the history
  • Loading branch information
finbargiusti committed Sep 19, 2023
1 parent 606ee4c commit b0dcc57
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 349 deletions.
6 changes: 3 additions & 3 deletions src/App.svelte
Expand Up @@ -8,10 +8,9 @@
<script lang="ts">
import Game from './Game.svelte';
import Play from './Play.svelte';
import type { CanvasOptions } from './logic/canvas';
import { Connection } from './logic/connection';
import { Connection, MessageEmitter } from './logic/connection';
import { deCompressRecording, type RecordingData } from './logic/dtr';
import { getConnection, setConnection } from './logic/state';
import { getConnection, setConnection, setEmitter } from './logic/state';
let idInput: string = '';
Expand Down Expand Up @@ -89,6 +88,7 @@
function playRecording(d: ArrayBuffer) {
try {
recording = deCompressRecording(d);
setEmitter(new MessageEmitter());
pushPageState('playback');
} catch (e) {
console.error(e);
Expand Down
175 changes: 47 additions & 128 deletions src/Play.svelte
Expand Up @@ -2,24 +2,27 @@
This is the file to be used for playing both recordings, and live streams.
-->
<script lang="ts">
import { onMount } from 'svelte';
import type { CanvasOptions } from './logic/canvas';
import type {
RecordableMessageTitle,
RecordingData,
RecordingFrames
} from './logic/dtr';
import { drawLine, type Line } from './logic/line';
import type { MessageData, FrameData } from './logic/message';
import Frame from './lib/Frame.svelte';
import {getEmitter } from './logic/state';
import type { MessageData } from './logic/message';
import FrameView from './lib/FrameView.svelte';
import CursorView from './lib/CursorView.svelte';
export let recording: RecordingData;
let [bg, opts, ...data] = recording;
const msg = getEmitter();
let messages: MessageData<'chat'>[] = [];
function addMessage(data: MessageData<'chat'>, from: string) {
let canvas: HTMLCanvasElement;
msg.on('chat', (data, from) => {
data.from = data.from ?? from;
messages = [...messages, data];
Expand All @@ -30,63 +33,13 @@
// This is inefficient, but it shouldn't matter
messages = messages.filter(d => JSON.stringify(d) !== comp);
}, 20000);
}
let mainCanvas: HTMLCanvasElement;
let frames: FrameData[] = [];
/* If we need component reference, this is how it's done */
// let frameComponents: { [id: string]: Frame } = {};
const MAX_FRAMES = 5;
async function addFrame(f: FrameData) {
frames.push(f);
if (frames.length > MAX_FRAMES) {
const old_frame = frames.shift();
await drawLine(mainCanvas.getContext('2d'), old_frame.line);
}
frames = frames;
return;
}
async function updateFrame(id: string, line: Line) {
const f = frames.find(v => v.id == id);
if (!f) {
await addFrame({
id,
line,
});
return;
}
f.line = line;
frames = frames;
return;
}
async function handleFrame(d: RecordingFrames[number]) {
switch (d.title) {
case 'chat':
addMessage(d.data as MessageData<'chat'>, d.from);
break;
case 'frame-update':
const data = d.data as MessageData<'frame-update'>;
updateFrame(data.id, data.line);
break;
}
}
})
let index = 0;
function renderFrame() {
const thisFrame = data[index];
handleFrame(thisFrame);
msg.emit(thisFrame.title, thisFrame.data, thisFrame.from);
if (index + 1 < data.length) {
index++;
setTimeout(renderFrame, data[index].time - thisFrame.time);
Expand All @@ -99,7 +52,7 @@
const background = new Image();
background.onload = () => {
mainCanvas.getContext('2d').drawImage(background, 0, 0);
canvas.getContext('2d').drawImage(background, 0, 0);
};
background.src = bg;
Expand All @@ -108,89 +61,55 @@
});
</script>

<div class="background">
<canvas
class="frame main"
height={opts.height}
width={opts.width}
bind:this={mainCanvas}
style="background-color: {opts.bgColor}"
/>
{#each frames as frameData}
<Frame {frameData} {opts} />
{/each}

<FrameView {opts} bind:canvas>
<CursorView {opts} />
<div class="chatbox">
{#each messages as m}
<p>
{m.from.slice(0, 5)}: {m.text}
</p>
{/each}
</div>
</div>
</FrameView>

<style lang="scss">
.background {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
transform: translateZ(0px);
display: flex;
justify-content: center;
align-items: center;
:global(.frame) {
position: absolute;
transform-origin: top left;
background-color: transparent;
max-width: 100%;
max-height: 100%;
object-fit: contain;
pointer-events: none;
&.main {
pointer-events: all;
background-color: var(--bg-color);
}
@keyframes message {
0% {
opacity: 0;
max-height: 0px;
}
@keyframes message {
0% {
opacity: 0;
max-height: 0px;
}
2% {
opacity: 1;
max-height: 200px;
}
2% {
opacity: 1;
max-height: 200px;
}
96% {
opacity: 1;
}
96% {
opacity: 1;
}
100% {
opacity: 0;
}
100% {
opacity: 0;
}
}
.chatbox {
overflow: hidden;
position: absolute;
bottom: 0px;
left: 0px;
width: 400px;
p {
box-sizing: border-box;
padding: 6px 12px 6px 12px;
font-size: 16px;
background-color: #222222;
animation: message 20s;
&:nth-child(even) {
background-color: #333333;
}
.chatbox {
overflow: hidden;
position: absolute;
bottom: 0px;
left: 0px;
width: 400px;
p {
box-sizing: border-box;
padding: 6px 12px 6px 12px;
font-size: 16px;
background-color: #222222;
animation: message 20s;
&:nth-child(even) {
background-color: #333333;
}
}
}
Expand Down
51 changes: 51 additions & 0 deletions src/lib/CursorView.svelte
@@ -0,0 +1,51 @@
<script lang="ts">
import type { CanvasOptions } from "../logic/canvas";
import type { CursorMoveMessage } from "../logic/message";
import {getEmitter} from "../logic/state";
let mouseCanvas: HTMLCanvasElement;
export let opts: CanvasOptions;
let cursors: { [id: string]: CursorMoveMessage } = {};
function updateCursor(pos: CursorMoveMessage, from: string) {
cursors[from] = pos;
cursors = cursors;
}
const msg = getEmitter();
msg.on('cursor-move', updateCursor);
$: {
if (opts && mouseCanvas && cursors) {
const ctx = mouseCanvas.getContext('2d');
ctx.clearRect(0, 0, opts.width, opts.height);
for (const name in cursors) {
const c = cursors[name];
const {x, y} = c.pos;
const {width, color} = c.opts;
ctx.save();
ctx.beginPath();
ctx.arc(x, y, width/2, 0, 2 * Math.PI);
ctx.fillStyle = `${color}80`; // add transparency
ctx.fill();
ctx.font = '12px serif';
ctx.textAlign = 'center';
ctx.fillText(name.substring(0, 6), x, y - 20 - width);
ctx.restore();
}
}
}
</script>


{#if opts}
<canvas
class="frame"
height={opts.height}
width={opts.width}
bind:this={mouseCanvas}
/>
{/if}

0 comments on commit b0dcc57

Please sign in to comment.