diff --git a/README.md b/README.md index 67d672d..1a72bbe 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,7 @@ Three.js powered Minecraft skin viewer. width: 600, height: 600, skinUrl: "img/skin.png", - capeUrl: "img/cape.png", - animation: skinview3d.WalkAnimation + capeUrl: "img/cape.png" }); // change the skin and cape @@ -38,14 +37,17 @@ Three.js powered Minecraft skin viewer. // enable the mouse control feature let control = new skinview3d.SkinControl(skinViewer); - // pause the animation - // skinViewer.animationPaused = true; - - // change the animation speed - // skinViewer.animationSpeed = 8; - // disable the 'right click to play/pause' feature // control.enableAnimationControl = false; + + skinViewer.animation = new skinview3d.CompositeAnimation(); + + // add an animation + let walk = skinViewer.animation.add(skinview3d.WalkAnimation); + + // set its speed and some others + walk.speed = 1.5; + // walk.paused = true; ``` diff --git a/src/animation.js b/src/animation.js index 646f906..7f53b04 100644 --- a/src/animation.js +++ b/src/animation.js @@ -1,3 +1,60 @@ +function invokeAnimation(animation, player, time) { + if (animation instanceof CompositeAnimation) { + animation.play(player, time); + } else if (animation instanceof Function) { + animation(player, time); + } else { + throw `Not an animation: ${animation}`; + } +} + +class AnimationHandle { + constructor(animation) { + this.animation = animation; + this.paused = this._paused = false; + this.speed = this._speed = 1.0; + this._lastChange = null; + this._lastChangeX = null; + } + play(player, time) { + if (this._lastChange === null) { + this._lastChange = time; + this._lastChangeX = 0; + } else if (this.paused !== this._paused || this.speed !== this._speed) { + let dt = time - this._lastChange; + if (this._paused === false) { + this._lastChangeX += dt * this._speed; + } + this._paused = this.paused; + this._speed = this.speed; + this._lastChange = time; + } + if (this.paused === false) { + let dt = time - this._lastChange; + let x = this._lastChangeX + this.speed * dt; + invokeAnimation(this.animation, player, x); + } + } + reset(){ + this._lastChange = null; + } +} + +class CompositeAnimation { + constructor() { + this.handles = new Set(); + } + add(animation) { + let handle = new AnimationHandle(animation); + handle.remove = () => this.handles.delete(handle); + this.handles.add(handle); + return handle; + } + play(player, time) { + this.handles.forEach(handle => handle.play(player, time)); + } +} + let WalkAnimation = (player, time) => { let skin = player.skin; let angleRot = time + Math.PI / 2; @@ -11,4 +68,4 @@ let WalkAnimation = (player, time) => { skin.rightArm.rotation.x = Math.cos(angleRot); }; -export { WalkAnimation }; +export { CompositeAnimation, WalkAnimation, invokeAnimation }; diff --git a/src/skinview3d.js b/src/skinview3d.js index c986907..cffcaab 100644 --- a/src/skinview3d.js +++ b/src/skinview3d.js @@ -25,4 +25,4 @@ export { SkinObject, CapeObject, PlayerObject } from "./model"; export { SkinViewer, SkinControl } from "./viewer"; -export { WalkAnimation } from "./animation"; +export { CompositeAnimation, WalkAnimation } from "./animation"; diff --git a/src/viewer.js b/src/viewer.js index cef1ffd..1af5839 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1,6 +1,7 @@ import THREE from "three"; import { PlayerObject } from "./model"; import { OrbitControls } from "./orbit_controls"; +import { invokeAnimation } from "./animation"; function copyImage(context, sX, sY, w, h, dX, dY, flipHorizontal) { let imgData = context.getImageData(sX, sY, w, h); @@ -160,7 +161,7 @@ class SkinViewer { if (!this.animationPaused) { this.animationTime++; if (this.animation) { - this.animation(this.playerObject, this.animationTime / 100 * this.animationSpeed); + invokeAnimation(this.animation, this.playerObject, this.animationTime / 100.0 * this.animationSpeed); } } this.renderer.render(this.scene, this.camera);