2024-07-08 16:35:42 +02:00

75 lines
3.1 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as fs from "fs"
import { fileURLToPath } from 'url';
import path from 'path';
import CanvasKitInit from "canvaskit-wasm";
import UPNG from "@pdf-lib/upng"
import {loadTextureAtlas, SkeletonRenderer, Skeleton, SkeletonBinary, AnimationState, AnimationStateData, AtlasAttachmentLoader, Physics, loadSkeletonData, SkeletonDrawable} from "../dist/index.js"
// Get the current directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// This app loads the Spineboy skeleton and its atlas, then renders Spineboy's "portal" animation
// at 30 fps to individual frames, which are then encoded as an animated PNG (APNG), which is
// written to "output.png"
async function main() {
// Initialize CanvasKit and create a surface and canvas.
const ck = await CanvasKitInit();
const surface = ck.MakeSurface(600, 400);
if (!surface) throw new Error();
// Load atlas
const atlas = await loadTextureAtlas(ck, __dirname + "/assets/spineboy.atlas", async (path) => fs.readFileSync(path));
// Load the skeleton data
const skeletonData = await loadSkeletonData(__dirname + "/assets/spineboy-pro.skel", atlas, async (path) => fs.readFileSync(path));
// Create a SkeletonDrawable
const drawable = new SkeletonDrawable(skeletonData);
// Scale and position the skeleton
drawable.skeleton.x = 300;
drawable.skeleton.y = 380;
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.5;
// Set the "hoverboard" animation on track one
drawable.animationState.setAnimation(0, "hoverboard", true);
// Create a skeleton renderer to render the skeleton to the canvas with
const renderer = new SkeletonRenderer(ck);
// Render the full animation in 1/30 second steps (30fps) and save it to an APNG
const animationDuration = skeletonData.findAnimation("hoverboard")?.duration ?? 0;
const FRAME_TIME = 1 / 30; // 30 FPS
let deltaTime = 0;
const frames = [];
const imageInfo = { width: 600, height: 400, colorType: ck.ColorType.RGBA_8888, alphaType: ck.AlphaType.Unpremul, colorSpace: ck.ColorSpace.SRGB };
const pixelArray = ck.Malloc(Uint8Array, imageInfo.width * imageInfo.height * 4);
for (let time = 0; time <= animationDuration; time += deltaTime) {
// Get the canvas object to render to the surface to
const canvas = surface.getCanvas();
// Clear the canvas
canvas.clear(ck.WHITE);
// Update the drawable, which will advance the animation(s)
// apply them to the skeleton, and update the skeleton's pose.
drawable.update(deltaTime);
// Render the skeleton to the canvas
renderer.render(canvas, drawable)
// Read the pixels of the current frame and store it.
canvas.readPixels(0, 0, imageInfo, pixelArray);
frames.push(new Uint8Array(pixelArray.toTypedArray()).buffer.slice(0));
// First frame has deltaTime 0, subsequent use FRAME_TIME
deltaTime = FRAME_TIME;
}
const apng = UPNG.default.encode(frames, 600, 400, 0, frames.map(() => FRAME_TIME * 1000));
fs.writeFileSync('output.png', Buffer.from(apng));
}
main();