JSFiddle - React, Tailwind, and code Playground
by Josh Pullen
JavaScript
class JSONVideo {
constructor(initialData) {
this.frames = [];
this.duration = 0;
if (typeof initialData === "object") {
this.insertFrame(0, initialData);
}
}
insertFrame(time, data) {
if (time < 0) throw new Error("Cannot insert frame at time < 0");
if (this.frames.some(frame => frame.time === time)) {
throw new Error("Frame at time already exists");
}
if (typeof data !== "object") {
throw new Error("Data must be JSON-serializable object");
}
this.frames.push({ time, data });
this.duration = Math.max(time, this.duration);
}
exportToString() {
const frames = this.frames.sort((a, b) => a.time - b.time);
const metadata = {
version: "0.1.0",
duration: this.duration
}
let str = `M ${JSON.stringify(metadata)}\n`;
for (let i = 0; i < frames.length; i++) {
let mode = this.frames[i].time === 0 ? "K" : "S";
str += JSONVideo.encode(this.frames[i], this.frames[i - 1] || null, mode);
str += "\n";
}
return str;
}
static encode(frame, previousFrame, mode) {
switch (mode) {
case "K": {
// Keyframe
return `K ${frame.time} ${JSON.stringify(frame.data)}`;
}
case "S": {
// Set
let patchObj = {};
for (const [key, value] of Object.entries(frame.data)) {
if (key in previousFrame.data) {
if (JSON.stringify(value) !== JSON.stringify(previousFrame.data[key])) {
patchObj[key] = value;
}
} else {
patchObj[key] = value;
}
}
return `P ${frame.time} ${JSON.stringify(patchObj)}`;
}
case "P": {
// TODO: Patch
// Deeper diffing than replacing top-level values
// Good for small changes deep in a tree
}
}
}
static encodeFrameGroup(frames) {
for ()
}
}
const video = new JSONVideo({ a: 2, b: ["x", "y"] });
video.insertFrame(500, { a: 4, b: ["x", "y"]...