Full refactor of animation control
This commit is contained in:
parent
9f7b94a1a2
commit
127d2ee7a3
14
README.md
14
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.speed = 0;
|
||||
run.paused = true;
|
||||
// Pause all animations!
|
||||
skinViewer.animationSpeed = 0;
|
||||
skinViewer.animations.paused = true;
|
||||
</script>
|
||||
```
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue