Merge branch 'animation-refactor'

This commit is contained in:
yushijinhun 2020-01-01 00:34:00 +08:00
commit db1884e782
No known key found for this signature in database
GPG Key ID: 5BC167F73EA558E4
5 changed files with 88 additions and 48 deletions

View File

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

View File

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

View File

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

View File

@ -20,8 +20,11 @@ export {
AnimationFn,
Animation,
invokeAnimation,
SubAnimationHandle,
AnimationHandle,
CompositeAnimation,
RootAnimation,
WalkingAnimation,
RunningAnimation,

View File

@ -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());
}