Bladeren bron

[canvaskit] HDPI support, fix blending, physics example

Mario Zechner 1 jaar geleden
bovenliggende
commit
81312c8983

+ 1 - 0
spine-ts/index.html

@@ -26,6 +26,7 @@
         <li><a href="/spine-canvaskit/example/animation-state-events.html">Animation State Events</a></li>
         <li><a href="/spine-canvaskit/example/mix-and-match.html">Skins Mix &amp; Match</a></li>
         <li><a href="/spine-canvaskit/example/ik-following.html">IK Following</a></li>
+        <li><a href="/spine-canvaskit/example/physics.html">Physics</a></li>
         <li><a href="/spine-canvaskit/example/micro-benchmark.html">Micro Benchmark</a></li>
       </ul>
       <li>Pixi</li>

+ 7 - 1
spine-ts/spine-canvaskit/example/animation-state-events.html

@@ -18,7 +18,7 @@
 <body class="p-4 flex flex-col items-center">
     <h1>Animation State Events</h1>
     <p class="mb-4">Open the console in the developer tools to view events logs.</p>
-    <canvas id=foo width=600 height=400 style="margin: 0 auto;"></canvas>
+    <canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
 </body>
 
 <script type="module">
@@ -28,8 +28,14 @@
         return await response.arrayBuffer();
     }
 
+    const canvasElement = document.querySelector("#foo");
+    const dpr = window.devicePixelRatio || 1;
+    canvasElement.width = canvasElement.clientWidth * dpr;
+    canvasElement.height = canvasElement.clientHeight * dpr;
+
     const ck = await CanvasKitInit();
     const surface = ck.MakeCanvasSurface('foo');
+    surface.getCanvas().scale(dpr, dpr);
 
     const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
     const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);

+ 7 - 2
spine-ts/spine-canvaskit/example/ik-following.html

@@ -18,7 +18,7 @@
 <body class="p-4 flex flex-col items-center">
     <h1>IK Following</h1>
     <p class="mb-4">Click/touch to set the aim</p>
-    <canvas id=foo width=600 height=400 style="margin: 0 auto;"></canvas>
+    <canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
 </body>
 
 <script type="module">
@@ -28,8 +28,14 @@
         return await response.arrayBuffer();
     }
 
+    const canvasElement = document.querySelector("#foo");
+    const dpr = window.devicePixelRatio || 1;
+    canvasElement.width = canvasElement.clientWidth * dpr;
+    canvasElement.height = canvasElement.clientHeight * dpr;
+
     const ck = await CanvasKitInit();
     const surface = ck.MakeCanvasSurface('foo');
+    surface.getCanvas().scale(dpr, dpr);
 
     const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
     const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);
@@ -65,7 +71,6 @@
         const clientRect = canvasElement.getBoundingClientRect();
         let x = touchX - clientRect.left;
         let y = touchY - clientRect.top;
-        console.log(`${x}, ${y}`);
 
         // Transform the touch/mouse position to the crosshair
         // bone's parent bone coordinate system

+ 11 - 1
spine-ts/spine-canvaskit/example/index.html

@@ -17,7 +17,7 @@
 
 <body class="p-4 flex flex-col items-center">
     <h1>CanvasKit Example</h1>
-    <canvas id=foo width=600 height=400 style="margin: 0 auto;"></canvas>
+    <canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
 </body>
 
 <script type="module">
@@ -28,11 +28,20 @@
         return await response.arrayBuffer();
     }
 
+    // Ensure we render at full DPI.
+    const canvasElement = document.querySelector("#foo");
+    const dpr = window.devicePixelRatio || 1;
+    canvasElement.width = canvasElement.clientWidth * dpr;
+    canvasElement.height = canvasElement.clientHeight * dpr;
+
 
     // Initialize CanvasKit and create a surface from the Canvas element to draw to
     const ck = await CanvasKitInit();
     const surface = ck.MakeCanvasSurface('foo');
 
+    // Scale the CanvasKit coordinate system
+    surface.getCanvas().scale(dpr, dpr);
+
     // Load the texture atlas
     const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
 
@@ -54,6 +63,7 @@
     let lastTime = performance.now();
     // Rendering loop
     function drawFrame(canvas) {
+        // Clear the canvas
         canvas.clear(ck.Color(52, 52, 54, 1));
 
         // Calculate the time that's passed between now and the last frame

+ 8 - 2
spine-ts/spine-canvaskit/example/micro-benchmark.html

@@ -18,7 +18,7 @@
 <body class="p-4 flex flex-col items-center">
     <h1>Micro Benchmark</h1>
     <div id="timing"></div>
-    <canvas id=foo width=600 height=400 style="margin: 0 auto;"></canvas>
+    <canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
 </body>
 
 <script type="module">
@@ -28,13 +28,19 @@
         return await response.arrayBuffer();
     }
 
+    const canvasElement = document.querySelector("#foo");
+    const dpr = window.devicePixelRatio || 1;
+    canvasElement.width = canvasElement.clientWidth * dpr;
+    canvasElement.height = canvasElement.clientHeight * dpr;
+
     const ck = await CanvasKitInit();
     const surface = ck.MakeCanvasSurface('foo');
+    surface.getCanvas().scale(dpr, dpr);
 
     const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
     const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);
 
-    // Instantiate 100 skeletons, randomly placed and scaled.
+    // Instantiate 100 drawables, randomly placed and scaled.
     const drawables = [];
     for (let i = 0; i < 100; i++) {
         const drawable = new spine.SkeletonDrawable(skeletonData);

+ 7 - 1
spine-ts/spine-canvaskit/example/mix-and-match.html

@@ -17,7 +17,7 @@
 
 <body class="p-4 flex flex-col items-center">
     <h1>Skins Mix &amp; Match Example</h1>
-    <canvas id=foo width=600 height=400 style="margin: 0 auto;"></canvas>
+    <canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
 </body>
 
 <script type="module">
@@ -27,8 +27,14 @@
         return await response.arrayBuffer();
     }
 
+    const canvasElement = document.querySelector("#foo");
+    const dpr = window.devicePixelRatio || 1;
+    canvasElement.width = canvasElement.clientWidth * dpr;
+    canvasElement.height = canvasElement.clientHeight * dpr;
+
     const ck = await CanvasKitInit();
     const surface = ck.MakeCanvasSurface('foo');
+    surface.getCanvas().scale(dpr, dpr);
 
     const atlas = await spine.loadTextureAtlas(ck, "assets/mix-and-match.atlas", readFile);
     const skeletonData = await spine.loadSkeletonData("assets/mix-and-match-pro.skel", atlas, readFile);

+ 103 - 0
spine-ts/spine-canvaskit/example/physics.html

