|
@@ -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>
|