Full refactor of animation control

This commit is contained in:
yushijinhun 2019-12-22 20:19:33 +08:00
parent 9f7b94a1a2
commit 127d2ee7a3
No known key found for this signature in database
GPG Key ID: 5BC167F73EA558E4
4 changed files with 86 additions and 29 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.speed = 0;
run.paused = true;
// Pause all animations!
skinViewer.animationSpeed = 0;
skinViewer.animations.paused = true;
</script>
```

View File

@ -36,8 +36,8 @@
// skinViewer.playerObject.skin.slim = true;
let control = new skinview3d.createOrbitControls(skinViewer);
skinViewer.animation = skinview3d.WalkingAnimation;
skinViewer.animationSpeed = 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;
@ -20,26 +21,50 @@ export function invokeAnimation(animation: Animation, player: PlayerObject, time
// This interface is used to control animations
export interface AnimationHandle {
speed: number;
paused: boolean;
progress: number;
readonly animation: Animation;
reset(): void;
remove(): void;
}
class AnimationWrapper implements AnimationHandle, IAnimation {
public speed: number = 1.0;
public readonly animation: Animation;
export interface SubAnimationHandle extends AnimationHandle {
remove(): void;
resetAndRemove(): void;
}
private lastTime = 0;
private progress = 0;
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) {
this.progress += (time - this.lastTime) * this.speed;
if (this.toResetAndRemove) {
invokeAnimation(this.animation, player, 0);
this.remove();
return;
}
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);
}
@ -50,6 +75,10 @@ class AnimationWrapper implements AnimationHandle, IAnimation {
remove() {
// stub get's overriden
}
resetAndRemove() {
this.toResetAndRemove = true;
}
}
export class CompositeAnimation implements IAnimation {
@ -58,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;
}
@ -68,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

@ -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;
public detectModel: boolean = true;
public animationSpeed: number = 1;
public animationTime: number = 0;
public disposed: boolean = false;
public readonly skinImg: HTMLImageElement;
@ -40,11 +38,8 @@ export class SkinViewer {
public readonly playerObject: PlayerObject;
private readonly clock: THREE.Clock;
constructor(options: SkinViewerOptions) {
this.domElement = options.domElement;
this.animation = options.animation || null;
if (options.detectModel === false) {
this.detectModel = false;
}
@ -116,14 +111,11 @@ export class SkinViewer {
if (options.width) this.width = options.width;
if (options.height) this.height = options.height;
this.clock = new THREE.Clock(true);
this.animations = new RootAnimation();
const draw = () => {
if (this.disposed) return;
window.requestAnimationFrame(draw);
this.animationTime += this.clock.getDelta() * this.animationSpeed;
if (this.animation) {
invokeAnimation(this.animation, this.playerObject, this.animationTime);
}
this.animations.runAnimationLoop(this.playerObject);
this.renderer.render(this.scene, this.camera);
};
draw();