Merge branch 'animation-refactor'
This commit is contained in:
commit
db1884e782
12
README.md
12
README.md
|
@ -42,23 +42,23 @@ Three.js powered Minecraft skin viewer.
|
|||
control.enableZoom = false;
|
||||
control.enablePan = false;
|
||||
|
||||
skinViewer.animation = new skinview3d.CompositeAnimation();
|
||||
|
||||
// Add an animation
|
||||
let walk = skinViewer.animation.add(skinview3d.WalkingAnimation);
|
||||
let walk = skinViewer.animations.add(skinview3d.WalkingAnimation);
|
||||
// Add another animation
|
||||
let rotate = skinViewer.animation.add(skinview3d.RotatingAnimation);
|
||||
let rotate = skinViewer.animations.add(skinview3d.RotatingAnimation);
|
||||
// Remove an animation, stop walking dude
|
||||
walk.remove();
|
||||
// Remove the rotating animation, and make the player face forward
|
||||
rotate.resetAndRemove();
|
||||
// And run for now!
|
||||
let run = skinViewer.animation.add(skinview3d.RunningAnimation);
|
||||
let run = skinViewer.animations.add(skinview3d.RunningAnimation);
|
||||
|
||||
// Set the speed of an animation
|
||||
run.speed = 3;
|
||||
// Pause single animation
|
||||
run.paused = true;
|
||||
// Pause all animations!
|
||||
skinViewer.animationPaused = true;
|
||||
skinViewer.animations.paused = true;
|
||||
</script>
|
||||
```
|
||||
|
||||
|
|
|
@ -36,10 +36,8 @@
|
|||
// skinViewer.playerObject.skin.slim = true;
|
||||
|
||||
let control = new skinview3d.createOrbitControls(skinViewer);
|
||||
skinViewer.animation = new skinview3d.CompositeAnimation();
|
||||
|
||||
let walk = skinViewer.animation.add(skinview3d.WalkingAnimation);
|
||||
walk.speed = 1.5;
|
||||
skinViewer.animations.add(skinview3d.WalkingAnimation);
|
||||
skinViewer.animations.speed = 1.5;
|
||||
|
||||
</script>
|
||||
|
||||
|
|
101
src/animation.ts
101
src/animation.ts
|
@ -1,4 +1,5 @@
|
|||
import { PlayerObject } from "./model";
|
||||
import * as THREE from "three";
|
||||
|
||||
export interface IAnimation {
|
||||
play(player: PlayerObject, time: number): void;
|
||||
|
@ -19,55 +20,65 @@ export function invokeAnimation(animation: Animation, player: PlayerObject, time
|
|||
|
||||
// This interface is used to control animations
|
||||
export interface AnimationHandle {
|
||||
paused: boolean;
|
||||
speed: number;
|
||||
paused: boolean;
|
||||
progress: number;
|
||||
readonly animation: Animation;
|
||||
|
||||
reset(): void;
|
||||
remove(): void;
|
||||
}
|
||||
|
||||
class AnimationWrapper implements AnimationHandle, IAnimation {
|
||||
public paused = false;
|
||||
public speed: number = 1.0;
|
||||
public readonly animation: Animation;
|
||||
export interface SubAnimationHandle extends AnimationHandle {
|
||||
remove(): void;
|
||||
resetAndRemove(): void;
|
||||
}
|
||||
|
||||
private _paused = false;
|
||||
private _lastChange: number | null = null;
|
||||
private _speed: number = 1.0;
|
||||
private _lastChangeX: number | null = null;
|
||||
class AnimationWrapper implements SubAnimationHandle, IAnimation {
|
||||
speed: number = 1.0;
|
||||
paused: boolean = false;
|
||||
progress: number = 0;
|
||||
readonly animation: Animation;
|
||||
|
||||
private lastTime: number = 0;
|
||||
private started: boolean = false;
|
||||
private toResetAndRemove: boolean = false;
|
||||
|
||||
constructor(animation: Animation) {
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
play(player: PlayerObject, time: number) {
|
||||
if (this._lastChange === null) {
|
||||
this._lastChange = time;
|
||||
this._lastChangeX = 0;
|
||||
} else if (this.paused !== this._paused || this.speed !== this._speed) {
|
||||
const 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.toResetAndRemove) {
|
||||
invokeAnimation(this.animation, player, 0);
|
||||
this.remove();
|
||||
return;
|
||||
}
|
||||
if (this.paused === false) {
|
||||
const dt = time - this._lastChange;
|
||||
const x = this._lastChangeX! + this.speed * dt;
|
||||
invokeAnimation(this.animation, player, x);
|
||||
|
||||
let delta: number;
|
||||
if (this.started) {
|
||||
delta = time - this.lastTime;
|
||||
} else {
|
||||
delta = 0;
|
||||
this.started = true;
|
||||
}
|
||||
this.lastTime = time;
|
||||
if (!this.paused) {
|
||||
this.progress += delta * this.speed;
|
||||
}
|
||||
invokeAnimation(this.animation, player, this.progress);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._lastChange = null;
|
||||
this.progress = 0;
|
||||
}
|
||||
|
||||
remove() {
|
||||
// stub get's overriden
|
||||
}
|
||||
|
||||
resetAndRemove() {
|
||||
this.toResetAndRemove = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class CompositeAnimation implements IAnimation {
|
||||
|
@ -76,7 +87,9 @@ export class CompositeAnimation implements IAnimation {
|
|||
|
||||
add(animation: Animation): AnimationHandle {
|
||||
const handle = new AnimationWrapper(animation);
|
||||
handle.remove = () => this.handles.delete(handle);
|
||||
handle.remove = () => {
|
||||
this.handles.delete(handle);
|
||||
};
|
||||
this.handles.add(handle);
|
||||
return handle;
|
||||
}
|
||||
|
@ -86,6 +99,40 @@ export class CompositeAnimation implements IAnimation {
|
|||
}
|
||||
}
|
||||
|
||||
export class RootAnimation extends CompositeAnimation implements AnimationHandle {
|
||||
speed: number = 1.0;
|
||||
progress: number = 0.0;
|
||||
readonly clock: THREE.Clock = new THREE.Clock(true);
|
||||
|
||||
get animation() {
|
||||
return this;
|
||||
}
|
||||
|
||||
get paused() {
|
||||
return !this.clock.running;
|
||||
}
|
||||
|
||||
set paused(value) {
|
||||
if (value) {
|
||||
this.clock.stop();
|
||||
} else {
|
||||
this.clock.start();
|
||||
}
|
||||
}
|
||||
|
||||
runAnimationLoop(player: PlayerObject): void {
|
||||
if (this.handles.size === 0) {
|
||||
return;
|
||||
}
|
||||
this.progress += this.clock.getDelta() * this.speed;
|
||||
this.play(player, this.progress);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.progress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const WalkingAnimation: Animation = (player, time) => {
|
||||
const skin = player.skin;
|
||||
|
||||
|
|
|
@ -20,8 +20,11 @@ export {
|
|||
AnimationFn,
|
||||
Animation,
|
||||
invokeAnimation,
|
||||
|
||||
SubAnimationHandle,
|
||||
AnimationHandle,
|
||||
CompositeAnimation,
|
||||
RootAnimation,
|
||||
|
||||
WalkingAnimation,
|
||||
RunningAnimation,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as THREE from "three";
|
||||
import { PlayerObject } from "./model";
|
||||
import { invokeAnimation } from "./animation";
|
||||
import { RootAnimation } from "./animation";
|
||||
import { loadSkinToCanvas, loadCapeToCanvas, isSlimSkin } from "./utils";
|
||||
|
||||
export interface SkinViewerOptions {
|
||||
|
@ -16,10 +16,8 @@ export interface SkinViewerOptions {
|
|||
export class SkinViewer {
|
||||
|
||||
public readonly domElement: Node;
|
||||
public animation: Animation | null;
|
||||
public readonly animations: RootAnimation = new RootAnimation();
|
||||
public detectModel: boolean = true;
|
||||
public animationPaused: boolean = false;
|
||||
public animationTime: number = 0;
|
||||
public disposed: boolean = false;
|
||||
|
||||
public readonly skinImg: HTMLImageElement;
|
||||
|
@ -44,7 +42,6 @@ export class SkinViewer {
|
|||
|
||||
constructor(options: SkinViewerOptions) {
|
||||
this.domElement = options.domElement;
|
||||
this.animation = options.animation || null;
|
||||
if (options.detectModel === false) {
|
||||
this.detectModel = false;
|
||||
}
|
||||
|
@ -123,12 +120,7 @@ export class SkinViewer {
|
|||
if (this.disposed || this._renderPaused) {
|
||||
return;
|
||||
}
|
||||
if (!this.animationPaused) {
|
||||
this.animationTime++;
|
||||
if (this.animation) {
|
||||
invokeAnimation(this.animation, this.playerObject, this.animationTime / 100.0);
|
||||
}
|
||||
}
|
||||
this.animations.runAnimationLoop(this.playerObject);
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
window.requestAnimationFrame(() => this.draw());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue