diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index bae9361f..ed126ad3 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 clean-install
+ - run: npm install
- run: npm test
diff --git a/README.md b/README.md
index 9211e396..48ef9633 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,10 +48,7 @@ More languages: [English](./README.md), [简体中文](./README_zh-CN.md)
```
-[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):
+[Try this example on CodePen](https://codepen.io/trusktr/pen/KKGaBVz?editors=1000)
# Installation
diff --git a/dist/tween.amd.js b/dist/tween.amd.js
index 77100205..a0cfdfdd 100644
--- a/dist/tween.amd.js
+++ b/dist/tween.amd.js
@@ -678,11 +678,13 @@ 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)
@@ -709,85 +711,72 @@ 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 elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime);
+ 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 elapsed = calculateElapsedPortion();
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);
- }
+ // properties transformations
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 (status === 'repeat' || status === 'about-to-repeat') {
- if (this._onRepeatCallback) {
- this._onRepeatCallback(this._object);
- }
- this._onEveryStartCallbackFired = false;
- }
- else if (status === 'completed') {
- this._isPlaying = false;
- 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);
- }
- }
- 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._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;
+ if (this._onRepeatCallback) {
+ this._onRepeatCallback(this._object);
+ }
+ this._onEveryStartCallbackFired = false;
+ return true;
}
- if (this._yoyo) {
- this._swapEndStartRepeatValues(property);
+ 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._valuesStart[property] = this._valuesStartRepeat[property];
}
- if (this._yoyo) {
- this._reversed = !this._reversed;
- }
- this._startTime += durationAndDelay * completeCount;
+ return true;
};
Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) {
for (var property in _valuesEnd) {
diff --git a/dist/tween.cjs b/dist/tween.cjs
index 7de60d34..0e15887c 100644
--- a/dist/tween.cjs
+++ b/dist/tween.cjs
@@ -680,11 +680,13 @@ 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)
@@ -711,85 +713,72 @@ 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 elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime);
+ 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 elapsed = calculateElapsedPortion();
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);
- }
+ // properties transformations
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 (status === 'repeat' || status === 'about-to-repeat') {
- if (this._onRepeatCallback) {
- this._onRepeatCallback(this._object);
- }
- this._onEveryStartCallbackFired = false;
- }
- else if (status === 'completed') {
- this._isPlaying = false;
- 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);
- }
- }
- 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._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;
+ if (this._onRepeatCallback) {
+ this._onRepeatCallback(this._object);
+ }
+ this._onEveryStartCallbackFired = false;
+ return true;
}
- if (this._yoyo) {
- this._swapEndStartRepeatValues(property);
+ 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._valuesStart[property] = this._valuesStartRepeat[property];
}
- if (this._yoyo) {
- this._reversed = !this._reversed;
- }
- this._startTime += durationAndDelay * completeCount;
+ return true;
};
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 c6d7dbae..2a5ecb67 100644
--- a/dist/tween.d.ts
+++ b/dist/tween.d.ts
@@ -137,9 +137,6 @@ 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 5fbb8699..4e6dc7d7 100644
--- a/dist/tween.esm.js
+++ b/dist/tween.esm.js
@@ -676,11 +676,13 @@ 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)
@@ -707,85 +709,72 @@ 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 elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime);
+ 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 elapsed = calculateElapsedPortion();
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);
- }
+ // properties transformations
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 (status === 'repeat' || status === 'about-to-repeat') {
- if (this._onRepeatCallback) {
- this._onRepeatCallback(this._object);
- }
- this._onEveryStartCallbackFired = false;
- }
- else if (status === 'completed') {
- this._isPlaying = false;
- 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);
- }
- }
- 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._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;
+ if (this._onRepeatCallback) {
+ this._onRepeatCallback(this._object);
+ }
+ this._onEveryStartCallbackFired = false;
+ return true;
}
- if (this._yoyo) {
- this._swapEndStartRepeatValues(property);
+ 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._valuesStart[property] = this._valuesStartRepeat[property];
}
- if (this._yoyo) {
- this._reversed = !this._reversed;
- }
- this._startTime += durationAndDelay * completeCount;
+ return true;
};
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 9b032f38..b4ffc32b 100644
--- a/dist/tween.umd.js
+++ b/dist/tween.umd.js
@@ -682,11 +682,13 @@
* 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,85 +715,72 @@
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 elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime);
+ 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 elapsed = calculateElapsedPortion();
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);
- }
+ // properties transformations
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 (status === 'repeat' || status === 'about-to-repeat') {
- if (this._onRepeatCallback) {
- this._onRepeatCallback(this._object);
- }
- this._onEveryStartCallbackFired = false;
- }
- else if (status === 'completed') {
- this._isPlaying = false;
- 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);
- }
- }
- 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._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;
+ if (this._onRepeatCallback) {
+ this._onRepeatCallback(this._object);
+ }
+ this._onEveryStartCallbackFired = false;
+ return true;
}
- if (this._yoyo) {
- this._swapEndStartRepeatValues(property);
+ 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._valuesStart[property] = this._valuesStartRepeat[property];
}
- if (this._yoyo) {
- this._reversed = !this._reversed;
- }
- this._startTime += durationAndDelay * completeCount;
+ return true;
};
Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) {
for (var property in _valuesEnd) {
diff --git a/package.json b/package.json
index 31072b6c..c5d9bd8b 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 format-check && npm run test-unit",
+ "test": "npm run build && npm run test-lint && npm run test-unit",
"test-unit": "nodeunit test/unit/nodeunitheadless.cjs",
- "format-check": "npm run prettier -- --check",
- "format": "npm run prettier -- --write",
+ "test-lint": "npm run prettier -- --check",
+ "lint": "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 0c73854a..2cc49bb0 100644
--- a/src/Tween.ts
+++ b/src/Tween.ts
@@ -400,6 +400,8 @@ 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) {
@@ -433,106 +435,87 @@ export class Tween {
const durationAndDelay = this._duration + (this._repeatDelayTime ?? this._delayTime)
const totalTime = this._duration + this._repeat * durationAndDelay
- const elapsed = this._calculateElapsedPortion(elapsedTime, durationAndDelay, totalTime)
- const value = this._easingFunction(elapsed)
+ const calculateElapsedPortion = () => {
+ if (this._duration === 0) return 1
+ if (elapsedTime > totalTime) {
+ return 1
+ }
- const status = this._calculateCompletionStatus(elapsedTime, durationAndDelay)
+ const timesRepeated = Math.trunc(elapsedTime / durationAndDelay)
+ const timeIntoCurrentRepeat = elapsedTime - timesRepeated * durationAndDelay
+ // TODO use %?
+ // const timeIntoCurrentRepeat = elapsedTime % durationAndDelay
- if (status === 'repeat') {
- // the current update is happening after the instant the tween repeated
- this._processRepetition(elapsedTime, durationAndDelay)
+ const portion = Math.min(timeIntoCurrentRepeat / this._duration, 1)
+ if (portion === 0 && elapsedTime === this._duration) {
+ return 1
+ }
+ return portion
}
+ const elapsed = calculateElapsedPortion()
+ const value = this._easingFunction(elapsed)
+ // properties transformations
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 (status === 'repeat' || status === 'about-to-repeat') {
- if (this._onRepeatCallback) {
- this._onRepeatCallback(this._object)
- }
-
- this._onEveryStartCallbackFired = false
- } else if (status === 'completed') {
- this._isPlaying = false
-
- if (this._onCompleteCallback) {
- this._onCompleteCallback(this._object)
- }
+ 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
+ }
- 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'
- }
+ // 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])
+ }
- private _calculateElapsedPortion(elapsedTime: number, durationAndDelay: number, totalTime: number) {
- if (this._duration === 0 || elapsedTime > totalTime) {
- return 1
- }
+ if (this._yoyo) {
+ this._swapEndStartRepeatValues(property)
+ }
- const timeIntoCurrentRepeat = elapsedTime % durationAndDelay
+ this._valuesStart[property] = this._valuesStartRepeat[property]
+ }
- const portion = Math.min(timeIntoCurrentRepeat / this._duration, 1)
- if (portion === 0 && elapsedTime !== 0 && elapsedTime % this._duration === 0) {
- return 1
- }
- return portion
- }
+ if (this._yoyo) {
+ this._reversed = !this._reversed
+ }
- private _calculateCompletionStatus(elapsedTime: number, durationAndDelay: number) {
- if (this._duration !== 0 && elapsedTime < this._duration) {
- return 'playing'
- }
+ this._startTime += durationAndDelay * completeCount
- if (this._repeat <= 0) {
- return 'completed'
- }
+ if (this._onRepeatCallback) {
+ this._onRepeatCallback(this._object)
+ }
- if (elapsedTime === this._duration) {
- return 'about-to-repeat'
- }
+ this._onEveryStartCallbackFired = false
- return 'repeat'
- }
+ return true
+ } else {
+ if (this._onCompleteCallback) {
+ this._onCompleteCallback(this._object)
+ }
- 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
- }
+ 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)
+ }
- // 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)
- }
+ this._isPlaying = false
- if (this._yoyo) {
- this._swapEndStartRepeatValues(property)
+ return false
}
-
- this._valuesStart[property] = this._valuesStartRepeat[property]
- }
-
- if (this._yoyo) {
- this._reversed = !this._reversed
}
- this._startTime += durationAndDelay * completeCount
+ return true
}
private _updateProperties(
diff --git a/src/tests.ts b/src/tests.ts
index 4ce30c71..6fbcd744 100644
--- a/src/tests.ts
+++ b/src/tests.ts
@@ -1283,46 +1283,6 @@ 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()
@@ -1449,69 +1409,6 @@ 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 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({})
diff --git a/test/unit/nodeunit.html b/test/unit/nodeunit.html
index 130bf914..69c7595b 100644
--- a/test/unit/nodeunit.html
+++ b/test/unit/nodeunit.html
@@ -7,13 +7,6 @@
-
-