sandboxels/mods/video.js

125 lines
4.7 KiB
JavaScript
Raw Normal View History

2024-02-25 12:18:36 -05:00
{
let currentVideoFrames = [];
let videoFrame = 0;
let processed = 0;
let started = false;
let videoWidth = 0;
let videoHeight = 0;
let FPS = tps / 2;
const splitHex = (hex) => hex.slice(1).match(/../g).map(a => Math.floor(parseInt(a, 16)));
const hexify = (rgb) => "#" + rgb.map(a => Math.floor(a).toString(16).padStart(2, "0")).join("");
function colorLerp(color_, color2_, t) {
const color = splitHex(color_);
const color2 = splitHex(color2_);
const r = (1 - t) * color[0] + t * color2[0];
const g = (1 - t) * color[1] + t * color2[1];
const b = (1 - t) * color[2] + t * color2[2];
return hexify([r, g, b]);
}
elements.video_pixel = {
color: "#ffffff",
hidden: true,
category: "special",
canPlace: false,
tool: () => {},
tick: (pixel) => {
if (started && pixelTicks % (tps / FPS) == 0) {
processed++;
if (processed >= videoWidth * videoHeight) {
videoFrame++;
processed = 0;
}
if (videoFrame >= currentVideoFrames.length) {
started = false;
processed = 0;
currentVideoFrames = [];
videoFrame = 0;
}
pixel.color = currentVideoFrames[videoFrame % currentVideoFrames.length][pixel.y][pixel.x - Math.floor((width - videoWidth) / 2)];
}
}
}
const chunk = (arr, size) => arr.map((_, i) => i % size == 0 ? arr.slice(i, i + size) : null).filter(a => a)
elements.video = {
color: ["#78bbff","#5bb81a"],
onSelect: () => {
if (!localStorage.getItem("video.js/tutorial")) {
alert("Videos might take a while (up to a few minutes with larger videos) to load. During that time, do not delete any video pixels");
localStorage.setItem("video.js/tutorial", true);
}
const file = document.createElement("input");
file.type = "file";
file.accept = "video/*";
file.click();
file.onchange = () => {
setTimeout(() => {
const video = document.createElement("video");
video.preload = "auto";
const url = URL.createObjectURL(file.files[0]);
video.src = url;
video.load();
let canvas = document.createElement("canvas", {
willReadFrequently: true
});
let ctx = canvas.getContext("2d");
video.onloadeddata = async () => {
const w = video.videoWidth;
const h = video.videoHeight;
const newHeight = height;
const newWidth = Math.floor((w / h) * newHeight);
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
videoWidth = newWidth;
videoHeight = newHeight;
const videoLength = video.duration * FPS;
const seekBy = video.duration / videoLength;
for (let i = 0; i < newWidth; i++) {
for (let y = 0; y < newHeight; y++) {
const x = Math.floor((width - newWidth) / 2) + i;
if (pixelMap[x][y]) deletePixel(x, y);
createPixel("video_pixel", x, y);
}
};
2024-03-21 19:45:41 -04:00
currentVideoFrames = [];
2024-02-25 12:18:36 -05:00
video.currentTime = 0;
video.onseeked = () => {
ctx.drawImage(video, 0, 0, newWidth, newHeight);
const imageData = chunk(chunk(Array.from(ctx.getImageData(0, 0, newWidth, newHeight).data), 4), newWidth);
const frame = [];
for (let y = 0; y < newHeight; y++) {
frame[y] = [];
for (let x = 0; x < newWidth; x++) {
frame[y][x] = `#${imageData[y][x].slice(0, 3).map(a => a.toString(16).padStart(2, "0")).join("")}`
}
}
currentVideoFrames.push(frame);
if (currentVideoFrames.length >= videoLength) {
const audio = new Audio();
audio.src = url;
audio.play();
started = true;
videoFrame = 0;
} else {
video.currentTime += seekBy;
}
}
}
}, 500);
}
},
tool: () => {},
category: "special"
}
}