Merge branch 'master' into patch-1

This commit is contained in:
Michael Casebolt 2017-07-14 17:26:46 -07:00 committed by GitHub
commit acdaa68453
12 changed files with 3089 additions and 251 deletions

75
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,75 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at coc@soledadpenades.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,7 +1,13 @@
# Contributing
## Code of conduct
Please note that this project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). **By participating in this project you agree to abide by its terms**.
Before reporting a bug
---
If you find something that you believe to be a bug, please
If you find something that you believe to be a bug, please
1. search the [issue tracker](https://github.com/tweenjs/tween.js/issues) for similar issues
2. check out the [master](https://github.com/tweenjs/tween.js/tree/master) branch and see if the bug still exists there.
@ -21,7 +27,7 @@ Contributing
1. Get a GitHub account (if you don't have one yet).
2. Fork the project in GitHub.
3. Check the [contribution guidelines](https://github.com/tweenjs/tween.js/wiki/Contributing-to-tween.js).
3. Check the [contribution guidelines](https://github.com/tweenjs/tween.js/wiki/Contributing-to-tween.js).
4. Make changes to your clone of the repository
5. Submit a pull request.

169
README.md
View File

@ -46,13 +46,13 @@ See [tween.js](https://cdnjs.com/libraries/tween.js/) for more versions.
#### Use `npm`
```bash
npm install tween.js
npm install @tweenjs/tween.js
```
Then include the Tween.js module with the standard node.js `require`:
```javascript
var TWEEN = require('tween.js');
var TWEEN = require('@tweenjs/tween.js');
```
And you can use Tween.js as in all other examples--for example:
@ -67,19 +67,19 @@ You will need to use a tool such as `browserify` to convert code using this styl
#### Use `bower`
```bash
bower install tweenjs --save
bower install @tweenjs/tweenjs --save
```
or install an specific tag. They are git tags, and you can run `git tag` in the command line for a list if you have cloned the repository locally, or you can also check out the list in the [tween.js tags page](https://github.com/tweenjs/tween.js/tags). For example, to install `v16.3.0`:
```bash
bower install tweenjs#v16.3.0
bower install @tweenjs/tweenjs#v16.3.0
```
Then reference the library source:
```html
<script src="bower_components/tweenjs/src/Tween.js"></script>
<script src="bower_components/@tweenjs/tweenjs/src/Tween.js"></script>
```
## Features
@ -93,26 +93,146 @@ Then reference the library source:
## Documentation
* [User guide](./docs/user_guide.md)
* [Contributor guide](./docs/contributor_guide.md)
* [Tutorial](http://learningthreejs.com/blog/2011/08/17/tweenjs-for-smooth-animation/) using tween.js with three.js
* Also: [libtween](https://github.com/jsm174/libtween), a port of tween.js to C by [jsm174](https://github.com/jsm174)
* Also: [es6-tween](https://github.com/tweenjs/es6-tween), a port of tween.js to ES6/Harmony by [dalisoft](https://github.com/dalisoft)
## Examples
[![Custom functions](http://tweenjs.github.io/tween.js/assets/examples/03_graphs.png)](http://tweenjs.github.io/tween.js/examples/12_graphs_custom_functions.html)
[source](./examples/12_graphs_custom_functions.html)
[![Stop all chained tweens](http://tweenjs.github.io/tween.js/assets/examples/11_stop_all_chained_tweens.png)](http://tweenjs.github.io/tween.js/examples/11_stop_all_chained_tweens.html) [source](./examples/11_stop_all_chained_tweens.html)
[![Yoyo](http://tweenjs.github.io/tween.js/assets/examples/10_yoyo.png)](http://tweenjs.github.io/tween.js/examples/10_yoyo.html) [source](./examples/10_yoyo.html)
[![Relative values](http://tweenjs.github.io/tween.js/assets/examples/09_relative.png)](http://tweenjs.github.io/tween.js/examples/09_relative_values.html) [source](./examples/09_relative_values.html)
[![Repeat](http://tweenjs.github.io/tween.js/assets/examples/08_repeat.png)](http://tweenjs.github.io/tween.js/examples/08_repeat.html) [source](./examples/08_repeat.html)
[![Dynamic to](http://tweenjs.github.io/tween.js/assets/examples/07_dynamic_to.png)](http://tweenjs.github.io/tween.js/examples/07_dynamic_to.html) [source](./examples/07_dynamic_to.html)
[![Array interpolation](http://tweenjs.github.io/tween.js/assets/examples/03_graphs.png)](http://tweenjs.github.io/tween.js/examples/06_array_interpolation.html) [source](./examples/06_array_interpolation.html)
[![Video and time](http://tweenjs.github.io/tween.js/assets/examples/06_video_and_time.png)](http://tweenjs.github.io/tween.js/examples/05_video_and_time.html) [source](./examples/05_video_and_time.html)
[![Simplest possible example](http://tweenjs.github.io/tween.js/assets/examples/04_simplest.png)](http://tweenjs.github.io/tween.js/examples/04_simplest.html) [source](./examples/04_simplest.html)
[![Graphs](http://tweenjs.github.io/tween.js/assets/examples/03_graphs.png)](http://tweenjs.github.io/tween.js/examples/03_graphs.html) [source](./examples/03_graphs.html)
[![Black and red](http://tweenjs.github.io/tween.js/assets/examples/02_black_and_red.png)](http://tweenjs.github.io/tween.js/examples/02_black_and_red.html) [source](./examples/02_black_and_red.html)
[![Bars](http://tweenjs.github.io/tween.js/assets/examples/01_bars.png)](http://tweenjs.github.io/tween.js/examples/01_bars.html) [source](./examples/01_bars.html)
[![hello world](http://tweenjs.github.io/tween.js/assets/examples/00_hello_world.png)](http://tweenjs.github.io/tween.js/examples/00_hello_world.html) [source](./examples/00_hello_world.html)
<table>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/12_graphs_custom_functions.html">
<img src="./assets/examples/03_graphs.png" alt="Custom functions" />
</a>
</td>
<td>
Custom functions<br />
(<a href="examples/12_graphs_custom_functions.html">source</a>)
</td>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/11_stop_all_chained_tweens.html">
<img src="./assets/examples/11_stop_all_chained_tweens.png" alt="Stop all chained tweens" />
</a>
</td>
<td>
Stop all chained tweens<br />
(<a href="examples/11_stop_all_chained_tweens.html">source</a>)
</td>
</tr>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/10_yoyo.html">
<img src="./assets/examples/10_yoyo.png" alt="Yoyo" />
</a>
</td>
<td>
Yoyo<br />
(<a href="examples/10_yoyo.html">source</a>)
</td>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/09_relative_values.html">
<img src="./assets/examples/09_relative.png" alt="Relative values" />
</a>
</td>
<td>
Relative values<br />
(<a href="examples/09_relative_values.html">source</a>)
</td>
</tr>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/08_repeat.html">
<img src="./assets/examples/08_repeat.png" alt="Repeat" />
</a>
</td>
<td>
Repeat<br />
(<a href="examples/08_repeat.html">source</a>)
</td>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/07_dynamic_to.html">
<img src="./assets/examples/07_dynamic_to.png" alt="Dynamic to" />
</a>
</td>
<td>
Dynamic to<br />
(<a href="examples/07_dynamic_to.html">source</a>)
</td>
</tr>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/06_array_interpolation.html">
<img src="./assets/examples/03_graphs.png" alt="Array interpolation" />
</a>
</td>
<td>
Array interpolation<br />
(<a href="examples/06_array_interpolation.html">source</a>)
</td>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/05_video_and_time.html">
<img src="./assets/examples/06_video_and_time.png" alt="Video and time" />
</a>
</td>
<td>
Video and time<br />
(<a href="examples/05_video_and_time.html">source</a>)
</td>
</tr>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/04_simplest.html">
<img src="./assets/examples/04_simplest.png" alt="Simplest possible example" />
</a>
</td>
<td>
Simplest possible example<br />
(<a href="examples/04_simplest.html">source</a>)
</td>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/03_graphs.html">
<img src="./assets/examples/03_graphs.png" alt="Graphs" />
</a>
</td>
<td>
Graphs<br />
(<a href="examples/03_graphs.html">source</a>)
</td>
</tr>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/02_black_and_red.html">
<img src="./assets/examples/02_black_and_red.png" alt="Black and red" />
</a>
</td>
<td>
Black and red<br />
(<a href="examples/02_black_and_red.html">source</a>)
</td>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/01_bars.html">
<img src="./assets/examples/01_bars.png" alt="Bars" />
</a>
</td>
<td>
Bars<br />
(<a href="examples/01_bars.html">source</a>)
</td>
</tr>
<tr>
<td>
<a href="http://tweenjs.github.io/tween.js/examples/00_hello_world.html">
<img src="./assets/examples/00_hello_world.png" alt="hello world" />
</a>
</td>
<td>
hello world<br />
(<a href="examples/00_hello_world.html">source</a>)
</td>
</tr>
</table>
## Tests
@ -130,7 +250,7 @@ npm test
every time you want to run the tests.
If you want to add any feature or change existing features, you *must* run the tests to make sure you didn't break anything else. If you send a PR to add something new and it doesn't have tests, or the tests don't pass, the PR won't be accepted. See [contributing](CONTRIBUTING.md) for more information.
If you want to add any feature or change existing features, you *must* run the tests to make sure you didn't break anything else. If you send a pull request (PR) to add something new and it doesn't have tests, or the tests don't pass, the PR won't be accepted. See [contributing](CONTRIBUTING.md) for more information.
## People
@ -151,13 +271,14 @@ Maintainers: [mikebolt](https://github.com/mikebolt), [sole](https://github.com/
[![The Wilderness Downtown](http://tweenjs.github.io/tween.js/assets/projects/01_wilderness.png)](http://thewildernessdowntown.com/)
[![Linechart](http://tweenjs.github.io/tween.js/assets/projects/00_linechart.png)](http://dejavis.org/linechart)
[npm-image]: https://img.shields.io/npm/v/tween.js.svg
[npm-url]: https://npmjs.org/package/tween.js
[downloads-image]: https://img.shields.io/npm/dm/tween.js.svg
[downloads-url]: https://npmjs.org/package/tween.js
[npm-image]: https://img.shields.io/npm/v/@tweenjs/tween.js.svg
[npm-url]: https://npmjs.org/package/@tweenjs/tween.js
[downloads-image]: https://img.shields.io/npm/dm/@tweenjs/tween.js.svg
[downloads-url]: https://npmjs.org/package/@tweenjs/tween.js
[travis-image]: https://travis-ci.org/tweenjs/tween.js.svg?branch=master
[travis-url]: https://travis-ci.org/tweenjs/tween.js
[flattr-image]: https://api.flattr.com/button/flattr-badge-large.png
[flattr-url]: https://flattr.com/thing/45014/tween-js
[cdnjs-image]: https://img.shields.io/cdnjs/v/tween.js.svg
[cdnjs-url]: https://cdnjs.com/libraries/tween.js

252
docs/contributor_guide.md Normal file
View File

@ -0,0 +1,252 @@
# tween.js contributor guide
This guide is for people who want to *contribute* to the library or are curious to learn what's behind the scenes: how is it tested? what do we automate? how do we do releases? etc.
If you are looking for documentation on *how to use* the library, the [user guide](./user_guide.md) is for you.
**NOTE: this document is a work in progress. More content will be added in stages. If you have questions you'd like to see answered, please add them [as comments on this issue](https://github.com/tweenjs/tween.js/issues/323). THANKS!**
In this guide:
* [Developer requirements](#developer-requirements)
* [Testing](#testing)
* [Continuous integration](#continuous-integration)
* [Release process](#release-process)
## Developer requirements
Although tween.js does *not* need node.js to work, we use it for development. So you will need to [install node.js](https://nodejs.org/en/download/) before you can work on the library.
Node.js includes the `npm` tool that we use to run scripts such as the one for packaging, running tests, etc. Please make sure it is working in your system before you attempt to run any of the steps detailed below.
Once node.js is installed, clone the tween.js repository:
```bash
git clone https://github.com/tweenjs/tween.js.git
```
Change to the folder:
```bash
cd tween.js
```
And run the script to install development dependencies:
```bash
npm install
```
Or in three lines:
```bash
git clone https://github.com/tweenjs/tween.js.git
cd tween.js
npm install
```
Once `npm install` completes successfully, try having a go at running the tests:
```bash
npm test
```
If you get issues running any of the above, try to search for the text of the error using your search engine of choice. This is normally the fastest route, as many people might have encountered the same issue already.
## Testing
There's a suite of automated tests in the `test` directory.
These can quickly spot regressions on the code--useful when new features are added or when code is changed to fix a bug; we don't want to introduce new bugs! They also spot style issues, which helps us keep the library cohesive.
To run the tests, type:
```bash
npm test
```
You should run the tests after you change code in the library. If you change the behaviour the tests describe, the tests won't pass and you'll get an error pointing to the test(s) that failed. This might be either because...
* you overlooked something or there's an error in your code, or...
* the library or the tests themselves are wrong
The one that happens more frequently is the first one, but the second one has happened, with edge cases.
**Another thing you should do once the automated tests pass is to run the examples in the `examples` folder**. It is rare, but it might happen that your changes introduced cosmetic differences that the automated tests are not checking for, and the only way to notice this is by running the examples and have a human spot the difference in output. If you don't want to checkout two copies of the library, you can look at [the online examples](https://github.com/tweenjs/tween.js#examples).
### Unit tests
Tests are in the `test/unit/tests.js` file.
The tests are executed using [nodeunit](https://www.npmjs.com/package/nodeunit).
**TODO:** the tests should also work if opening `test/unit/nodeunit.html` in a browser, but they are broken right now. There is [an open issue](https://github.com/tweenjs/tween.js/issues/307) to make them work again.
### Correction and style tests
We use [JSCS](http://jscs.info/) and [JSHint](http://jshint.com/) to ensure the code style is uniform.
#### JSCS
This tool helps us spot mostly 'cosmetic' code style issues. For example, white spaces versus tabs, spaces between brackets, etc.
To run it:
```bash
npm run test-style
```
The rules for JSCS are in `test/jscs.json`.
#### JSHint
This tool helps us spot code quality issues. For example, using the right equality operator, unused variables, etc.
To run it:
```bash
npm run test-correctness
```
The rules for JSHint are in `test/jshintrc`.
### Other types of tests
We would like to test for performance regressions i.e. if a change made things go slower, or simply, for performance, so we can compare the performance of the same code between different browsers.
There's an [open issue](https://github.com/tweenjs/discuss/issues/3) to track work on this, but we have not made progress on it yet. Help! :-)
## Continuous integration
We have implemented a continuous integration system that does things automatically for us. It runs the tests automatically each time a pull request is made, and it can also publish new releases automatically in certain cases.
If proposed changes in a pull request break anything, contributors get feedback without having to wait for a human to have a look at the code. Also, the request cannot be merged until the tests pass.
We are using the Travis CI platform to run the tests. You will find a little information area at the bottom of the pull request page, letting you know about the state of the tests.
Example of all checks passing:
![Automated checks OK](./imgs/pull-request-checks.png)
And when checks fail:
![Automated checks failing](./imgs/pull-request-checks-failing.png)
If a pull request is updated by adding new commits, the tests will run again.
Travis is configured with the `.travis.yml` file (if you don't see it with your file explorer or the Finder, it's because the file name starts with a dot and so it's *hidden*--try opening it with the terminal).
## Release process
We use the [semantic-release](https://github.com/semantic-release/semantic-release) tool in combination with Travis to automatically [create releases on GitHub](https://github.com/tweenjs/tween.js/releases) and publish them [to npm](https://npmjs.org).
Each time a pull request is merged, Travis will run the tests. If they pass without errors, Travis will run the `after_success` step:
```yaml
after_success:
- npm run semantic-release
```
This in turn will run the `semantic-release` script in `package.json`:
```json
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
```
And when the new release is made:
* `semantic-release` determines the next version number
* a new entry is added to the GitHub releases list, along with a list of all the commits included in the change, and a ZIP file with that version, for people who want to download ZIPs
* the git commit is tagged with the version number (tools like [Bower](http://bower.io/) use tags)
* it is also published to npm, with the new version number in `package.json`.
**Note:** the default configuration option for `semantic-release` is to run only if the branch name is `master`. Otherwise, we would be generating lots of pushes and releases, as Travis runs with each pull request!
Please also note that the version number in `package.json` is intentionally `0.0.0-development`, as we do not want to encourage anyone to modify this manually, but we also cannot remove the `version` field from the file or installing modules using the git repository directly will fail.
### How the new version number is determined
Like npm, `semantic-release` follows the [semver](http://semver.org/) convention, so each release is identified by a unique `MAJOR.MINOR.PATCH` version number. For example, given version `1.2.3`: 1 = major, 2 = minor, 3 = patch.
In this system, breaking changes (e.g. the API is modified, and updating to a new version might require updating the code that uses the library) should increase the major number. If there are backwards compatible changes (e.g. a new feature that does not modify existing APIs) the minor number will increase. Smaller changes, such as a documentation update, only increase the patch number.
`semantic-release` uses the commit messages to decide on the next version number automatically.
This is really *great*, because keeping track of version numbers or deciding on whether a new release should be a major or minor change is an extremely boring task, best left to machines.
For this to work automatically, the commit messages need to follow a certain syntax:
`<type_of_commit>: <commit log message as usual>.`
The following table lists the types of commits and their effect on version numbers, using [the default behaviour](https://github.com/semantic-release/commit-analyzer/blob/master/src/index.js).
| Type of commit | Description | Version increase? |
|----------------|-----------------------------------------|-------------------|
| fix | fixes a bug but does not change the API | Increases PATCH |
| style | formatting changes | |
| docs | adding/removing/changing docs | |
| refactor | rewriting code but not breaking changes, adding features or fixing bugs | |
| test | changes in tests, e.g. adding a missing one) | |
| feat | adding new features which do not change the API | Increases MINOR |
| BREAKING CHANGE | changes the API | Increases MAJOR |
### How to install and configure `semantic-release`
This is mostly for informational purposes, as `semantic-release` is already configured with Travis and contributors shouldn't need to worry about this, but it is good to document everything.
#### Option 1: using the CLI utility
First install the global cli utility:
```bash
npm install -g semantic-release-cli
```
Then in an existing node.js based project (i.e. a `package.json` already exists):
```bash
semantic-release-cli setup
```
It will ask you a series of questions to set up `semantic-release` on the project. If all goes well, the next time you push to GitHub, a new release will automatically happen.
You will need to have TravisCI enabled in your account.
#### Option 2: manually
Install the module:
```bash
npm install --save-dev semantic-release
```
Edit `package.json` to add the `semantic-release` script:
```javascript
"scripts": {
//...
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
//...
},
```
Create a `.travis.yml` file if it doesn't exist yet (here is [help creating `travis.yml` files](https://docs.travis-ci.com/user/getting-started/)), or [have a look at ours](https://github.com/tweenjs/tween.js/blob/master/.travis.yml).
Add an `after_success` section to `.travis.yml`, in order to run `semantic-release`:
```yaml
after_success:
- npm run semantic-release
```
Now we need to enable the project in Travis CI, so make sure you have an account there and are logged in.
Enable the project in Travis (if you're the maintainer) or ask the maintainer to enable it, in the [Travis settings page](https://travis-ci.org/profile/).
Click on the cog near to the project name to configure some options.
Scroll down until you see *Environment Variables*.
Add tokens for `GH_TOKEN` and `NPM_TOKEN`. Make sure both variables are hidden: `Display value in build log` should be `Off`.
You can get tokens from [npm](https://www.npmjs.com/settings/tokens) and from [GitHub](https://github.com/settings/tokens). These allow services such as Travis to act on your behalf, which is why you need to ensure they are not displayed in the build log.
Hopefully, now each time you commit and push to GitHub `semantic-release` will run (if using the `master` branch as described above) and maybe a new version will be published.

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -163,15 +163,17 @@ In other cases, you may want to chain multiple tweens to another tween in a way
tweenA.chain(tweenB,tweenC);
````
> WARNING: Calling `tweenA.chain(tweenB)` actually modifies tweenA so that tweenB is always started when tweenA finishes. The return value of `chain` is just tweenA, not a new tween.
### `repeat`
If you wanted a tween to repeat forever you could chain it to itself, but a better way is to use the `repeat` method. It accepts a parameter that describes how many repetitions you want:
If you wanted a tween to repeat forever you could chain it to itself, but a better way is to use the `repeat` method. It accepts a parameter that describes how many repetitions you want after the first tween is completed:
````javascript
tween.repeat(10); // repeats 10 times and stops
tween.repeat(10); // repeats 10 times after the first tween and stops
tween.repeat(Infinity); // repeats forever
````
The total number of tweens will be the repeat parameter plus one for the initial tween.
Check the [Repeat](../examples/08_repeat.html) example.
### `yoyo`
@ -209,6 +211,46 @@ Used to add a tween to the list of active tweens, or to remove an specific one f
These methods are usually used internally only, but are exposed just in case you want to do something _funny_.
## Controlling groups of tweens
Using the `TWEEN` singleton to manage your tweens can cause issues in large apps with many components. In these cases, you may want to create your own smaller groups of tweens.
#### Example: cross-component conflict
A conflict can occur if you have multiple components using `TWEEN`, and each component wants to manage its own set of tweens. If one component calls `TWEEN.update()` or `TWEEN.removeAll()` the tweens of other components will also be updated or removed.
#### Creating your own tween groups
To solve this, each component can make their own instance of `TWEEN.Group` (which is what the global `TWEEN` object uses internally). These groups can be passed in as a second optional parameter when instantiating a new tween:
```javascript
var groupA = new TWEEN.Group();
var groupB = new TWEEN.Group();
var tweenA = new TWEEN.Tween({ x: 1 }, groupA)
.to({ x: 10 }, 100)
.start();
var tweenB = new TWEEN.Tween({ x: 1 }, groupB)
.to({ x: 10 }, 100)
.start();
var tweenC = new TWEEN.Tween({ x: 1 })
.to({ x: 10 }, 100)
.start();
groupA.update(); // only updates tweenA
groupB.update(); // only updates tweenB
TWEEN.update(); // only updates tweenC
groupA.removeAll(); // only removes tweenA
groupB.removeAll(); // only removes tweenB
TWEEN.removeAll(); // only removes tweenC
```
In this way, each component can handle creating, updating, and destroying its own set of tweens.
## Changing the easing function (AKA make it bouncy)
Tween.js will perform the interpolation between values (i.e. the easing) in a linear manner, so the change will be directly proportional to the elapsed time. This is predictable but also quite uninteresting visually wise. Worry not--this behaviour can be easily changed using the `easing` method. For example:

View File

@ -1,5 +1,5 @@
{
"name": "tween.js",
"name": "@tweenjs/tween.js",
"description": "Super simple, fast and easy to use tweening engine which incorporates optimised Robert Penner's equations.",
"version": "0.0.0-development",
"main": "src/Tween.js",
@ -26,9 +26,9 @@
},
"author": "tween.js contributors (https://github.com/tweenjs/tween.js/graphs/contributors)",
"devDependencies": {
"jscs": "^2.2.0",
"jshint": "^2.8.0",
"nodeunit": "^0.9.1",
"jscs": "^2.11.0",
"jshint": "^2.9.4",
"nodeunit": "^0.9.5",
"semantic-release": "^6.3.2"
}
}

View File

@ -7,66 +7,88 @@
* Thank you all, you're awesome!
*/
var TWEEN = TWEEN || (function () {
var TWEEN = TWEEN || {};
var _tweens = [];
return {
TWEEN._nextId = 0;
TWEEN.nextId = function () {
return TWEEN._nextId++;
};
getAll: function () {
return _tweens;
TWEEN.Group = function () {
this._tweens = {};
this._tweensAddedDuringUpdate = {};
};
},
TWEEN.Group.prototype = assign(Object.create(Object.prototype), {
getAll: function () {
removeAll: function () {
return Object.keys(this._tweens).map(function (tweenId) {
return this._tweens[tweenId];
}.bind(this));
_tweens = [];
},
},
removeAll: function () {
add: function (tween) {
this._tweens = {};
_tweens.push(tween);
},
},
add: function (tween) {
remove: function (tween) {
this._tweens[tween.getId()] = tween;
this._tweensAddedDuringUpdate[tween.getId()] = tween;
var i = _tweens.indexOf(tween);
},
if (i !== -1) {
_tweens.splice(i, 1);
}
remove: function (tween) {
},
delete this._tweens[tween.getId()];
delete this._tweensAddedDuringUpdate[tween.getId()];
update: function (time, preserve) {
},
if (_tweens.length === 0) {
return false;
}
update: function (time, preserve) {
var i = 0;
time = time !== undefined ? time : TWEEN.now();
while (i < _tweens.length) {
if (_tweens[i].update(time) || preserve) {
i++;
} else {
_tweens.splice(i, 1);
}
}
return true;
var tweenIds = Object.keys(this._tweens);
if (tweenIds.length === 0) {
return false;
}
};
})();
time = time !== undefined ? time : TWEEN.now();
// Tweens are updated in "batches". If you add a new tween during an update, then the
// new tween will be updated in the next batch.
// If you remove a tween during an update, it will normally still be updated. However,
// if the removed tween was added during the current batch, then it will not be updated.
while (tweenIds.length > 0) {
this._tweensAddedDuringUpdate = {};
for (var i = 0; i < tweenIds.length; i++) {
if (this._tweens[tweenIds[i]].update(time) === false) {
this._tweens[tweenIds[i]]._isPlaying = false;
if (!preserve) {
delete this._tweens[tweenIds[i]];
}
}
}
tweenIds = Object.keys(this._tweensAddedDuringUpdate);
}
return true;
}
});
// Create global group
assignDeep(TWEEN, new TWEEN.Group());
// Include a performance.now polyfill.
@ -99,235 +121,274 @@ else {
}
TWEEN.Tween = function (object) {
function assign(target, source) {
var keys = Object.keys(source);
var length = keys.length;
var _object = object;
var _valuesStart = {};
var _valuesEnd = {};
var _valuesStartRepeat = {};
var _duration = 1000;
var _repeat = 0;
var _repeatDelayTime;
var _yoyo = false;
var _isPlaying = false;
var _reversed = false;
var _delayTime = 0;
var _startTime = null;
var _easingFunction = TWEEN.Easing.Linear.None;
var _interpolationFunction = TWEEN.Interpolation.Linear;
var _chainedTweens = [];
var _onStartCallback = null;
var _onStartCallbackFired = false;
var _onUpdateCallback = null;
var _onCompleteCallback = null;
var _onStopCallback = null;
for (var i = 0; i < length; i += 1) {
target[keys[i]] = source[keys[i]];
}
this.to = function (properties, duration) {
return target;
}
_valuesEnd = properties;
function assignDeep(target, source) {
// Assign own properties
assign(target, source);
// Assign prototype properties
var targetProto = Object.getPrototypeOf(target);
var sourceProto = Object.getPrototypeOf(source);
for (var prop in sourceProto) {
targetProto[prop] = sourceProto[prop];
}
return target;
}
TWEEN.Tween = function (object, group) {
this._object = object;
this._valuesStart = {};
this._valuesEnd = {};
this._valuesStartRepeat = {};
this._duration = 1000;
this._repeat = 0;
this._repeatDelayTime = undefined;
this._yoyo = false;
this._isPlaying = false;
this._reversed = false;
this._delayTime = 0;
this._startTime = null;
this._easingFunction = TWEEN.Easing.Linear.None;
this._interpolationFunction = TWEEN.Interpolation.Linear;
this._chainedTweens = [];
this._onStartCallback = null;
this._onStartCallbackFired = false;
this._onUpdateCallback = null;
this._onCompleteCallback = null;
this._onStopCallback = null;
this._group = group || TWEEN;
this._id = TWEEN.nextId();
};
TWEEN.Tween.prototype = assign(Object.create(Object.prototype), {
getId: function getId() {
return this._id;
},
isPlaying: function isPlaying() {
return this._isPlaying;
},
to: function to(properties, duration) {
this._valuesEnd = properties;
if (duration !== undefined) {
_duration = duration;
this._duration = duration;
}
return this;
};
},
this.start = function (time) {
start: function start(time) {
TWEEN.add(this);
this._group.add(this);
_isPlaying = true;
this._isPlaying = true;
_onStartCallbackFired = false;
this._onStartCallbackFired = false;
_startTime = time !== undefined ? time : TWEEN.now();
_startTime += _delayTime;
this._startTime = time !== undefined ? time : TWEEN.now();
this._startTime += this._delayTime;
for (var property in _valuesEnd) {
for (var property in this._valuesEnd) {
// Check if an Array was provided as property value
if (_valuesEnd[property] instanceof Array) {
if (this._valuesEnd[property] instanceof Array) {
if (_valuesEnd[property].length === 0) {
if (this._valuesEnd[property].length === 0) {
continue;
}
// Create a local copy of the Array with the start value at the front
_valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]);
this._valuesEnd[property] = [this._object[property]].concat(this._valuesEnd[property]);
}
// If `to()` specifies a property that doesn't exist in the source object,
// we should not set that property in the object
if (_object[property] === undefined) {
if (this._object[property] === undefined) {
continue;
}
// Save the starting value.
_valuesStart[property] = _object[property];
this._valuesStart[property] = this._object[property];
if ((_valuesStart[property] instanceof Array) === false) {
_valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
if ((this._valuesStart[property] instanceof Array) === false) {
this._valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
}
_valuesStartRepeat[property] = _valuesStart[property] || 0;
this._valuesStartRepeat[property] = this._valuesStart[property] || 0;
}
return this;
};
},
this.stop = function () {
stop: function stop() {
if (!_isPlaying) {
if (!this._isPlaying) {
return this;
}
TWEEN.remove(this);
_isPlaying = false;
this._group.remove(this);
this._isPlaying = false;
if (_onStopCallback !== null) {
_onStopCallback.call(_object, _object);
if (this._onStopCallback !== null) {
this._onStopCallback.call(this._object, this._object);
}
this.stopChainedTweens();
return this;
};
},
this.end = function () {
end: function end() {
this.update(_startTime + _duration);
this.update(this._startTime + this._duration);
return this;
};
},
this.stopChainedTweens = function () {
stopChainedTweens: function stopChainedTweens() {
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
_chainedTweens[i].stop();
for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) {
this._chainedTweens[i].stop();
}
};
},
this.delay = function (amount) {
delay: function delay(amount) {
_delayTime = amount;
this._delayTime = amount;
return this;
};
},
this.repeat = function (times) {
repeat: function repeat(times) {
_repeat = times;
this._repeat = times;
return this;
};
},
this.repeatDelay = function (amount) {
repeatDelay: function repeatDelay(amount) {
_repeatDelayTime = amount;
this._repeatDelayTime = amount;
return this;
};
},
this.yoyo = function (yoyo) {
yoyo: function yoyo(yoyo) {
_yoyo = yoyo;
this._yoyo = yoyo;
return this;
};
},
easing: function easing(easing) {
this.easing = function (easing) {
_easingFunction = easing;
this._easingFunction = easing;
return this;
};
},
this.interpolation = function (interpolation) {
interpolation: function interpolation(interpolation) {
_interpolationFunction = interpolation;
this._interpolationFunction = interpolation;
return this;
};
},
this.chain = function () {
chain: function chain() {
_chainedTweens = arguments;
this._chainedTweens = arguments;
return this;
};
},
this.onStart = function (callback) {
onStart: function onStart(callback) {
_onStartCallback = callback;
this._onStartCallback = callback;
return this;
};
},
this.onUpdate = function (callback) {
onUpdate: function onUpdate(callback) {
_onUpdateCallback = callback;
this._onUpdateCallback = callback;
return this;
};
},
this.onComplete = function (callback) {
onComplete: function onComplete(callback) {
_onCompleteCallback = callback;
this._onCompleteCallback = callback;
return this;
};
},
this.onStop = function (callback) {
onStop: function onStop(callback) {
_onStopCallback = callback;
this._onStopCallback = callback;
return this;
};
},
this.update = function (time) {
update: function update(time) {
var property;
var elapsed;
var value;
if (time < _startTime) {
if (time < this._startTime) {
return true;
}
if (_onStartCallbackFired === false) {
if (this._onStartCallbackFired === false) {
if (_onStartCallback !== null) {
_onStartCallback.call(_object, _object);
if (this._onStartCallback !== null) {
this._onStartCallback.call(this._object, this._object);
}
_onStartCallbackFired = true;
this._onStartCallbackFired = true;
}
elapsed = (time - _startTime) / _duration;
elapsed = (time - this._startTime) / this._duration;
elapsed = elapsed > 1 ? 1 : elapsed;
value = _easingFunction(elapsed);
value = this._easingFunction(elapsed);
for (property in _valuesEnd) {
for (property in this._valuesEnd) {
// Don't update properties that do not exist in the source object
if (_valuesStart[property] === undefined) {
if (this._valuesStart[property] === undefined) {
continue;
}
var start = _valuesStart[property] || 0;
var end = _valuesEnd[property];
var start = this._valuesStart[property] || 0;
var end = this._valuesEnd[property];
if (end instanceof Array) {
_object[property] = _interpolationFunction(end, value);
this._object[property] = this._interpolationFunction(end, value);
} else {
@ -343,66 +404,66 @@ TWEEN.Tween = function (object) {
// Protect against non numeric properties.
if (typeof (end) === 'number') {
_object[property] = start + (end - start) * value;
this._object[property] = start + (end - start) * value;
}
}
}
if (_onUpdateCallback !== null) {
_onUpdateCallback.call(_object, value);
if (this._onUpdateCallback !== null) {
this._onUpdateCallback.call(this._object, value);
}
if (elapsed === 1) {
if (_repeat > 0) {
if (this._repeat > 0) {
if (isFinite(_repeat)) {
_repeat--;
if (isFinite(this._repeat)) {
this._repeat--;
}
// Reassign starting values, restart by making startTime = now
for (property in _valuesStartRepeat) {
for (property in this._valuesStartRepeat) {
if (typeof (_valuesEnd[property]) === 'string') {
_valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property]);
if (typeof (this._valuesEnd[property]) === 'string') {
this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]);
}
if (_yoyo) {
var tmp = _valuesStartRepeat[property];
if (this._yoyo) {
var tmp = this._valuesStartRepeat[property];
_valuesStartRepeat[property] = _valuesEnd[property];
_valuesEnd[property] = tmp;
this._valuesStartRepeat[property] = this._valuesEnd[property];
this._valuesEnd[property] = tmp;
}
_valuesStart[property] = _valuesStartRepeat[property];
this._valuesStart[property] = this._valuesStartRepeat[property];
}
if (_yoyo) {
_reversed = !_reversed;
if (this._yoyo) {
this._reversed = !this._reversed;
}
if (_repeatDelayTime !== undefined) {
_startTime = time + _repeatDelayTime;
if (this._repeatDelayTime !== undefined) {
this._startTime = time + this._repeatDelayTime;
} else {
_startTime = time + _delayTime;
this._startTime = time + this._delayTime;
}
return true;
} else {
if (_onCompleteCallback !== null) {
if (this._onCompleteCallback !== null) {
_onCompleteCallback.call(_object, _object);
this._onCompleteCallback.call(this._object, this._object);
}
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
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
_chainedTweens[i].start(_startTime + _duration);
this._chainedTweens[i].start(this._startTime + this._duration);
}
return false;
@ -413,9 +474,8 @@ TWEEN.Tween = function (object) {
return true;
};
};
}
});
TWEEN.Easing = {

View File

@ -1,7 +1,7 @@
<html>
<head>
<title>nodeunit based tests</title>
<script src="../../node_modules/nodeunit/examples/browser/nodeunit.js"></script>
<script src="nodeunit.js"></script>
<script src="../../src/Tween.js"></script>
<script src="tests.js"></script>
</head>

2127
test/unit/nodeunit.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -57,7 +57,7 @@
TWEEN.add( t );
test.equal( numTweens + 1, TWEEN.getAll().length );
test.equal( all, TWEEN.getAll() );
test.done();
},
@ -70,13 +70,12 @@
TWEEN.add( t );
test.ok( all.indexOf( t ) != -1 );
test.ok( TWEEN.getAll().indexOf( t ) != -1 );
TWEEN.remove( t );
test.equal( numTweens, TWEEN.getAll().length );
test.equal( all, TWEEN.getAll() );
test.equal( all.indexOf( t ), -1 );
test.equal( TWEEN.getAll().indexOf( t ), -1 );
test.done();
},
@ -893,51 +892,6 @@
test.done();
},
'Test TWEEN.Tween.stopChainedTweens()': function(test) {
var t = new TWEEN.Tween( {} ),
tStarted = false,
tCompleted = false,
t2 = new TWEEN.Tween( {} ),
t2Started = false;
TWEEN.removeAll();
t.to( {}, 1000 );
t2.delay(500).to( {}, 1000 );
t.chain( t2 );
t2.chain( t );
t.onStart(function() {
tStarted = true;
});
t.onComplete(function() {
tCompleted = true;
});
t2.onStart(function() {
test.equal( tStarted, true );
test.equal( tCompleted, true );
test.equal( t2Started, false );
t2Started = true;
});
test.equal( tStarted, false );
test.equal( t2Started, false );
t.start( 0 );
TWEEN.update( 1001 );
t.stop();
test.equal( tStarted, true );
test.equal( t2Started, false );
test.equal( TWEEN.getAll().length, 0 );
test.done();
},
'Test TWEEN.Tween.chain progressess into chained tweens': function(test) {
var obj = { t: 1000 };
@ -1142,7 +1096,208 @@
test.done();
}
},
'tween.isPlaying() is false before the tween starts': function(test) {
TWEEN.removeAll();
var t = new TWEEN.Tween({x:0}).to({x:1}, 100);
test.equal(t.isPlaying(), false);
test.done();
},
'tween.isPlaying() is true when a tween is started and before it ends': function(test) {
TWEEN.removeAll();
var t = new TWEEN.Tween({x:0}).to({x:1}, 100);
t.start(0);
test.equal(t.isPlaying(), true);
test.done();
},
'tween.isPlaying() is false after a tween ends': function(test) {
TWEEN.removeAll();
var t = new TWEEN.Tween({x:0}).to({x:1}, 100);
t.start(0);
TWEEN.update(150);
test.equal(t.isPlaying(), false);
test.done();
},
// Custom TWEEN.Group tests
'Custom group.getAll()': function(test) {
var group = new TWEEN.Group();
test.ok( group.getAll() instanceof Array );
test.done();
},
'Custom group stores tweens instead of global TWEEN group': function(test) {
var group = new TWEEN.Group();
var numGlobalTweensBefore = TWEEN.getAll().length;
var numGroupTweensBefore = group.getAll().length;
var globalTween = new TWEEN.Tween( {} );
var groupTweenA = new TWEEN.Tween( {}, group );
var groupTweenB = new TWEEN.Tween( {}, group );
globalTween.start();
groupTweenA.start();
groupTweenB.start();
test.equal( TWEEN.getAll().length, numGlobalTweensBefore + 1 );
test.equal( group.getAll().length, numGroupTweensBefore + 2 );
test.done();
},
'Custom group.removeAll() doesn\'t conflict with global TWEEN group': function(test) {
var group = new TWEEN.Group();
TWEEN.removeAll();
group.removeAll();
test.equal( TWEEN.getAll().length, 0, "No global tweens left" );
test.equal( group.getAll().length, 0, "No group tweens left" );
var globalTween = new TWEEN.Tween( {} );
var groupTweenA = new TWEEN.Tween( {}, group );
var groupTweenB = new TWEEN.Tween( {}, group );
globalTween.start();
groupTweenA.start();
groupTweenB.start();
test.equal( TWEEN.getAll().length, 1, "One global tween has been added" );
test.equal( group.getAll().length, 2, "Two group tweens have been added" );
group.removeAll();
test.equal( TWEEN.getAll().length, 1, "One global tween left" );
test.equal( group.getAll().length, 0, "No group tweens left" );
TWEEN.removeAll();
test.equal( TWEEN.getAll().length, 0, "No global tweens left" );
test.done();
},
'Global TWEEN.removeAll() doesn\'t conflict with custom group': function(test) {
var group = new TWEEN.Group();
TWEEN.removeAll();
group.removeAll();
test.equal( TWEEN.getAll().length, 0, "No global tweens left" );
test.equal( group.getAll().length, 0, "No group tweens left" );
var globalTween = new TWEEN.Tween( {} );
var groupTweenA = new TWEEN.Tween( {}, group );
var groupTweenB = new TWEEN.Tween( {}, group );
globalTween.start();
groupTweenA.start();
groupTweenB.start();
test.equal( TWEEN.getAll().length, 1, "One global tween has been added" );
test.equal( group.getAll().length, 2, "Two group tweens have been added" );
TWEEN.removeAll();
test.equal( TWEEN.getAll().length, 0, "No global tweens left" );
test.equal( group.getAll().length, 2, "Two group tweens left" );
group.removeAll();
test.equal( group.getAll().length, 0, "No group tweens left" );
test.done();
},
'Custom group.add() doesn\'t conflict with global TWEEN group, or vice versa': function(test) {
var group = new TWEEN.Group();
var globalTween = new TWEEN.Tween( {} );
var groupTweenA = new TWEEN.Tween( {}, group );
var groupTweenB = new TWEEN.Tween( {}, group );
var numGlobalTweens = TWEEN.getAll().length;
var numGroupTweens = group.getAll().length;
TWEEN.add( globalTween );
group.add( groupTweenA );
group.add( groupTweenB );
test.equal( numGlobalTweens + 1, TWEEN.getAll().length );
test.equal( numGroupTweens + 2, group.getAll().length );
test.done();
},
'Custom group.update() doesn\'t conflict with global TWEEN group': function(test) {
var group = new TWEEN.Group();
var startObj = { x: 1 };
var endObj = { x: 2 };
var duration = 1000;
var globalObj = Object.assign( startObj );
var globalTween = new TWEEN.Tween( globalObj )
.to( endObj, duration )
.start( 0 );
var groupObj = Object.assign( startObj );
var groupTween = new TWEEN.Tween( groupObj, group )
.to( endObj, duration )
.start( 0 );
group.update( duration );
test.deepEqual( globalObj, startObj );
test.deepEqual( groupObj, endObj );
test.done();
},
'Global TWEEN.update() doesn\'t conflict with custom group': function(test) {
var group = new TWEEN.Group();
var startObj = { x: 1 };
var endObj = { x: 2 };
var duration = 1000;
var globalObj = Object.assign( startObj );
var globalTween = new TWEEN.Tween( globalObj )
.to( endObj, duration )
.start( 0 );
var groupObj = Object.assign( startObj );
var groupTween = new TWEEN.Tween( groupObj, group )
.to( endObj, duration )
.start( 0 );
TWEEN.update( duration );
test.deepEqual( globalObj, endObj );
test.deepEqual( groupObj, startObj );
test.done();
},
};