@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="../../index.css">
+    <script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
+    <script src="../dist/iife/spine-canvaskit.js"></script>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+        }
+    </style>
+</head>
+
+<body class="p-4 flex flex-col items-center">
+    <h1>IK Following</h1>
+    <p class="mb-4">Drag anywhere</p>
+    <canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
+</body>
+
+<script type="module">
+    async function readFile(path) {
+        const response = await fetch(path);
+        if (!response.ok) throw new Error("Could not load file " + path);
+        return await response.arrayBuffer();
+    }
+
+    const canvasElement = document.querySelector("#foo");
+    const dpr = window.devicePixelRatio || 1;
+    canvasElement.width = canvasElement.clientWidth * dpr;
+    canvasElement.height = canvasElement.clientHeight * dpr;
+
+    const ck = await CanvasKitInit();
+    const surface = ck.MakeCanvasSurface('foo');
+    surface.getCanvas().scale(dpr, dpr);
+
+    const atlas = await spine.loadTextureAtlas(ck, "assets/celestial-circus.atlas", readFile);
+    const skeletonData = await spine.loadSkeletonData("assets/celestial-circus-pro.json", atlas, readFile);
+    const drawable = new spine.SkeletonDrawable(skeletonData);
+    drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.15;
+    drawable.skeleton.x = 300;
+    drawable.skeleton.y = 300;
+
+    // Set the blink animation on track 0
+    drawable.animationState.setAnimation(0, "eyeblink-long", true);
+
+    // Set up touch and mouse listeners on the canvas element
+    // and set the touch/mouse coordinate on the aim bone
+    let mouseDown = false;
+    let lastX = -1, lastY = -1;
+    canvasElement.addEventListener("touchmove", (ev) => drag(ev.changedTouches[0].clientX, ev.changedTouches[0].clientY));
+    canvasElement.addEventListener("mousedown", (ev) => {
+        mouseDown = true;
+        drag(ev.clientX, ev.clientY);
+    });
+    canvasElement.addEventListener("mouseup", () => {
+        mouseDown = false;
+        lastX = -1; lastY = -1;
+    })
+    canvasElement.addEventListener("mousemove", (ev) => {
+        if (mouseDown) drag(ev.clientX, ev.clientY);
+    })
+
+    // Move the skeleton around based on the distance between
+    // the last touch/mouse location and the current touch/mouse location.
+    const drag = (touchX, touchY) => {
+        const clientRect = canvasElement.getBoundingClientRect();
+        let x = touchX - clientRect.left;
+        let y = touchY - clientRect.top;
+        if (lastX == -1 && lastY == -1) {
+            lastX = x;
+            lastY = y;
+            return;
+        }
+
+        drawable.skeleton.x += (x - lastX);
+        drawable.skeleton.y += (y - lastY);
+        lastX = x;
+        lastY = y;
+    }
+
+    const renderer = new spine.SkeletonRenderer(ck);
+    let lastTime = performance.now();
+
+    function drawFrame(canvas) {
+        canvas.clear(ck.Color(52, 52, 54, 1));
+
+        const now = performance.now();
+        const deltaTime = (now - lastTime) / 1000;
+        lastTime = now;
+
+        drawable.update(deltaTime);
+        renderer.render(canvas, drawable);
+
+        surface.requestAnimationFrame(drawFrame);
+    }
+    surface.requestAnimationFrame(drawFrame);
+</script>
+
+</html>

+ 6 - 2
spine-ts/spine-canvaskit/src/index.ts

@@ -12,7 +12,7 @@ function toCkBlendMode(ck: CanvasKit, blendMode: BlendMode) {
     switch(blendMode) {
         case BlendMode.Normal: return ck.BlendMode.SrcOver;
         case BlendMode.Additive: return ck.BlendMode.Plus;
-        case BlendMode.Multiply: return ck.BlendMode.Modulate;
+        case BlendMode.Multiply: return ck.BlendMode.SrcOver;
         case BlendMode.Screen: return ck.BlendMode.Screen;
         default: return ck.BlendMode.SrcOver;
     }
@@ -92,7 +92,11 @@ export async function loadTextureAtlas(ck: CanvasKit, atlasFile: string, readFil
 export async function loadSkeletonData(skeletonFile: string, atlas: TextureAtlas, readFile: (path: string) => Promise<Buffer>): Promise<SkeletonData> {
     const attachmentLoader = new AtlasAttachmentLoader(atlas);
     const loader = skeletonFile.endsWith(".json") ? new SkeletonJson(attachmentLoader) : new SkeletonBinary(attachmentLoader);
-    const skeletonData = loader.readSkeletonData(await readFile(skeletonFile));
+    let data = await readFile(skeletonFile);
+    if (skeletonFile.endsWith(".json")) {
+        data = bufferToUtf8String(data);
+    }
+    const skeletonData = loader.readSkeletonData(data);
     return skeletonData;
 }