From f47038b58fba1954a77b7b8eab1b4d88c3aa6372 Mon Sep 17 00:00:00 2001 From: Hugo Dzin Date: Mon, 12 Feb 2024 10:11:18 +0100 Subject: [PATCH 1/4] fix: when yoyo repeats, first frame behaves as if yoyo=false --- dist/tween.amd.js | 101 ++++++++++++++++++++++----------------- dist/tween.cjs | 101 ++++++++++++++++++++++----------------- dist/tween.esm.js | 101 ++++++++++++++++++++++----------------- dist/tween.umd.js | 101 ++++++++++++++++++++++----------------- src/Tween.ts | 103 +++++++++++++++++++++++----------------- src/tests.ts | 25 ++++++++++ test/unit/nodeunit.html | 7 +++ 7 files changed, 324 insertions(+), 215 deletions(-) diff --git a/dist/tween.amd.js b/dist/tween.amd.js index 4b43ab07..cc120afa 100644 --- a/dist/tween.amd.js +++ b/dist/tween.amd.js @@ -727,56 +727,71 @@ define(['exports'], (function (exports) { 'use strict'; } return portion; }; - var elapsed = calculateElapsedPortion(); - var value = this._easingFunction(elapsed); - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed); - } - if (this._duration === 0 || elapsedTime >= this._duration) { - if (this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); - if (isFinite(this._repeat)) { - this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]); + var checkStillPlaying = function () { + if (_this._duration === 0 || elapsedTime >= _this._duration) { + if (_this._repeat > 0) { + var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); + if (isFinite(_this._repeat)) { + _this._repeat -= completeCount; } - if (this._yoyo) { - this._swapEndStartRepeatValues(property); + // Reassign starting values, restart by making startTime = now + for (property in _this._valuesStartRepeat) { + if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { + _this._valuesStartRepeat[property] = + // eslint-disable-next-line + // @ts-ignore FIXME? + _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); + } + if (_this._yoyo) { + _this._swapEndStartRepeatValues(property); + } + _this._valuesStart[property] = _this._valuesStartRepeat[property]; } - this._valuesStart[property] = this._valuesStartRepeat[property]; - } - if (this._yoyo) { - this._reversed = !this._reversed; + if (_this._yoyo) { + _this._reversed = !_this._reversed; + } + _this._startTime += durationAndDelay * completeCount; + if (_this._onRepeatCallback) { + _this._onRepeatCallback(_this._object); + } + _this._onEveryStartCallbackFired = false; + return true; } - this._startTime += durationAndDelay * completeCount; - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object); + else { + if (_this._onCompleteCallback) { + _this._onCompleteCallback(_this._object); + } + for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + _this._chainedTweens[i].start(_this._startTime + _this._duration, false); + } + _this._isPlaying = false; + return false; } - this._onEveryStartCallbackFired = false; - return true; } - else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object); - } - for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false); - } - this._isPlaying = false; - return false; + return true; + }; + var doUpdates = function () { + var elapsed = calculateElapsedPortion(); + var value = _this._easingFunction(elapsed); + // properties transformations + _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); + if (_this._onUpdateCallback) { + _this._onUpdateCallback(_this._object, elapsed); } + }; + var stillPlaying; + // if (true) { + if (elapsedTime <= this._duration) { + doUpdates(); + stillPlaying = checkStillPlaying(); } - return true; + else { + stillPlaying = checkStillPlaying(); + doUpdates(); + } + return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/dist/tween.cjs b/dist/tween.cjs index 1470fcbc..8cbf0377 100644 --- a/dist/tween.cjs +++ b/dist/tween.cjs @@ -729,56 +729,71 @@ var Tween = /** @class */ (function () { } return portion; }; - var elapsed = calculateElapsedPortion(); - var value = this._easingFunction(elapsed); - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed); - } - if (this._duration === 0 || elapsedTime >= this._duration) { - if (this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); - if (isFinite(this._repeat)) { - this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]); + var checkStillPlaying = function () { + if (_this._duration === 0 || elapsedTime >= _this._duration) { + if (_this._repeat > 0) { + var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); + if (isFinite(_this._repeat)) { + _this._repeat -= completeCount; } - if (this._yoyo) { - this._swapEndStartRepeatValues(property); + // Reassign starting values, restart by making startTime = now + for (property in _this._valuesStartRepeat) { + if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { + _this._valuesStartRepeat[property] = + // eslint-disable-next-line + // @ts-ignore FIXME? + _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); + } + if (_this._yoyo) { + _this._swapEndStartRepeatValues(property); + } + _this._valuesStart[property] = _this._valuesStartRepeat[property]; } - this._valuesStart[property] = this._valuesStartRepeat[property]; - } - if (this._yoyo) { - this._reversed = !this._reversed; + if (_this._yoyo) { + _this._reversed = !_this._reversed; + } + _this._startTime += durationAndDelay * completeCount; + if (_this._onRepeatCallback) { + _this._onRepeatCallback(_this._object); + } + _this._onEveryStartCallbackFired = false; + return true; } - this._startTime += durationAndDelay * completeCount; - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object); + else { + if (_this._onCompleteCallback) { + _this._onCompleteCallback(_this._object); + } + for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + _this._chainedTweens[i].start(_this._startTime + _this._duration, false); + } + _this._isPlaying = false; + return false; } - this._onEveryStartCallbackFired = false; - return true; } - else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object); - } - for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false); - } - this._isPlaying = false; - return false; + return true; + }; + var doUpdates = function () { + var elapsed = calculateElapsedPortion(); + var value = _this._easingFunction(elapsed); + // properties transformations + _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); + if (_this._onUpdateCallback) { + _this._onUpdateCallback(_this._object, elapsed); } + }; + var stillPlaying; + // if (true) { + if (elapsedTime <= this._duration) { + doUpdates(); + stillPlaying = checkStillPlaying(); } - return true; + else { + stillPlaying = checkStillPlaying(); + doUpdates(); + } + return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/dist/tween.esm.js b/dist/tween.esm.js index ba359a49..b7457786 100644 --- a/dist/tween.esm.js +++ b/dist/tween.esm.js @@ -725,56 +725,71 @@ var Tween = /** @class */ (function () { } return portion; }; - var elapsed = calculateElapsedPortion(); - var value = this._easingFunction(elapsed); - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed); - } - if (this._duration === 0 || elapsedTime >= this._duration) { - if (this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); - if (isFinite(this._repeat)) { - this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]); + var checkStillPlaying = function () { + if (_this._duration === 0 || elapsedTime >= _this._duration) { + if (_this._repeat > 0) { + var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); + if (isFinite(_this._repeat)) { + _this._repeat -= completeCount; } - if (this._yoyo) { - this._swapEndStartRepeatValues(property); + // Reassign starting values, restart by making startTime = now + for (property in _this._valuesStartRepeat) { + if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { + _this._valuesStartRepeat[property] = + // eslint-disable-next-line + // @ts-ignore FIXME? + _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); + } + if (_this._yoyo) { + _this._swapEndStartRepeatValues(property); + } + _this._valuesStart[property] = _this._valuesStartRepeat[property]; } - this._valuesStart[property] = this._valuesStartRepeat[property]; - } - if (this._yoyo) { - this._reversed = !this._reversed; + if (_this._yoyo) { + _this._reversed = !_this._reversed; + } + _this._startTime += durationAndDelay * completeCount; + if (_this._onRepeatCallback) { + _this._onRepeatCallback(_this._object); + } + _this._onEveryStartCallbackFired = false; + return true; } - this._startTime += durationAndDelay * completeCount; - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object); + else { + if (_this._onCompleteCallback) { + _this._onCompleteCallback(_this._object); + } + for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + _this._chainedTweens[i].start(_this._startTime + _this._duration, false); + } + _this._isPlaying = false; + return false; } - this._onEveryStartCallbackFired = false; - return true; } - else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object); - } - for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false); - } - this._isPlaying = false; - return false; + return true; + }; + var doUpdates = function () { + var elapsed = calculateElapsedPortion(); + var value = _this._easingFunction(elapsed); + // properties transformations + _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); + if (_this._onUpdateCallback) { + _this._onUpdateCallback(_this._object, elapsed); } + }; + var stillPlaying; + // if (true) { + if (elapsedTime <= this._duration) { + doUpdates(); + stillPlaying = checkStillPlaying(); } - return true; + else { + stillPlaying = checkStillPlaying(); + doUpdates(); + } + return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/dist/tween.umd.js b/dist/tween.umd.js index 82c7babb..65671ca7 100644 --- a/dist/tween.umd.js +++ b/dist/tween.umd.js @@ -731,56 +731,71 @@ } return portion; }; - var elapsed = calculateElapsedPortion(); - var value = this._easingFunction(elapsed); - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed); - } - if (this._duration === 0 || elapsedTime >= this._duration) { - if (this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); - if (isFinite(this._repeat)) { - this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]); + var checkStillPlaying = function () { + if (_this._duration === 0 || elapsedTime >= _this._duration) { + if (_this._repeat > 0) { + var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); + if (isFinite(_this._repeat)) { + _this._repeat -= completeCount; } - if (this._yoyo) { - this._swapEndStartRepeatValues(property); + // Reassign starting values, restart by making startTime = now + for (property in _this._valuesStartRepeat) { + if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { + _this._valuesStartRepeat[property] = + // eslint-disable-next-line + // @ts-ignore FIXME? + _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); + } + if (_this._yoyo) { + _this._swapEndStartRepeatValues(property); + } + _this._valuesStart[property] = _this._valuesStartRepeat[property]; } - this._valuesStart[property] = this._valuesStartRepeat[property]; - } - if (this._yoyo) { - this._reversed = !this._reversed; + if (_this._yoyo) { + _this._reversed = !_this._reversed; + } + _this._startTime += durationAndDelay * completeCount; + if (_this._onRepeatCallback) { + _this._onRepeatCallback(_this._object); + } + _this._onEveryStartCallbackFired = false; + return true; } - this._startTime += durationAndDelay * completeCount; - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object); + else { + if (_this._onCompleteCallback) { + _this._onCompleteCallback(_this._object); + } + for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + _this._chainedTweens[i].start(_this._startTime + _this._duration, false); + } + _this._isPlaying = false; + return false; } - this._onEveryStartCallbackFired = false; - return true; } - else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object); - } - for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false); - } - this._isPlaying = false; - return false; + return true; + }; + var doUpdates = function () { + var elapsed = calculateElapsedPortion(); + var value = _this._easingFunction(elapsed); + // properties transformations + _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); + if (_this._onUpdateCallback) { + _this._onUpdateCallback(_this._object, elapsed); } + }; + var stillPlaying; + // if (true) { + if (elapsedTime <= this._duration) { + doUpdates(); + stillPlaying = checkStillPlaying(); } - return true; + else { + stillPlaying = checkStillPlaying(); + doUpdates(); + } + return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/src/Tween.ts b/src/Tween.ts index 2cc49bb0..b25c4d89 100644 --- a/src/Tween.ts +++ b/src/Tween.ts @@ -452,70 +452,87 @@ export class Tween { } return portion } - const elapsed = calculateElapsedPortion() - const value = this._easingFunction(elapsed) - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value) + const checkStillPlaying = () => { + if (this._duration === 0 || elapsedTime >= this._duration) { + if (this._repeat > 0) { + const completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat) + if (isFinite(this._repeat)) { + this._repeat -= completeCount + } - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed) - } + // Reassign starting values, restart by making startTime = now + for (property in this._valuesStartRepeat) { + if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { + this._valuesStartRepeat[property] = + // eslint-disable-next-line + // @ts-ignore FIXME? + this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]) + } - if (this._duration === 0 || elapsedTime >= this._duration) { - if (this._repeat > 0) { - const completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat) - if (isFinite(this._repeat)) { - this._repeat -= completeCount - } + if (this._yoyo) { + this._swapEndStartRepeatValues(property) + } - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]) + this._valuesStart[property] = this._valuesStartRepeat[property] } if (this._yoyo) { - this._swapEndStartRepeatValues(property) + this._reversed = !this._reversed } - this._valuesStart[property] = this._valuesStartRepeat[property] - } + this._startTime += durationAndDelay * completeCount - if (this._yoyo) { - this._reversed = !this._reversed - } + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object) + } - this._startTime += durationAndDelay * completeCount + this._onEveryStartCallbackFired = false - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object) - } + return true + } else { + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object) + } + + for (let i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false) + } - this._onEveryStartCallbackFired = false + this._isPlaying = false - return true - } else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object) + return false } + } - for (let i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false) - } + return true + } + + const doUpdates = () => { + const elapsed = calculateElapsedPortion() + const value = this._easingFunction(elapsed) - this._isPlaying = false + // properties transformations + this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value) - return false + if (this._onUpdateCallback) { + this._onUpdateCallback(this._object, elapsed) } + }; + + let stillPlaying; + + if (elapsedTime <= this._duration) { + doUpdates(); + stillPlaying = checkStillPlaying(); + } else { + stillPlaying = checkStillPlaying(); + doUpdates(); } - return true + return stillPlaying; } private _updateProperties( diff --git a/src/tests.ts b/src/tests.ts index 6fbcd744..485cd510 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1409,6 +1409,31 @@ export const tests = { test.done() }, + 'Test yoyo reverses at right instant'(test: Test): void { + TWEEN.removeAll(); + + const obj = { x: 0 }; + new TWEEN.Tween(obj).to({ x: 100 }, 100).repeat(1).yoyo(true).start(0); + + TWEEN.update(98); + test.equal(obj.x, 98); + + TWEEN.update(99); + test.equal(obj.x, 99); + + // Previously this would fail, the first update after 100 would happen as if yoyo=false + TWEEN.update(101); + test.equal(obj.x, 99); + + TWEEN.update(101); + test.equal(obj.x, 99); + + TWEEN.update(102); + test.equal(obj.x, 98); + + test.done(); + }, + 'Test TWEEN.Tween.stopChainedTweens()'(test: Test): void { const t = new TWEEN.Tween({}), t2 = new TWEEN.Tween({}) diff --git a/test/unit/nodeunit.html b/test/unit/nodeunit.html index 69c7595b..130bf914 100644 --- a/test/unit/nodeunit.html +++ b/test/unit/nodeunit.html @@ -7,6 +7,13 @@ + + From 933982ff0129ce6cbf42b274d445531fbed09653 Mon Sep 17 00:00:00 2001 From: Hugo Dzin Date: Mon, 12 Feb 2024 11:07:19 +0100 Subject: [PATCH 2/4] fix: ensure callbacks are called in right order --- dist/tween.amd.js | 42 ++++++++++++++++++++++---------------- dist/tween.cjs | 42 ++++++++++++++++++++++---------------- dist/tween.esm.js | 42 ++++++++++++++++++++++---------------- dist/tween.umd.js | 42 ++++++++++++++++++++++---------------- src/Tween.ts | 52 ++++++++++++++++++++++++++++------------------- src/tests.ts | 38 ++++++++++++++++++++++++++++++++++ 6 files changed, 169 insertions(+), 89 deletions(-) diff --git a/dist/tween.amd.js b/dist/tween.amd.js index cc120afa..04eb269f 100644 --- a/dist/tween.amd.js +++ b/dist/tween.amd.js @@ -727,7 +727,9 @@ define(['exports'], (function (exports) { 'use strict'; } return portion; }; - var checkStillPlaying = function () { + var repeated = false; + var completed = false; + var checkStillPlayingAndReverse = function () { if (_this._duration === 0 || elapsedTime >= _this._duration) { if (_this._repeat > 0) { var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); @@ -751,22 +753,11 @@ define(['exports'], (function (exports) { 'use strict'; _this._reversed = !_this._reversed; } _this._startTime += durationAndDelay * completeCount; - if (_this._onRepeatCallback) { - _this._onRepeatCallback(_this._object); - } - _this._onEveryStartCallbackFired = false; + repeated = true; return true; } else { - if (_this._onCompleteCallback) { - _this._onCompleteCallback(_this._object); - } - for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - _this._chainedTweens[i].start(_this._startTime + _this._duration, false); - } - _this._isPlaying = false; + completed = true; return false; } } @@ -782,15 +773,32 @@ define(['exports'], (function (exports) { 'use strict'; } }; var stillPlaying; - // if (true) { if (elapsedTime <= this._duration) { doUpdates(); - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); } else { - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); doUpdates(); } + if (repeated) { + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object); + } + this._onEveryStartCallbackFired = false; + } + if (completed) { + completed = true; + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object); + } + for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false); + } + this._isPlaying = false; + } return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { diff --git a/dist/tween.cjs b/dist/tween.cjs index 8cbf0377..2ddff5e0 100644 --- a/dist/tween.cjs +++ b/dist/tween.cjs @@ -729,7 +729,9 @@ var Tween = /** @class */ (function () { } return portion; }; - var checkStillPlaying = function () { + var repeated = false; + var completed = false; + var checkStillPlayingAndReverse = function () { if (_this._duration === 0 || elapsedTime >= _this._duration) { if (_this._repeat > 0) { var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); @@ -753,22 +755,11 @@ var Tween = /** @class */ (function () { _this._reversed = !_this._reversed; } _this._startTime += durationAndDelay * completeCount; - if (_this._onRepeatCallback) { - _this._onRepeatCallback(_this._object); - } - _this._onEveryStartCallbackFired = false; + repeated = true; return true; } else { - if (_this._onCompleteCallback) { - _this._onCompleteCallback(_this._object); - } - for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - _this._chainedTweens[i].start(_this._startTime + _this._duration, false); - } - _this._isPlaying = false; + completed = true; return false; } } @@ -784,15 +775,32 @@ var Tween = /** @class */ (function () { } }; var stillPlaying; - // if (true) { if (elapsedTime <= this._duration) { doUpdates(); - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); } else { - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); doUpdates(); } + if (repeated) { + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object); + } + this._onEveryStartCallbackFired = false; + } + if (completed) { + completed = true; + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object); + } + for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false); + } + this._isPlaying = false; + } return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { diff --git a/dist/tween.esm.js b/dist/tween.esm.js index b7457786..e65c839d 100644 --- a/dist/tween.esm.js +++ b/dist/tween.esm.js @@ -725,7 +725,9 @@ var Tween = /** @class */ (function () { } return portion; }; - var checkStillPlaying = function () { + var repeated = false; + var completed = false; + var checkStillPlayingAndReverse = function () { if (_this._duration === 0 || elapsedTime >= _this._duration) { if (_this._repeat > 0) { var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); @@ -749,22 +751,11 @@ var Tween = /** @class */ (function () { _this._reversed = !_this._reversed; } _this._startTime += durationAndDelay * completeCount; - if (_this._onRepeatCallback) { - _this._onRepeatCallback(_this._object); - } - _this._onEveryStartCallbackFired = false; + repeated = true; return true; } else { - if (_this._onCompleteCallback) { - _this._onCompleteCallback(_this._object); - } - for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - _this._chainedTweens[i].start(_this._startTime + _this._duration, false); - } - _this._isPlaying = false; + completed = true; return false; } } @@ -780,15 +771,32 @@ var Tween = /** @class */ (function () { } }; var stillPlaying; - // if (true) { if (elapsedTime <= this._duration) { doUpdates(); - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); } else { - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); doUpdates(); } + if (repeated) { + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object); + } + this._onEveryStartCallbackFired = false; + } + if (completed) { + completed = true; + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object); + } + for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false); + } + this._isPlaying = false; + } return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { diff --git a/dist/tween.umd.js b/dist/tween.umd.js index 65671ca7..4bbf99fc 100644 --- a/dist/tween.umd.js +++ b/dist/tween.umd.js @@ -731,7 +731,9 @@ } return portion; }; - var checkStillPlaying = function () { + var repeated = false; + var completed = false; + var checkStillPlayingAndReverse = function () { if (_this._duration === 0 || elapsedTime >= _this._duration) { if (_this._repeat > 0) { var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); @@ -755,22 +757,11 @@ _this._reversed = !_this._reversed; } _this._startTime += durationAndDelay * completeCount; - if (_this._onRepeatCallback) { - _this._onRepeatCallback(_this._object); - } - _this._onEveryStartCallbackFired = false; + repeated = true; return true; } else { - if (_this._onCompleteCallback) { - _this._onCompleteCallback(_this._object); - } - for (var i = 0, numChainedTweens = _this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - _this._chainedTweens[i].start(_this._startTime + _this._duration, false); - } - _this._isPlaying = false; + completed = true; return false; } } @@ -786,15 +777,32 @@ } }; var stillPlaying; - // if (true) { if (elapsedTime <= this._duration) { doUpdates(); - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); } else { - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); doUpdates(); } + if (repeated) { + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object); + } + this._onEveryStartCallbackFired = false; + } + if (completed) { + completed = true; + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object); + } + for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false); + } + this._isPlaying = false; + } return stillPlaying; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { diff --git a/src/Tween.ts b/src/Tween.ts index b25c4d89..f094e82c 100644 --- a/src/Tween.ts +++ b/src/Tween.ts @@ -453,7 +453,10 @@ export class Tween { return portion } - const checkStillPlaying = () => { + let repeated = false; + let completed = false; + + const checkStillPlayingAndReverse = () => { if (this._duration === 0 || elapsedTime >= this._duration) { if (this._repeat > 0) { const completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat) @@ -482,27 +485,11 @@ export class Tween { } this._startTime += durationAndDelay * completeCount - - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object) - } - - this._onEveryStartCallbackFired = false + repeated = true; return true } else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object) - } - - for (let i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false) - } - - this._isPlaying = false - + completed = true; return false } } @@ -526,12 +513,35 @@ export class Tween { if (elapsedTime <= this._duration) { doUpdates(); - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); } else { - stillPlaying = checkStillPlaying(); + stillPlaying = checkStillPlayingAndReverse(); doUpdates(); } + if (repeated) { + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object) + } + + this._onEveryStartCallbackFired = false + } + + if (completed) { + completed = true; + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object) + } + + for (let i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false) + } + + this._isPlaying = false + } + return stillPlaying; } diff --git a/src/tests.ts b/src/tests.ts index 485cd510..f96e3dca 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1434,6 +1434,44 @@ export const tests = { test.done(); }, + 'Test yoyo callbacks happen on right order'(test: Test): void { + TWEEN.removeAll(); + + let events: string[] = []; + const obj = { x: 0 } + + new TWEEN.Tween(obj) + .to({ x: 100 }, 100) + .repeat(1) + .yoyo(true) + .easing(TWEEN.Easing.Linear.None) + .onUpdate(() => events.push('update')) + .onStart(() => events.push('start')) + .onEveryStart(() => events.push('everystart')) + .onRepeat(() => events.push('repeat')) + .onComplete(() => events.push('complete')) + .start(0); + + function testAndReset(expected: string[]) { + test.deepEqual(events, expected); + events = []; + } + + testAndReset([]); + TWEEN.update(99); + testAndReset(['start', 'everystart', 'update']); + TWEEN.update(101); + testAndReset(['update', 'repeat']); + TWEEN.update(150); + testAndReset(['everystart', 'update']); + TWEEN.update(199); + testAndReset(['update']); + TWEEN.update(201); + testAndReset(['update', 'complete']); + + test.done(); + }, + 'Test TWEEN.Tween.stopChainedTweens()'(test: Test): void { const t = new TWEEN.Tween({}), t2 = new TWEEN.Tween({}) From 78120c5a4b5f4716e828206e8995672abe89f1f8 Mon Sep 17 00:00:00 2001 From: Hugo Dzin Date: Fri, 19 Apr 2024 13:46:59 +0200 Subject: [PATCH 3/4] clean up a little bit --- dist/tween.amd.js | 138 +++++++++++++++++++---------------------- dist/tween.cjs | 138 +++++++++++++++++++---------------------- dist/tween.d.ts | 3 + dist/tween.esm.js | 138 +++++++++++++++++++---------------------- dist/tween.umd.js | 138 +++++++++++++++++++---------------------- src/Tween.ts | 152 ++++++++++++++++++++++------------------------ src/tests.ts | 49 +++++++++++++++ 7 files changed, 375 insertions(+), 381 deletions(-) diff --git a/dist/tween.amd.js b/dist/tween.amd.js index 04eb269f..7697fba2 100644 --- a/dist/tween.amd.js +++ b/dist/tween.amd.js @@ -678,13 +678,11 @@ define(['exports'], (function (exports) { 'use strict'; * it is still playing, just paused). */ Tween.prototype.update = function (time, autoStart) { - var _this = this; var _a; if (time === void 0) { time = now(); } if (autoStart === void 0) { autoStart = true; } if (this._isPaused) return true; - var property; var endTime = this._startTime + this._duration; if (!this._goToEnd && !this._isPlaying) { if (time > endTime) @@ -711,84 +709,31 @@ define(['exports'], (function (exports) { 'use strict'; var elapsedTime = time - this._startTime; var durationAndDelay = this._duration + ((_a = this._repeatDelayTime) !== null && _a !== void 0 ? _a : this._delayTime); var totalTime = this._duration + this._repeat * durationAndDelay; - var calculateElapsedPortion = function () { - if (_this._duration === 0) - return 1; - if (elapsedTime > totalTime) { - return 1; - } - var timesRepeated = Math.trunc(elapsedTime / durationAndDelay); - var timeIntoCurrentRepeat = elapsedTime - timesRepeated * durationAndDelay; - // TODO use %? - // const timeIntoCurrentRepeat = elapsedTime % durationAndDelay - var portion = Math.min(timeIntoCurrentRepeat / _this._duration, 1); - if (portion === 0 && elapsedTime === _this._duration) { - return 1; - } - return portion; - }; - var repeated = false; - var completed = false; - var checkStillPlayingAndReverse = function () { - if (_this._duration === 0 || elapsedTime >= _this._duration) { - if (_this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); - if (isFinite(_this._repeat)) { - _this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in _this._valuesStartRepeat) { - if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { - _this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); - } - if (_this._yoyo) { - _this._swapEndStartRepeatValues(property); - } - _this._valuesStart[property] = _this._valuesStartRepeat[property]; - } - if (_this._yoyo) { - _this._reversed = !_this._reversed; - } - _this._startTime += durationAndDelay * completeCount; - repeated = true; - return true; - } - else { - completed = true; - return false; - } - } - return true; - }; - var doUpdates = function () { - var elapsed = calculateElapsedPortion(); - var value = _this._easingFunction(elapsed); - // properties transformations - _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); - if (_this._onUpdateCallback) { - _this._onUpdateCallback(_this._object, elapsed); - } - }; - var stillPlaying; - if (elapsedTime <= this._duration) { - doUpdates(); - stillPlaying = checkStillPlayingAndReverse(); + var elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime); + var value = this._easingFunction(elapsed); + var status = this._calculateCompletionStatus(elapsedTime, durationAndDelay); + if (status === 'repeat') { + // the current update is happening after the instant the tween repeated + this._processRepetition(elapsedTime, durationAndDelay); } - else { - stillPlaying = checkStillPlayingAndReverse(); - doUpdates(); + this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); + if (status === 'about-to-repeat') { + // the current update is happening at the exact instant the tween is going to repeat + // the values should match the end of the tween, not the beginning, + // that's why _processRepetition happens after _updateProperties + this._processRepetition(elapsedTime, durationAndDelay); + } + if (this._onUpdateCallback) { + this._onUpdateCallback(this._object, elapsed); } - if (repeated) { + if (status === 'repeat' || status === 'about-to-repeat') { if (this._onRepeatCallback) { this._onRepeatCallback(this._object); } this._onEveryStartCallbackFired = false; } - if (completed) { - completed = true; + else if (status === 'completed') { + this._isPlaying = false; if (this._onCompleteCallback) { this._onCompleteCallback(this._object); } @@ -797,9 +742,52 @@ define(['exports'], (function (exports) { 'use strict'; // even if the `update()` method was called way past the duration of the tween this._chainedTweens[i].start(this._startTime + this._duration, false); } - this._isPlaying = false; } - return stillPlaying; + return status !== 'completed'; + }; + Tween.prototype._calculateElapsedPortion = function (elapsedTime, durationAndDelay, totalTime) { + if (this._duration === 0 || elapsedTime > totalTime) { + return 1; + } + var timeIntoCurrentRepeat = elapsedTime % durationAndDelay; + var portion = Math.min(timeIntoCurrentRepeat / this._duration, 1); + if (portion === 0 && elapsedTime !== 0 && elapsedTime % this._duration === 0) { + return 1; + } + return portion; + }; + Tween.prototype._calculateCompletionStatus = function (elapsedTime, durationAndDelay) { + if (this._duration !== 0 && elapsedTime < this._duration) { + return 'playing'; + } + if (this._repeat <= 0) { + return 'completed'; + } + if (elapsedTime === this._duration) { + return 'about-to-repeat'; + } + return 'repeat'; + }; + Tween.prototype._processRepetition = function (elapsedTime, durationAndDelay) { + var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); + if (isFinite(this._repeat)) { + this._repeat -= completeCount; + } + // Reassign starting values, restart by making startTime = now + for (var property in this._valuesStartRepeat) { + var valueEnd = this._valuesEnd[property]; + if (!this._yoyo && typeof valueEnd === 'string') { + this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(valueEnd); + } + if (this._yoyo) { + this._swapEndStartRepeatValues(property); + } + this._valuesStart[property] = this._valuesStartRepeat[property]; + } + if (this._yoyo) { + this._reversed = !this._reversed; + } + this._startTime += durationAndDelay * completeCount; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/dist/tween.cjs b/dist/tween.cjs index 2ddff5e0..86c8ee1e 100644 --- a/dist/tween.cjs +++ b/dist/tween.cjs @@ -680,13 +680,11 @@ var Tween = /** @class */ (function () { * it is still playing, just paused). */ Tween.prototype.update = function (time, autoStart) { - var _this = this; var _a; if (time === void 0) { time = now(); } if (autoStart === void 0) { autoStart = true; } if (this._isPaused) return true; - var property; var endTime = this._startTime + this._duration; if (!this._goToEnd && !this._isPlaying) { if (time > endTime) @@ -713,84 +711,31 @@ var Tween = /** @class */ (function () { var elapsedTime = time - this._startTime; var durationAndDelay = this._duration + ((_a = this._repeatDelayTime) !== null && _a !== void 0 ? _a : this._delayTime); var totalTime = this._duration + this._repeat * durationAndDelay; - var calculateElapsedPortion = function () { - if (_this._duration === 0) - return 1; - if (elapsedTime > totalTime) { - return 1; - } - var timesRepeated = Math.trunc(elapsedTime / durationAndDelay); - var timeIntoCurrentRepeat = elapsedTime - timesRepeated * durationAndDelay; - // TODO use %? - // const timeIntoCurrentRepeat = elapsedTime % durationAndDelay - var portion = Math.min(timeIntoCurrentRepeat / _this._duration, 1); - if (portion === 0 && elapsedTime === _this._duration) { - return 1; - } - return portion; - }; - var repeated = false; - var completed = false; - var checkStillPlayingAndReverse = function () { - if (_this._duration === 0 || elapsedTime >= _this._duration) { - if (_this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); - if (isFinite(_this._repeat)) { - _this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in _this._valuesStartRepeat) { - if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { - _this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); - } - if (_this._yoyo) { - _this._swapEndStartRepeatValues(property); - } - _this._valuesStart[property] = _this._valuesStartRepeat[property]; - } - if (_this._yoyo) { - _this._reversed = !_this._reversed; - } - _this._startTime += durationAndDelay * completeCount; - repeated = true; - return true; - } - else { - completed = true; - return false; - } - } - return true; - }; - var doUpdates = function () { - var elapsed = calculateElapsedPortion(); - var value = _this._easingFunction(elapsed); - // properties transformations - _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); - if (_this._onUpdateCallback) { - _this._onUpdateCallback(_this._object, elapsed); - } - }; - var stillPlaying; - if (elapsedTime <= this._duration) { - doUpdates(); - stillPlaying = checkStillPlayingAndReverse(); + var elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime); + var value = this._easingFunction(elapsed); + var status = this._calculateCompletionStatus(elapsedTime, durationAndDelay); + if (status === 'repeat') { + // the current update is happening after the instant the tween repeated + this._processRepetition(elapsedTime, durationAndDelay); } - else { - stillPlaying = checkStillPlayingAndReverse(); - doUpdates(); + this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); + if (status === 'about-to-repeat') { + // the current update is happening at the exact instant the tween is going to repeat + // the values should match the end of the tween, not the beginning, + // that's why _processRepetition happens after _updateProperties + this._processRepetition(elapsedTime, durationAndDelay); + } + if (this._onUpdateCallback) { + this._onUpdateCallback(this._object, elapsed); } - if (repeated) { + if (status === 'repeat' || status === 'about-to-repeat') { if (this._onRepeatCallback) { this._onRepeatCallback(this._object); } this._onEveryStartCallbackFired = false; } - if (completed) { - completed = true; + else if (status === 'completed') { + this._isPlaying = false; if (this._onCompleteCallback) { this._onCompleteCallback(this._object); } @@ -799,9 +744,52 @@ var Tween = /** @class */ (function () { // even if the `update()` method was called way past the duration of the tween this._chainedTweens[i].start(this._startTime + this._duration, false); } - this._isPlaying = false; } - return stillPlaying; + return status !== 'completed'; + }; + Tween.prototype._calculateElapsedPortion = function (elapsedTime, durationAndDelay, totalTime) { + if (this._duration === 0 || elapsedTime > totalTime) { + return 1; + } + var timeIntoCurrentRepeat = elapsedTime % durationAndDelay; + var portion = Math.min(timeIntoCurrentRepeat / this._duration, 1); + if (portion === 0 && elapsedTime !== 0 && elapsedTime % this._duration === 0) { + return 1; + } + return portion; + }; + Tween.prototype._calculateCompletionStatus = function (elapsedTime, durationAndDelay) { + if (this._duration !== 0 && elapsedTime < this._duration) { + return 'playing'; + } + if (this._repeat <= 0) { + return 'completed'; + } + if (elapsedTime === this._duration) { + return 'about-to-repeat'; + } + return 'repeat'; + }; + Tween.prototype._processRepetition = function (elapsedTime, durationAndDelay) { + var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); + if (isFinite(this._repeat)) { + this._repeat -= completeCount; + } + // Reassign starting values, restart by making startTime = now + for (var property in this._valuesStartRepeat) { + var valueEnd = this._valuesEnd[property]; + if (!this._yoyo && typeof valueEnd === 'string') { + this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(valueEnd); + } + if (this._yoyo) { + this._swapEndStartRepeatValues(property); + } + this._valuesStart[property] = this._valuesStartRepeat[property]; + } + if (this._yoyo) { + this._reversed = !this._reversed; + } + this._startTime += durationAndDelay * completeCount; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/dist/tween.d.ts b/dist/tween.d.ts index 19e9095d..7fdeba02 100644 --- a/dist/tween.d.ts +++ b/dist/tween.d.ts @@ -137,6 +137,9 @@ declare class Tween { * it is still playing, just paused). */ update(time?: number, autoStart?: boolean): boolean; + private _calculateElapsedPortion; + private _calculateCompletionStatus; + private _processRepetition; private _updateProperties; private _handleRelativeValue; private _swapEndStartRepeatValues; diff --git a/dist/tween.esm.js b/dist/tween.esm.js index e65c839d..86a97403 100644 --- a/dist/tween.esm.js +++ b/dist/tween.esm.js @@ -676,13 +676,11 @@ var Tween = /** @class */ (function () { * it is still playing, just paused). */ Tween.prototype.update = function (time, autoStart) { - var _this = this; var _a; if (time === void 0) { time = now(); } if (autoStart === void 0) { autoStart = true; } if (this._isPaused) return true; - var property; var endTime = this._startTime + this._duration; if (!this._goToEnd && !this._isPlaying) { if (time > endTime) @@ -709,84 +707,31 @@ var Tween = /** @class */ (function () { var elapsedTime = time - this._startTime; var durationAndDelay = this._duration + ((_a = this._repeatDelayTime) !== null && _a !== void 0 ? _a : this._delayTime); var totalTime = this._duration + this._repeat * durationAndDelay; - var calculateElapsedPortion = function () { - if (_this._duration === 0) - return 1; - if (elapsedTime > totalTime) { - return 1; - } - var timesRepeated = Math.trunc(elapsedTime / durationAndDelay); - var timeIntoCurrentRepeat = elapsedTime - timesRepeated * durationAndDelay; - // TODO use %? - // const timeIntoCurrentRepeat = elapsedTime % durationAndDelay - var portion = Math.min(timeIntoCurrentRepeat / _this._duration, 1); - if (portion === 0 && elapsedTime === _this._duration) { - return 1; - } - return portion; - }; - var repeated = false; - var completed = false; - var checkStillPlayingAndReverse = function () { - if (_this._duration === 0 || elapsedTime >= _this._duration) { - if (_this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); - if (isFinite(_this._repeat)) { - _this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in _this._valuesStartRepeat) { - if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { - _this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); - } - if (_this._yoyo) { - _this._swapEndStartRepeatValues(property); - } - _this._valuesStart[property] = _this._valuesStartRepeat[property]; - } - if (_this._yoyo) { - _this._reversed = !_this._reversed; - } - _this._startTime += durationAndDelay * completeCount; - repeated = true; - return true; - } - else { - completed = true; - return false; - } - } - return true; - }; - var doUpdates = function () { - var elapsed = calculateElapsedPortion(); - var value = _this._easingFunction(elapsed); - // properties transformations - _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); - if (_this._onUpdateCallback) { - _this._onUpdateCallback(_this._object, elapsed); - } - }; - var stillPlaying; - if (elapsedTime <= this._duration) { - doUpdates(); - stillPlaying = checkStillPlayingAndReverse(); + var elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime); + var value = this._easingFunction(elapsed); + var status = this._calculateCompletionStatus(elapsedTime, durationAndDelay); + if (status === 'repeat') { + // the current update is happening after the instant the tween repeated + this._processRepetition(elapsedTime, durationAndDelay); } - else { - stillPlaying = checkStillPlayingAndReverse(); - doUpdates(); + this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); + if (status === 'about-to-repeat') { + // the current update is happening at the exact instant the tween is going to repeat + // the values should match the end of the tween, not the beginning, + // that's why _processRepetition happens after _updateProperties + this._processRepetition(elapsedTime, durationAndDelay); + } + if (this._onUpdateCallback) { + this._onUpdateCallback(this._object, elapsed); } - if (repeated) { + if (status === 'repeat' || status === 'about-to-repeat') { if (this._onRepeatCallback) { this._onRepeatCallback(this._object); } this._onEveryStartCallbackFired = false; } - if (completed) { - completed = true; + else if (status === 'completed') { + this._isPlaying = false; if (this._onCompleteCallback) { this._onCompleteCallback(this._object); } @@ -795,9 +740,52 @@ var Tween = /** @class */ (function () { // even if the `update()` method was called way past the duration of the tween this._chainedTweens[i].start(this._startTime + this._duration, false); } - this._isPlaying = false; } - return stillPlaying; + return status !== 'completed'; + }; + Tween.prototype._calculateElapsedPortion = function (elapsedTime, durationAndDelay, totalTime) { + if (this._duration === 0 || elapsedTime > totalTime) { + return 1; + } + var timeIntoCurrentRepeat = elapsedTime % durationAndDelay; + var portion = Math.min(timeIntoCurrentRepeat / this._duration, 1); + if (portion === 0 && elapsedTime !== 0 && elapsedTime % this._duration === 0) { + return 1; + } + return portion; + }; + Tween.prototype._calculateCompletionStatus = function (elapsedTime, durationAndDelay) { + if (this._duration !== 0 && elapsedTime < this._duration) { + return 'playing'; + } + if (this._repeat <= 0) { + return 'completed'; + } + if (elapsedTime === this._duration) { + return 'about-to-repeat'; + } + return 'repeat'; + }; + Tween.prototype._processRepetition = function (elapsedTime, durationAndDelay) { + var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); + if (isFinite(this._repeat)) { + this._repeat -= completeCount; + } + // Reassign starting values, restart by making startTime = now + for (var property in this._valuesStartRepeat) { + var valueEnd = this._valuesEnd[property]; + if (!this._yoyo && typeof valueEnd === 'string') { + this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(valueEnd); + } + if (this._yoyo) { + this._swapEndStartRepeatValues(property); + } + this._valuesStart[property] = this._valuesStartRepeat[property]; + } + if (this._yoyo) { + this._reversed = !this._reversed; + } + this._startTime += durationAndDelay * completeCount; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/dist/tween.umd.js b/dist/tween.umd.js index 4bbf99fc..9d1e42b7 100644 --- a/dist/tween.umd.js +++ b/dist/tween.umd.js @@ -682,13 +682,11 @@ * it is still playing, just paused). */ Tween.prototype.update = function (time, autoStart) { - var _this = this; var _a; if (time === void 0) { time = now(); } if (autoStart === void 0) { autoStart = true; } if (this._isPaused) return true; - var property; var endTime = this._startTime + this._duration; if (!this._goToEnd && !this._isPlaying) { if (time > endTime) @@ -715,84 +713,31 @@ var elapsedTime = time - this._startTime; var durationAndDelay = this._duration + ((_a = this._repeatDelayTime) !== null && _a !== void 0 ? _a : this._delayTime); var totalTime = this._duration + this._repeat * durationAndDelay; - var calculateElapsedPortion = function () { - if (_this._duration === 0) - return 1; - if (elapsedTime > totalTime) { - return 1; - } - var timesRepeated = Math.trunc(elapsedTime / durationAndDelay); - var timeIntoCurrentRepeat = elapsedTime - timesRepeated * durationAndDelay; - // TODO use %? - // const timeIntoCurrentRepeat = elapsedTime % durationAndDelay - var portion = Math.min(timeIntoCurrentRepeat / _this._duration, 1); - if (portion === 0 && elapsedTime === _this._duration) { - return 1; - } - return portion; - }; - var repeated = false; - var completed = false; - var checkStillPlayingAndReverse = function () { - if (_this._duration === 0 || elapsedTime >= _this._duration) { - if (_this._repeat > 0) { - var completeCount = Math.min(Math.trunc((elapsedTime - _this._duration) / durationAndDelay) + 1, _this._repeat); - if (isFinite(_this._repeat)) { - _this._repeat -= completeCount; - } - // Reassign starting values, restart by making startTime = now - for (property in _this._valuesStartRepeat) { - if (!_this._yoyo && typeof _this._valuesEnd[property] === 'string') { - _this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - _this._valuesStartRepeat[property] + parseFloat(_this._valuesEnd[property]); - } - if (_this._yoyo) { - _this._swapEndStartRepeatValues(property); - } - _this._valuesStart[property] = _this._valuesStartRepeat[property]; - } - if (_this._yoyo) { - _this._reversed = !_this._reversed; - } - _this._startTime += durationAndDelay * completeCount; - repeated = true; - return true; - } - else { - completed = true; - return false; - } - } - return true; - }; - var doUpdates = function () { - var elapsed = calculateElapsedPortion(); - var value = _this._easingFunction(elapsed); - // properties transformations - _this._updateProperties(_this._object, _this._valuesStart, _this._valuesEnd, value); - if (_this._onUpdateCallback) { - _this._onUpdateCallback(_this._object, elapsed); - } - }; - var stillPlaying; - if (elapsedTime <= this._duration) { - doUpdates(); - stillPlaying = checkStillPlayingAndReverse(); + var elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime); + var value = this._easingFunction(elapsed); + var status = this._calculateCompletionStatus(elapsedTime, durationAndDelay); + if (status === 'repeat') { + // the current update is happening after the instant the tween repeated + this._processRepetition(elapsedTime, durationAndDelay); } - else { - stillPlaying = checkStillPlayingAndReverse(); - doUpdates(); + this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); + if (status === 'about-to-repeat') { + // the current update is happening at the exact instant the tween is going to repeat + // the values should match the end of the tween, not the beginning, + // that's why _processRepetition happens after _updateProperties + this._processRepetition(elapsedTime, durationAndDelay); + } + if (this._onUpdateCallback) { + this._onUpdateCallback(this._object, elapsed); } - if (repeated) { + if (status === 'repeat' || status === 'about-to-repeat') { if (this._onRepeatCallback) { this._onRepeatCallback(this._object); } this._onEveryStartCallbackFired = false; } - if (completed) { - completed = true; + else if (status === 'completed') { + this._isPlaying = false; if (this._onCompleteCallback) { this._onCompleteCallback(this._object); } @@ -801,9 +746,52 @@ // even if the `update()` method was called way past the duration of the tween this._chainedTweens[i].start(this._startTime + this._duration, false); } - this._isPlaying = false; } - return stillPlaying; + return status !== 'completed'; + }; + Tween.prototype._calculateElapsedPortion = function (elapsedTime, durationAndDelay, totalTime) { + if (this._duration === 0 || elapsedTime > totalTime) { + return 1; + } + var timeIntoCurrentRepeat = elapsedTime % durationAndDelay; + var portion = Math.min(timeIntoCurrentRepeat / this._duration, 1); + if (portion === 0 && elapsedTime !== 0 && elapsedTime % this._duration === 0) { + return 1; + } + return portion; + }; + Tween.prototype._calculateCompletionStatus = function (elapsedTime, durationAndDelay) { + if (this._duration !== 0 && elapsedTime < this._duration) { + return 'playing'; + } + if (this._repeat <= 0) { + return 'completed'; + } + if (elapsedTime === this._duration) { + return 'about-to-repeat'; + } + return 'repeat'; + }; + Tween.prototype._processRepetition = function (elapsedTime, durationAndDelay) { + var completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat); + if (isFinite(this._repeat)) { + this._repeat -= completeCount; + } + // Reassign starting values, restart by making startTime = now + for (var property in this._valuesStartRepeat) { + var valueEnd = this._valuesEnd[property]; + if (!this._yoyo && typeof valueEnd === 'string') { + this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(valueEnd); + } + if (this._yoyo) { + this._swapEndStartRepeatValues(property); + } + this._valuesStart[property] = this._valuesStartRepeat[property]; + } + if (this._yoyo) { + this._reversed = !this._reversed; + } + this._startTime += durationAndDelay * completeCount; }; Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { for (var property in _valuesEnd) { diff --git a/src/Tween.ts b/src/Tween.ts index f094e82c..a3b04375 100644 --- a/src/Tween.ts +++ b/src/Tween.ts @@ -400,8 +400,6 @@ export class Tween { update(time = now(), autoStart = true): boolean { if (this._isPaused) return true - let property - const endTime = this._startTime + this._duration if (!this._goToEnd && !this._isPlaying) { @@ -435,114 +433,106 @@ export class Tween { const durationAndDelay = this._duration + (this._repeatDelayTime ?? this._delayTime) const totalTime = this._duration + this._repeat * durationAndDelay - const calculateElapsedPortion = () => { - if (this._duration === 0) return 1 - if (elapsedTime > totalTime) { - return 1 - } + const elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime) + const value = this._easingFunction(elapsed) - const timesRepeated = Math.trunc(elapsedTime / durationAndDelay) - const timeIntoCurrentRepeat = elapsedTime - timesRepeated * durationAndDelay - // TODO use %? - // const timeIntoCurrentRepeat = elapsedTime % durationAndDelay + const status = this._calculateCompletionStatus(elapsedTime, durationAndDelay); - const portion = Math.min(timeIntoCurrentRepeat / this._duration, 1) - if (portion === 0 && elapsedTime === this._duration) { - return 1 - } - return portion + if (status === 'repeat') { + // the current update is happening after the instant the tween repeated + this._processRepetition(elapsedTime, durationAndDelay) } - let repeated = false; - let completed = false; + this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value) - const checkStillPlayingAndReverse = () => { - if (this._duration === 0 || elapsedTime >= this._duration) { - if (this._repeat > 0) { - const completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat) - if (isFinite(this._repeat)) { - this._repeat -= completeCount - } - - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]) - } + if (status === 'about-to-repeat') { + // the current update is happening at the exact instant the tween is going to repeat + // the values should match the end of the tween, not the beginning, + // that's why _processRepetition happens after _updateProperties + this._processRepetition(elapsedTime, durationAndDelay) + } - if (this._yoyo) { - this._swapEndStartRepeatValues(property) - } + if (this._onUpdateCallback) { + this._onUpdateCallback(this._object, elapsed) + } - this._valuesStart[property] = this._valuesStartRepeat[property] - } + if (status === 'repeat' || status === 'about-to-repeat') { + if (this._onRepeatCallback) { + this._onRepeatCallback(this._object) + } - if (this._yoyo) { - this._reversed = !this._reversed - } + this._onEveryStartCallbackFired = false + } else if (status === 'completed') { + this._isPlaying = false; - this._startTime += durationAndDelay * completeCount - repeated = true; + if (this._onCompleteCallback) { + this._onCompleteCallback(this._object) + } - return true - } else { - completed = true; - return false - } + for (let i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + this._chainedTweens[i].start(this._startTime + this._duration, false) } + } + return status !== 'completed' + } - return true + private _calculateElapsedPortion(elapsedTime: number, durationAndDelay: number, totalTime: number) { + if (this._duration === 0 || elapsedTime > totalTime) { + return 1 } - const doUpdates = () => { - const elapsed = calculateElapsedPortion() - const value = this._easingFunction(elapsed) + const timeIntoCurrentRepeat = elapsedTime % durationAndDelay - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value) + const portion = Math.min(timeIntoCurrentRepeat / this._duration, 1) + if (portion === 0 && elapsedTime !== 0 && elapsedTime % this._duration === 0) { + return 1 + } + return portion + } - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed) - } - }; + private _calculateCompletionStatus(elapsedTime: number, durationAndDelay: number) { + if (this._duration !== 0 && elapsedTime < this._duration) { + return 'playing' + } - let stillPlaying; + if (this._repeat <= 0) { + return 'completed' + } - if (elapsedTime <= this._duration) { - doUpdates(); - stillPlaying = checkStillPlayingAndReverse(); - } else { - stillPlaying = checkStillPlayingAndReverse(); - doUpdates(); + if (elapsedTime === this._duration) { + return 'about-to-repeat' } - if (repeated) { - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object) - } + return 'repeat' + } - this._onEveryStartCallbackFired = false + private _processRepetition(elapsedTime: number, durationAndDelay: number) { + const completeCount = Math.min(Math.trunc((elapsedTime - this._duration) / durationAndDelay) + 1, this._repeat) + if (isFinite(this._repeat)) { + this._repeat -= completeCount } - if (completed) { - completed = true; - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object) + // Reassign starting values, restart by making startTime = now + for (const property in this._valuesStartRepeat) { + const valueEnd = this._valuesEnd[property] + if (!this._yoyo && typeof valueEnd === 'string') { + this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(valueEnd) } - for (let i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false) + if (this._yoyo) { + this._swapEndStartRepeatValues(property) } - this._isPlaying = false + this._valuesStart[property] = this._valuesStartRepeat[property] + } + + if (this._yoyo) { + this._reversed = !this._reversed } - return stillPlaying; + this._startTime += durationAndDelay * completeCount } private _updateProperties( diff --git a/src/tests.ts b/src/tests.ts index f96e3dca..d22695f8 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1283,6 +1283,55 @@ export const tests = { test.done() }, + 'Test repeat behaves the same with quick and slow updates'(test: Test): void { + TWEEN.removeAll() + + const makeTween = (obj: { x: number }) => + new TWEEN.Tween(obj) + .to({x: 100}, 100) + .repeat(20) + .start(0); + + const obj1 = {x: 0}; + const tween1 = makeTween(obj1); + + for (let t = 0; t <= 300; t += 25) { + tween1.update(t); + + const obj2 = {x: 0}; + const tween2 = makeTween(obj2); + tween2.update(t); + test.equal(obj1.x, obj2.x, `t=${t}: ${obj1.x} === ${obj2.x}`); + } + + test.done() + }, + + 'Test repeat+delay behaves the same with quick and slow updates'(test: Test): void { + TWEEN.removeAll() + + const makeTween = (obj: { x: number }) => + new TWEEN.Tween(obj) + .to({x: 100}, 100) + .delay(50) + .repeat(20) + .start(0); + + const obj1 = {x: 0}; + const tween1 = makeTween(obj1); + + for (let t = 0; t <= 300; t += 25) { + tween1.update(t); + + const obj2 = {x: 0}; + const tween2 = makeTween(obj2); + tween2.update(t); + test.equal(obj1.x, obj2.x, `t=${t}: ${obj1.x} === ${obj2.x}`); + } + + test.done() + }, + 'Test yoyo with repeat Infinity happens forever'(test: Test): void { TWEEN.removeAll() From 64153127ea95de9bfb9dc10998a046d07a6a234f Mon Sep 17 00:00:00 2001 From: Joe Pea Date: Sun, 5 May 2024 13:58:32 -0700 Subject: [PATCH 4/4] format code, ensure CI uses clean-install for reproducible builds, rename 'lint' script to 'format', add a Three.js example to README --- .github/workflows/tests.yml | 2 +- README.md | 7 ++- package.json | 6 +- src/Tween.ts | 4 +- src/tests.ts | 109 +++++++++++++++++------------------- 5 files changed, 61 insertions(+), 67 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ed126ad3..bae9361f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,5 +19,5 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: npm install + - run: npm clean-install - run: npm test diff --git a/README.md b/README.md index 48ef9633..9211e396 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ JavaScript (TypeScript) tweening engine for easy animations, incorporating optim More languages: [English](./README.md), [简体中文](./README_zh-CN.md) ---- +# Example ```html @@ -48,7 +48,10 @@ More languages: [English](./README.md), [简体中文](./README_zh-CN.md) ``` -[Try this example on CodePen](https://codepen.io/trusktr/pen/KKGaBVz?editors=1000) +[Try the above example on CodePen](https://codepen.io/trusktr/pen/KKGaBVz?editors=1000) + +Animate numbers in any JavaScript object. For example, [rotate a 3D box made +with Three.js](https://codepen.io/trusktr/pen/ExJqvgZ): # Installation diff --git a/package.json b/package.json index 8560a18f..15affe45 100644 --- a/package.json +++ b/package.json @@ -39,10 +39,10 @@ "tsc": "tsc", "tsc-watch": "tsc --watch", "examples": "npx serve .", - "test": "npm run build && npm run test-lint && npm run test-unit", + "test": "npm run build && npm run format-check && npm run test-unit", "test-unit": "nodeunit test/unit/nodeunitheadless.cjs", - "test-lint": "npm run prettier -- --check", - "lint": "npm run prettier -- --write", + "format-check": "npm run prettier -- --check", + "format": "npm run prettier -- --write", "prettier": "prettier .", "prepare": "npm run build", "version": "npm test && git add .", diff --git a/src/Tween.ts b/src/Tween.ts index a3b04375..0c73854a 100644 --- a/src/Tween.ts +++ b/src/Tween.ts @@ -436,7 +436,7 @@ export class Tween { const elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime) const value = this._easingFunction(elapsed) - const status = this._calculateCompletionStatus(elapsedTime, durationAndDelay); + const status = this._calculateCompletionStatus(elapsedTime, durationAndDelay) if (status === 'repeat') { // the current update is happening after the instant the tween repeated @@ -463,7 +463,7 @@ export class Tween { this._onEveryStartCallbackFired = false } else if (status === 'completed') { - this._isPlaying = false; + this._isPlaying = false if (this._onCompleteCallback) { this._onCompleteCallback(this._object) diff --git a/src/tests.ts b/src/tests.ts index d22695f8..4ce30c71 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -1286,22 +1286,18 @@ export const tests = { 'Test repeat behaves the same with quick and slow updates'(test: Test): void { TWEEN.removeAll() - const makeTween = (obj: { x: number }) => - new TWEEN.Tween(obj) - .to({x: 100}, 100) - .repeat(20) - .start(0); + const makeTween = (obj: {x: number}) => new TWEEN.Tween(obj).to({x: 100}, 100).repeat(20).start(0) - const obj1 = {x: 0}; - const tween1 = makeTween(obj1); + const obj1 = {x: 0} + const tween1 = makeTween(obj1) for (let t = 0; t <= 300; t += 25) { - tween1.update(t); + tween1.update(t) - const obj2 = {x: 0}; - const tween2 = makeTween(obj2); - tween2.update(t); - test.equal(obj1.x, obj2.x, `t=${t}: ${obj1.x} === ${obj2.x}`); + const obj2 = {x: 0} + const tween2 = makeTween(obj2) + tween2.update(t) + test.equal(obj1.x, obj2.x, `t=${t}: ${obj1.x} === ${obj2.x}`) } test.done() @@ -1310,23 +1306,18 @@ export const tests = { 'Test repeat+delay behaves the same with quick and slow updates'(test: Test): void { TWEEN.removeAll() - const makeTween = (obj: { x: number }) => - new TWEEN.Tween(obj) - .to({x: 100}, 100) - .delay(50) - .repeat(20) - .start(0); + const makeTween = (obj: {x: number}) => new TWEEN.Tween(obj).to({x: 100}, 100).delay(50).repeat(20).start(0) - const obj1 = {x: 0}; - const tween1 = makeTween(obj1); + const obj1 = {x: 0} + const tween1 = makeTween(obj1) for (let t = 0; t <= 300; t += 25) { - tween1.update(t); + tween1.update(t) - const obj2 = {x: 0}; - const tween2 = makeTween(obj2); - tween2.update(t); - test.equal(obj1.x, obj2.x, `t=${t}: ${obj1.x} === ${obj2.x}`); + const obj2 = {x: 0} + const tween2 = makeTween(obj2) + tween2.update(t) + test.equal(obj1.x, obj2.x, `t=${t}: ${obj1.x} === ${obj2.x}`) } test.done() @@ -1459,38 +1450,38 @@ export const tests = { }, 'Test yoyo reverses at right instant'(test: Test): void { - TWEEN.removeAll(); + TWEEN.removeAll() - const obj = { x: 0 }; - new TWEEN.Tween(obj).to({ x: 100 }, 100).repeat(1).yoyo(true).start(0); + const obj = {x: 0} + new TWEEN.Tween(obj).to({x: 100}, 100).repeat(1).yoyo(true).start(0) - TWEEN.update(98); - test.equal(obj.x, 98); + TWEEN.update(98) + test.equal(obj.x, 98) - TWEEN.update(99); - test.equal(obj.x, 99); + TWEEN.update(99) + test.equal(obj.x, 99) // Previously this would fail, the first update after 100 would happen as if yoyo=false - TWEEN.update(101); - test.equal(obj.x, 99); + TWEEN.update(101) + test.equal(obj.x, 99) - TWEEN.update(101); - test.equal(obj.x, 99); + TWEEN.update(101) + test.equal(obj.x, 99) - TWEEN.update(102); - test.equal(obj.x, 98); + TWEEN.update(102) + test.equal(obj.x, 98) - test.done(); + test.done() }, 'Test yoyo callbacks happen on right order'(test: Test): void { - TWEEN.removeAll(); + TWEEN.removeAll() - let events: string[] = []; - const obj = { x: 0 } + let events: string[] = [] + const obj = {x: 0} new TWEEN.Tween(obj) - .to({ x: 100 }, 100) + .to({x: 100}, 100) .repeat(1) .yoyo(true) .easing(TWEEN.Easing.Linear.None) @@ -1499,26 +1490,26 @@ export const tests = { .onEveryStart(() => events.push('everystart')) .onRepeat(() => events.push('repeat')) .onComplete(() => events.push('complete')) - .start(0); + .start(0) function testAndReset(expected: string[]) { - test.deepEqual(events, expected); - events = []; + test.deepEqual(events, expected) + events = [] } - testAndReset([]); - TWEEN.update(99); - testAndReset(['start', 'everystart', 'update']); - TWEEN.update(101); - testAndReset(['update', 'repeat']); - TWEEN.update(150); - testAndReset(['everystart', 'update']); - TWEEN.update(199); - testAndReset(['update']); - TWEEN.update(201); - testAndReset(['update', 'complete']); - - test.done(); + testAndReset([]) + TWEEN.update(99) + testAndReset(['start', 'everystart', 'update']) + TWEEN.update(101) + testAndReset(['update', 'repeat']) + TWEEN.update(150) + testAndReset(['everystart', 'update']) + TWEEN.update(199) + testAndReset(['update']) + TWEEN.update(201) + testAndReset(['update', 'complete']) + + test.done() }, 'Test TWEEN.Tween.stopChainedTweens()'(test: Test): void {