awv3
Version:
⚡ AWV3 embedded CAD
349 lines (303 loc) • 10.5 kB
JavaScript
import * as THREE from 'three';
import { exponential } from './easing';
let tweens = [];
export default class Tween {
constructor(parent, properties) {
this.parent = parent;
this.internalUpdate = null;
this.isPlaying = false;
this.paused = false;
this.duration = 0;
this.repeatTimes = 0;
this.repeatMode = Tween.Repeat.Normal;
this.reversed = false;
this.delayTime = 0;
this.onStartCallbackFired = false;
this.easingFunction = exponential.out;
this.startTime = null;
this.onStartCallback = null;
this.onUpdateCallback = null;
this.onCompleteCallback = null;
this.thenCallback = null;
this.onStopCallback = null;
this.valuesEnd = flatten(parent, typeof properties === 'function' ? properties() : properties);
this.valuesStart = {};
this.valuesStartRepeat = {};
this.object = {};
this.map = {};
for (let key in this.valuesEnd) {
let prop = this.map[key] = returnValue(parent, key), value = prop[0][prop[1]];
this.object[key] = value;
this.valuesStart[key] = value;
this.valuesStartRepeat[key] = value;
}
}
hasProperty(key) {
return this.object.hasOwnProperty(key);
}
getProperties() {
return { ...this.valuesStart };
}
removeProperty(key) {
delete this.valuesStart[key];
delete this.valuesEnd[key];
delete this.object[key];
delete this.map[key];
if (Object.keys(this.object).length === 0) this.stop();
return this;
}
from(properties) {
if (!properties) return this;
properties = typeof properties === 'function' ? properties() : properties;
let flattened = flatten(this.parent, properties);
let previous = undefined;
for (let key in flattened) {
let prop = returnValue(properties, key);
if (prop[0]) previous = prop[0];
prop[0] = prop[0] || previous;
let value = prop[0][prop[1]];
let actualObject = this.map[key];
actualObject[0][actualObject[1]] = value;
this.object[key] = value;
this.valuesStart[key] = value;
}
return this;
}
start(length) {
this.duration = length || 0;
Tween.add(this);
this.isPlaying = true;
this.paused = false;
this.onStartCallbackFired = false;
this.startTime = null;
for (let item of Tween.getAll()) {
if (item !== this && item.isPlaying && item.parent == this.parent) {
for (let key in this.object) {
if (item.hasProperty(key)) item.removeProperty(key);
}
}
}
if (this.duration == 0) {
for (let property in this.valuesEnd) {
let end = this.valuesEnd[property], actualObject = this.map[property];
this.object[property] = end;
actualObject[0][actualObject[1]] = end;
}
}
this.parent.view && this.parent.view.invalidate();
return this;
}
now() {
return this.start(0);
}
stop() {
this.isPlaying = false;
this.paused = false;
if (this.onStopCallback !== null) this.onStopCallback.call(this.object);
if (this.thenCallback !== null) {
this.thenCallback.call(this.object);
this.thenCallback = null;
}
return this;
}
pause() {
this.paused = true;
return this;
}
continue() {
this.paused = false;
return this;
}
toggle() {
this.paused = !this.paused;
return this;
}
delay(amount) {
this.delayTime = amount;
return this;
}
repeat(times = 0, yoyo = Tween.Repeat.Normal) {
this.repeatTimes = times;
this.repeatMode = yoyo;
return this;
}
easing(easing) {
this.easingFunction = easing;
return this;
}
onStart(callback) {
this.onStartCallback = callback;
return this;
}
onUpdate(callback) {
this.onUpdateCallback = callback;
return this;
}
onComplete(callback) {
this.onCompleteCallback = callback;
return this;
}
wait() {
if (!this.isPlaying) return Promise.resolve();
else return new Promise(resolve => this.thenCallback = resolve);
}
onStop(callback) {
this.onStopCallback = callback;
return this;
}
update(time) {
if (this.paused) return true;
if (!this.isPlaying) return false;
if (this.startTime == null) {
this.startTime = window.performance.now();
this.startTime += this.delayTime;
}
let property;
if (time < this.startTime) return true;
if (this.onStartCallbackFired === false) {
if (this.onStartCallback !== null) this.onStartCallback.call(this.object);
this.onStartCallbackFired = true;
}
let elapsed = (time - this.startTime) / this.duration;
elapsed = elapsed > 1 ? 1 : elapsed;
let value = this.easingFunction(elapsed);
for (property in this.valuesEnd) {
let start = this.valuesStart[property],
end = this.valuesEnd[property],
fraction = start + (end - start) * value,
actualObject = this.map[property];
this.object[property] = fraction;
actualObject[0][actualObject[1]] = fraction;
}
if (this.onUpdateCallback !== null) this.onUpdateCallback.call(this.object, value, time);
if (elapsed == 1) {
if (this.repeatTimes > 0) {
if (isFinite(this.repeatTimes)) this.repeatTimes--;
for (property in this.valuesStartRepeat) {
if (typeof this.valuesEnd[property] === 'string')
this.valuesStartRepeat[property] = this.valuesStartRepeat[property] +
parseFloat(this.valuesEnd[property], 10);
if (this.repeatMode === Tween.Repeat.Yoyo) {
let tmp = this.valuesStartRepeat[property];
this.valuesStartRepeat[property] = this.valuesEnd[property];
this.valuesEnd[property] = tmp;
}
this.valuesStart[property] = this.valuesStartRepeat[property];
}
if (this.repeatMode === Tween.Repeat.Yoyo) this.reversed = !this.reversed;
this.startTime = time + this.delayTime;
return true;
} else {
if (this.onCompleteCallback !== null) this.onCompleteCallback.call(this.object);
if (this.thenCallback !== null) {
this.thenCallback.call(this.object);
this.thenCallback = null;
}
return false;
}
}
return true;
}
static getAll() {
return tweens;
}
static removeAll() {
tweens = [];
}
static add(tween) {
tweens.push(tween);
}
static remove(tween) {
let i = tweens.indexOf(tween);
if (i !== -1) tweens.splice(i, 1);
}
static removeObjectTweens(object) {
Tween.getAll().forEach(function(item) {
if (item.parent == object) {
item.stop();
}
});
}
static update(time, renderer) {
time = time !== undefined ? time : window.performance.now();
if (tweens.length === 0) return false;
let index = 0, length = tweens.length;
for (index; index < length; index++) {
let tween = tweens[index];
if (!tween.update(time)) {
tweens.splice((index--), 1);
length--;
} else if (!tween.paused && !!tween.parent.view) {
tween.parent.view.invalidate();
} else if (!tween.paused && renderer) {
renderer.invalidateViews();
}
}
return true;
}
}
Tween.Repeat = {
Normal: 1,
Yoyo: 2
};
function flatten(parent, source, pathArray, result) {
// Roll out merged arrays
if (Array.isArray(source) && source.length == 1 && typeof source[0] === 'object') {
source = mergeArray(parent, pathArray.join('.'), source[0], true);
}
pathArray = typeof pathArray === 'undefined' ? [] : pathArray;
result = typeof result === 'undefined' ? {} : result;
let key, value, newKey;
for (let i in source) {
if (source.hasOwnProperty(i)) {
key = i;
value = source[i];
pathArray.push(key);
if (typeof value === 'object' && value !== null) {
if (value instanceof THREE.Euler) {
result[key + '.x'] = value.x;
result[key + '.y'] = value.y;
result[key + '.z'] = value.z;
} else
result = flatten(parent, value, pathArray, result);
} else if (typeof value === 'number') {
newKey = pathArray.join('.');
result[newKey] = value;
}
pathArray.pop();
}
}
return result;
}
function ref(obj, str) {
return str.split('.').reduce(
function(o, x) {
return o[x];
},
obj
);
}
function returnValue(obj, key) {
let parts = key.split(/\.(?=[^.]+$)/);
if (parts.length == 1) return [obj, parts[0], parts.length];
else return [ref(obj, parts[0]), parts[1], parts.length];
}
function mergeArray(root, path, properties, justContent) {
let object = {}, result = [], map = returnValue(root, path);
map[0][map[1]].forEach(function(item) {
if (Array.isArray(properties)) {
let array = {};
properties.forEach(function(prop) {
let obj = {};
obj[prop] = item[prop];
Object.assign(array, obj);
});
result.push(array);
} else if (typeof properties === 'object') {
result.push(properties);
}
});
if (map[2] == 1 || !!justContent) object = result;
else object[map[1]] = result;
return object;
}