├── .editorconfig ├── .gitignore ├── .prettierrc ├── .eslintignore ├── .travis.yml ├── .vscode └── settings.json ├── .npmignore ├── rollup.config.js ├── .babelrc ├── .eslintrc.json ├── src ├── shim.js ├── index.js ├── selector.js ├── NodeCache.js ├── Interpolator.js ├── PlaybackPosition.js ├── Easing.js ├── Interpolation.js ├── core.js ├── Timeline.js ├── constants.js └── Tween.js ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── regular-issue.md │ └── bug_report.md └── FUNDING.yml ├── jsdoc.json ├── withPage.js ├── LICENSE ├── examples ├── basic.html └── test.html ├── CONTRIBUTING.md ├── performance ├── gsap.html ├── kute.html ├── es6-tween.html └── kute.js ├── logo ├── box.svg └── es6-logo.svg ├── package.json ├── CODE_OF_CONDUCT.md ├── README.md ├── test.js └── API.md /.editorconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | docs/ 3 | full/ 4 | bundled/ 5 | lite/ 6 | guide_notes/ 7 | node_modules/ 8 | npm-debug.log 9 | yarn-error.log 10 | .vscode 11 | .idea 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "trailingComma": "es5", 5 | "arrowParens": "always", 6 | "bracketSpacing": true 7 | } 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # /node_modules/* and /bower_components/* in the project root are ignored by default 2 | 3 | # Ignore 4 | .github/* 5 | bundled/* 6 | examples/* 7 | performance/* 8 | logo/* 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - '12' 9 | before_script: 10 | - npm prune 11 | script: 12 | - npm test 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": true, 3 | "eslint.autoFixOnSave": true, 4 | "eslint.packageManager": "npm", 5 | "prettier.requireConfig": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .git 3 | .github 4 | bower.json 5 | tsconfig.json 6 | tslint.json 7 | assets 8 | docs 9 | examples 10 | logo 11 | performance 12 | guide_notes 13 | CONTRIBUTING.md 14 | ISSUE_TEMPLATE.md 15 | API.md 16 | .travis.yml 17 | yarn-error.log 18 | .vscode 19 | .idea 20 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | 3 | export default { 4 | input: 'src/index.js', 5 | output: { 6 | format: 'umd', 7 | file: 'bundled/Tween.js', 8 | name: 'TWEEN', 9 | sourcemap: true 10 | }, 11 | context: 'this', 12 | plugins: [babel()] 13 | } 14 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["node_modules/**", "bundled/**", "performance/**", "logo/**", "examples/**"], 3 | "presets": [ 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "modules": false, 8 | "shippedProposals": true 9 | } 10 | ] 11 | ], 12 | "plugins": ["@babel/plugin-proposal-class-properties"] 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "standard", 8 | "globals": { 9 | "Atomics": "readonly", 10 | "SharedArrayBuffer": "readonly" 11 | }, 12 | "parserOptions": { 13 | "ecmaVersion": 2018, 14 | "sourceType": "module" 15 | }, 16 | "rules": {} 17 | } 18 | -------------------------------------------------------------------------------- /src/shim.js: -------------------------------------------------------------------------------- 1 | /* global global, self */ 2 | export let root = 3 | typeof self !== 'undefined' 4 | ? self 5 | : typeof window !== 'undefined' 6 | ? window 7 | : typeof global !== 'undefined' 8 | ? global 9 | : this || (typeof exports !== 'undefined' ? exports : {}) 10 | export let requestAnimationFrame = root.requestAnimationFrame || ((fn) => root.setTimeout(fn, 50 / 3)) 11 | export let cancelAnimationFrame = root.cancelAnimationFrame || ((id) => root.clearTimeout(id)) 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | # github: dalisoft 4 | patreon: dalisoft # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # dalisoft # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # ['https://www.buymeacoffee.com/dalisoft', 'https://paypal.me/dalisoft'] 13 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": [ 5 | "jsdoc" 6 | ] 7 | }, 8 | "source": { 9 | "include": [ 10 | "src" 11 | ], 12 | "includePattern": ".js$", 13 | "excludePattern": "(node_modules/|logo|docs|bundled|examples|performance|.vscode|.github)" 14 | }, 15 | "plugins": [ 16 | "plugins/markdown" 17 | ], 18 | "templates": { 19 | "cleverLinks": false, 20 | "monospaceLinks": true 21 | }, 22 | "opts": { 23 | "destination": "../es6-tween-gh/docs/", 24 | "encoding": "utf8", 25 | "private": true, 26 | "recurse": true, 27 | "template": "./node_modules/docdash" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/regular-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Regular issue 3 | about: For regular issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | |Issue name|Issue description|Issue Demo URL 11 | |---|---|---| 12 | | | | | 13 | 14 | ### Related 15 | This issue is related to 16 | 17 | - [ ] README 18 | - [ ] package.json 19 | - [ ] Demos 20 | - [ ] Core features/functionality 21 | - [ ] Tween class 22 | - [ ] Easing 23 | - [ ] Interpolation 24 | - [ ] Timeline class 25 | - [ ] LICENSE 26 | - [ ] ABOUT COPYRIGHT VIOLATION (be careful before checking this...!) 27 | 28 | - or provide something else (from es6-tween files...) 29 | 30 | ### What you excepted? 31 | - Describe your result of research or idea... 32 | 33 | ### NOTE 34 | - Please add read code, docs, files, issues, PR list for avoid duplicating existing question, bug or something else... 35 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | add, 3 | onTick, 4 | autoPlay, 5 | get, 6 | getAll, 7 | has, 8 | isRunning, 9 | now, 10 | onRequestTick, 11 | FrameThrottle, 12 | ToggleLagSmoothing, 13 | Plugins, 14 | remove, 15 | removeAll, 16 | update 17 | } from './core' 18 | import Easing from './Easing' 19 | import Interpolation from './Interpolation' 20 | import Tween from './Tween' 21 | import Timeline from './Timeline' 22 | import Selector from './selector' 23 | import Interpolator from './Interpolator' 24 | import * as utils from './constants' 25 | import './shim' 26 | export { 27 | Plugins, 28 | Selector, 29 | Interpolator, 30 | onTick, 31 | has, 32 | get, 33 | getAll, 34 | removeAll, 35 | remove, 36 | add, 37 | now, 38 | update, 39 | autoPlay, 40 | isRunning, 41 | onRequestTick, 42 | FrameThrottle, 43 | ToggleLagSmoothing, 44 | Tween, 45 | Timeline, 46 | Easing, 47 | Interpolation, 48 | utils 49 | } 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/selector.js: -------------------------------------------------------------------------------- 1 | export default function (selector, collection, allowRaw) { 2 | if (!selector) { 3 | return null 4 | } 5 | 6 | const isGlobalScope = 7 | (typeof window !== 'undefined' && selector === window) || 8 | (typeof document !== 'undefined' && selector === document) 9 | 10 | if (collection) { 11 | return isGlobalScope 12 | ? [selector] 13 | : typeof selector === 'string' 14 | ? !!document.querySelectorAll && document.querySelectorAll(selector) 15 | : Array.isArray(selector) 16 | ? selector 17 | : selector.nodeType 18 | ? [selector] 19 | : allowRaw 20 | ? selector 21 | : [] 22 | } 23 | return isGlobalScope 24 | ? selector 25 | : typeof selector === 'string' 26 | ? !!document.querySelector && document.querySelector(selector) 27 | : Array.isArray(selector) 28 | ? selector[0] 29 | : selector.nodeType 30 | ? selector 31 | : allowRaw 32 | ? selector 33 | : null 34 | } 35 | -------------------------------------------------------------------------------- /withPage.js: -------------------------------------------------------------------------------- 1 | import puppeteer from 'puppeteer' 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | 6 | // Parse 7 | function describe (jsHandle) { 8 | return jsHandle.executionContext().evaluate((obj) => { 9 | // serialize |obj| however you want 10 | return JSON.stringify(obj) 11 | }, jsHandle) 12 | } 13 | 14 | export default (t, run) => { 15 | let _browser 16 | return puppeteer 17 | .launch({ headless: true, args: ['--no-sandbox'] }) 18 | .then((browser) => { 19 | _browser = browser 20 | return browser 21 | .newPage() 22 | .then((page) => { 23 | page.on('console', async (msg) => { 24 | const args = await Promise.all(msg.args().map((arg) => describe(arg))) 25 | console.log('Logs from Headless Chrome', ...args) 26 | }) 27 | return page.evaluate(fs.readFileSync(path.join(__dirname, 'bundled/Tween.js'), 'utf8')).then(() => page) 28 | }) 29 | .then((page) => run(t, page).then(() => page)) 30 | }) 31 | .then((page) => { 32 | _browser.close() 33 | page.close() 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 tween.js and es6-tween contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/NodeCache.js: -------------------------------------------------------------------------------- 1 | import { remove } from './core' 2 | 3 | export const Store = {} 4 | export default function (node, object, tween) { 5 | if (!node || !node.nodeType) { 6 | return object 7 | } 8 | const ID = node.queueID || 'q_' + Date.now() 9 | if (!node.queueID) { 10 | node.queueID = ID 11 | } 12 | const storeID = Store[ID] 13 | if (storeID) { 14 | if (storeID.object === object && node === storeID.tween.node && tween._startTime === storeID.tween._startTime) { 15 | remove(storeID.tween) 16 | } else if (typeof object === 'object' && !!object && !!storeID.object) { 17 | for (let prop in object) { 18 | if (prop in storeID.object) { 19 | if (tween._startTime === storeID.tween._startTime) { 20 | delete storeID.object[prop] 21 | } else { 22 | storeID.propNormaliseRequired = true 23 | } 24 | } 25 | } 26 | Object.assign(storeID.object, object) 27 | } 28 | return storeID.object 29 | } 30 | 31 | if (typeof object === 'object' && !!object) { 32 | Store[ID] = { 33 | tween, 34 | object, 35 | propNormaliseRequired: false 36 | } 37 | return Store[ID].object 38 | } 39 | 40 | return object 41 | } 42 | -------------------------------------------------------------------------------- /examples/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tween.js - Basic Demo 5 | 6 | 17 | 18 | 19 |
20 |
21 | 22 | 48 | 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | ## Before reporting a bug 4 | 5 | --- 6 | 7 | If you find something that you believe to be a bug, please 8 | 9 | 1. search the [issue tracker](https://github.com/tweenjs/es6-tween/issues) for similar issues 10 | 2. check out the [master](https://github.com/tweenjs/es6-tween/tree/master) branch and see if the bug still exists there. 11 | 12 | ## How to report a bug 13 | 14 | --- 15 | 16 | 1. Specify the revision number of the tween.js library where the bug occurred 17 | 2. Specify your browser version and operating system (i.e. Chrome 23.0.1271.95, Windows 7) 18 | 3. Describe the problem in detail. What happened? What did you expect to happen? 19 | 4. Provide a small test case (e.g. using [jsfiddle](http://jsfiddle.net)). Or if not possible, provide a link to a live version of your application. 20 | 21 | ## Contributing 22 | 23 | --- 24 | 25 | 1. Get a GitHub account (if you don't have one yet). 26 | 2. Fork the project in GitHub. 27 | 3. Check the [contribution guidelines](https://github.com/tweenjs/es6-tween/wiki/Contributing-to-tween.js). 28 | 4. Make changes to your clone of the repository 29 | 5. Submit a pull request. 30 | 31 | _If you tried all of the above and still can't fix the bug, or you're not sure you're doing things right, [let us know](https://github.com/tween.js/es6-tween/issues)._ 32 | -------------------------------------------------------------------------------- /src/Interpolator.js: -------------------------------------------------------------------------------- 1 | import { decompose, recompose, decomposeString } from './constants' 2 | 3 | /** 4 | * Tween helper for plugins 5 | * @namespace TWEEN.Interpolator 6 | * @memberof TWEEN 7 | * @param {any} a - Initial position 8 | * @param {any} b - End position 9 | * @return {Function} Returns function that accepts number between `0-1` 10 | */ 11 | const Interpolator = (a, b) => { 12 | let isArray = Array.isArray(a) && !a.isString 13 | let origin = typeof a === 'string' ? a : isArray ? a.slice() : { ...a } 14 | if (isArray) { 15 | for (let i = 0, len = a.length; i < len; i++) { 16 | if (a[i] !== b[i] || typeof a[i] !== 'number' || typeof b[i] === 'number') { 17 | decompose(i, origin, a, b) 18 | } 19 | } 20 | } else if (typeof a === 'object') { 21 | for (let i in a) { 22 | if (a[i] !== b[i] || typeof a[i] !== 'number' || typeof b[i] === 'number') { 23 | decompose(i, origin, a, b) 24 | } 25 | } 26 | } else if (typeof a === 'string') { 27 | a = decomposeString(a) 28 | b = decomposeString(b) 29 | 30 | let i = 1 31 | while (i < a.length) { 32 | if (a[i] === b[i] && typeof a[i - 1] === 'string') { 33 | a.splice(i - 1, 2, a[i - 1] + a[i]) 34 | b.splice(i - 1, 2, b[i - 1] + b[i]) 35 | } else { 36 | i++ 37 | } 38 | } 39 | } 40 | return (t) => { 41 | if (isArray) { 42 | for (let i = 0, len = a.length; i < len; i++) { 43 | recompose(i, origin, a, b, t) 44 | } 45 | } else if (typeof origin === 'object') { 46 | for (let i in a) { 47 | recompose(i, origin, a, b, t) 48 | } 49 | } else if (typeof origin === 'string') { 50 | origin = recompose(0, 0, a, b, t, t, true) 51 | } 52 | return origin 53 | } 54 | } 55 | 56 | export default Interpolator 57 | -------------------------------------------------------------------------------- /performance/gsap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GSAP - TweenMax 7 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /logo/box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | RealDRAW 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/PlaybackPosition.js: -------------------------------------------------------------------------------- 1 | export default class PlaybackPosition { 2 | constructor () { 3 | this.totalTime = 0 4 | this.labels = [] 5 | this.offsets = [] 6 | } 7 | parseLabel (name, offset) { 8 | const { offsets, labels } = this 9 | let i = labels.indexOf(name) 10 | if (typeof name === 'string' && name.indexOf('=') !== -1 && !offset && i === -1) { 11 | const rty = name.substr(name.indexOf('=') - 1, 2) 12 | const rt = name.split(rty) 13 | offset = rt.length === 2 ? rty + rt[1] : null 14 | name = rt[0] 15 | i = labels.indexOf(name) 16 | } 17 | if (i !== -1 && name) { 18 | let currOffset = offsets[i] || 0 19 | if (typeof offset === 'number') { 20 | currOffset = offset 21 | } else if (typeof offset === 'string') { 22 | if (offset.indexOf('=') !== -1) { 23 | const type = offset.charAt(0) 24 | offset = Number(offset.substr(2)) 25 | if (type === '+' || type === '-') { 26 | currOffset += parseFloat(type + offset) 27 | } else if (type === '*') { 28 | currOffset *= offset 29 | } else if (type === '/') { 30 | currOffset /= offset 31 | } else if (type === '%') { 32 | currOffset *= offset / 100 33 | } 34 | } 35 | } 36 | return currOffset 37 | } 38 | return typeof offset === 'number' ? offset : 0 39 | } 40 | addLabel (name, offset) { 41 | this.labels.push(name) 42 | this.offsets.push(this.parseLabel(name, offset)) 43 | return this 44 | } 45 | setLabel (name, offset) { 46 | const i = this.labels.indexOf(name) 47 | if (i !== -1) { 48 | this.offsets.splice(i, 1, this.parseLabel(name, offset)) 49 | } 50 | return this 51 | } 52 | eraseLabel (name) { 53 | const i = this.labels.indexOf(name) 54 | if (i !== -1) { 55 | this.labels.splice(i, 1) 56 | this.offsets.splice(i, 1) 57 | } 58 | return this 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /performance/kute.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | KUTE.js - Core only 7 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | es6-tween 7 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /performance/es6-tween.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | es6-tween 7 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es6-tween", 3 | "version": "5.5.11", 4 | "description": "ES6 implementation of amazing tween.js", 5 | "browser": "bundled/Tween.min.js", 6 | "cdn": "bundled/Tween.min.js", 7 | "main": "bundled/Tween.js", 8 | "module": "src/index.js", 9 | "directories": { 10 | "example": "examples" 11 | }, 12 | "funding": { 13 | "type": "patreon", 14 | "url": "https://www.patreon.com/dalisoft" 15 | }, 16 | "scripts": { 17 | "source": "npx rollup -c", 18 | "minify": "npx uglifyjs bundled/Tween.js -c -m -o bundled/Tween.min.js --source-map \"filename='bundled/Tween.min.js.map'\"", 19 | "build": "npm run source && npm run minify", 20 | "dev": "npx rollup -c -w", 21 | "prepublishOnly": "npm run lint && npm run build", 22 | "doc": "npx jsdoc --readme README.md --configure jsdoc.json", 23 | "doc-md": "npx jsdoc2md src/** > API.md", 24 | "test": "npm run lint && npm run source && npx ava", 25 | "lint": "npx eslint ./src", 26 | "lint-fix": "npx eslint ./src --fix" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/tweenjs/es6-tween.git" 31 | }, 32 | "keywords": [ 33 | "tween", 34 | "tweening", 35 | "es6", 36 | "numeric", 37 | "interpolation", 38 | "easing", 39 | "emit" 40 | ], 41 | "author": "es6-tween contributors", 42 | "license": "MIT", 43 | "bugs": { 44 | "url": "https://github.com/tweenjs/es6-tween/issues" 45 | }, 46 | "homepage": "https://es6-tween.js.org", 47 | "devDependencies": { 48 | "@babel/core": "^7.4.4", 49 | "@babel/plugin-proposal-class-properties": "^7.4.4", 50 | "@babel/preset-env": "^7.4.4", 51 | "ava": "^5.1.0", 52 | "docdash": "^1.1.0", 53 | "eslint": "^5.16.0", 54 | "eslint-config-standard": "^12.0.0", 55 | "eslint-plugin-import": "^2.17.2", 56 | "eslint-plugin-node": "^9.0.1", 57 | "eslint-plugin-promise": "^4.1.1", 58 | "eslint-plugin-standard": "^4.0.0", 59 | "jsdoc": "^3.6.2", 60 | "jsdoc-to-markdown": "^8.0.0", 61 | "puppeteer": "^1.16.0", 62 | "rollup": "^1.12.1", 63 | "rollup-plugin-babel": "^4.3.2", 64 | "uglify-js": "^3.5.12" 65 | }, 66 | "dependencies": {}, 67 | "ava": { 68 | "verbose": true, 69 | "require": [ 70 | "esm" 71 | ], 72 | "babel": { 73 | "extensions": [ 74 | "js" 75 | ] 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | 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. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | 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. 28 | 29 | 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. 30 | 31 | ## Scope 32 | 33 | 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. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dalisoft@mail.ru. The project team will review and investigate all complaints, and will respond in a way that it deems 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. 38 | 39 | 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. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/Easing.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of full easings 3 | * @namespace TWEEN.Easing 4 | * @example 5 | * import {Tween, Easing} from 'es6-tween' 6 | * 7 | * // then set via new Tween({x:0}).to({x:100}, 1000).easing(Easing.Quadratic.InOut).start() 8 | */ 9 | const Easing = { 10 | Linear: { 11 | None (k) { 12 | return k 13 | } 14 | }, 15 | 16 | Quadratic: { 17 | In (k) { 18 | return Math.pow(k, 2) 19 | }, 20 | 21 | Out (k) { 22 | return k * (2 - k) 23 | }, 24 | 25 | InOut (k) { 26 | if ((k *= 2) < 1) { 27 | return 0.5 * Math.pow(k, 2) 28 | } 29 | 30 | return -0.5 * (--k * (k - 2) - 1) 31 | } 32 | }, 33 | 34 | Cubic: { 35 | In (k) { 36 | return Math.pow(k, 3) 37 | }, 38 | 39 | Out (k) { 40 | return --k * k * k + 1 41 | }, 42 | 43 | InOut (k) { 44 | if ((k *= 2) < 1) { 45 | return 0.5 * Math.pow(k, 3) 46 | } 47 | 48 | return 0.5 * ((k -= 2) * k * k + 2) 49 | } 50 | }, 51 | 52 | Quartic: { 53 | In (k) { 54 | return Math.pow(k, 4) 55 | }, 56 | 57 | Out (k) { 58 | return 1 - --k * k * k * k 59 | }, 60 | 61 | InOut (k) { 62 | if ((k *= 2) < 1) { 63 | return 0.5 * Math.pow(k, 4) 64 | } 65 | 66 | return -0.5 * ((k -= 2) * k * k * k - 2) 67 | } 68 | }, 69 | 70 | Quintic: { 71 | In (k) { 72 | return Math.pow(k, 5) 73 | }, 74 | 75 | Out (k) { 76 | return --k * k * k * k * k + 1 77 | }, 78 | 79 | InOut (k) { 80 | if ((k *= 2) < 1) { 81 | return 0.5 * Math.pow(k, 5) 82 | } 83 | 84 | return 0.5 * ((k -= 2) * k * k * k * k + 2) 85 | } 86 | }, 87 | 88 | Sinusoidal: { 89 | In (k) { 90 | return 1 - Math.cos((k * Math.PI) / 2) 91 | }, 92 | 93 | Out (k) { 94 | return Math.sin((k * Math.PI) / 2) 95 | }, 96 | 97 | InOut (k) { 98 | return 0.5 * (1 - Math.cos(Math.PI * k)) 99 | } 100 | }, 101 | 102 | Exponential: { 103 | In (k) { 104 | return k === 0 ? 0 : Math.pow(1024, k - 1) 105 | }, 106 | 107 | Out (k) { 108 | return k === 1 ? 1 : 1 - Math.pow(2, -10 * k) 109 | }, 110 | 111 | InOut (k) { 112 | if (k === 0) { 113 | return 0 114 | } 115 | 116 | if (k === 1) { 117 | return 1 118 | } 119 | 120 | if ((k *= 2) < 1) { 121 | return 0.5 * Math.pow(1024, k - 1) 122 | } 123 | 124 | return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2) 125 | } 126 | }, 127 | 128 | Circular: { 129 | In (k) { 130 | return 1 - Math.sqrt(1 - k * k) 131 | }, 132 | 133 | Out (k) { 134 | return Math.sqrt(1 - --k * k) 135 | }, 136 | 137 | InOut (k) { 138 | if ((k *= 2) < 1) { 139 | return -0.5 * (Math.sqrt(1 - k * k) - 1) 140 | } 141 | 142 | return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1) 143 | } 144 | }, 145 | 146 | Elastic: { 147 | In (k) { 148 | if (k === 0) { 149 | return 0 150 | } 151 | 152 | if (k === 1) { 153 | return 1 154 | } 155 | 156 | return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) 157 | }, 158 | 159 | Out (k) { 160 | if (k === 0) { 161 | return 0 162 | } 163 | 164 | if (k === 1) { 165 | return 1 166 | } 167 | 168 | return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1 169 | }, 170 | 171 | InOut (k) { 172 | if (k === 0) { 173 | return 0 174 | } 175 | 176 | if (k === 1) { 177 | return 1 178 | } 179 | 180 | k *= 2 181 | 182 | if (k < 1) { 183 | return -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) 184 | } 185 | 186 | return 0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1 187 | } 188 | }, 189 | 190 | Back: { 191 | In (k) { 192 | const s = 1.70158 193 | 194 | return k * k * ((s + 1) * k - s) 195 | }, 196 | 197 | Out (k) { 198 | const s = 1.70158 199 | 200 | return --k * k * ((s + 1) * k + s) + 1 201 | }, 202 | 203 | InOut (k) { 204 | const s = 1.70158 * 1.525 205 | 206 | if ((k *= 2) < 1) { 207 | return 0.5 * (k * k * ((s + 1) * k - s)) 208 | } 209 | 210 | return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2) 211 | } 212 | }, 213 | 214 | Bounce: { 215 | In (k) { 216 | return 1 - Easing.Bounce.Out(1 - k) 217 | }, 218 | 219 | Out (k) { 220 | let x = 2.75 221 | let y = 7.5625 222 | if (k < 1 / x) { 223 | return y * k * k 224 | } else if (k < 2 / x) { 225 | return y * (k -= 1.5 / x) * k + 0.75 226 | } else if (k < 2.5 / x) { 227 | return y * (k -= 2.25 / x) * k + 0.9375 228 | } else { 229 | return y * (k -= 2.625 / x) * k + 0.984375 230 | } 231 | }, 232 | 233 | InOut (k) { 234 | if (k < 0.5) { 235 | return Easing.Bounce.In(k * 2) * 0.5 236 | } 237 | 238 | return Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5 239 | } 240 | }, 241 | 242 | Stepped: { 243 | steps: (steps) => (k) => ((k * steps) | 0) / steps 244 | } 245 | } 246 | 247 | export default Easing 248 | -------------------------------------------------------------------------------- /src/Interpolation.js: -------------------------------------------------------------------------------- 1 | import { isRGBColor, RGBA, STRING_PROP } from './constants' 2 | 3 | /** 4 | * List of full Interpolation 5 | * @namespace TWEEN.Interpolation 6 | * @example 7 | * import {Interpolation, Tween} from 'es6-tween' 8 | * 9 | * let bezier = Interpolation.Bezier 10 | * new Tween({x:0}).to({x:[0, 4, 8, 12, 15, 20, 30, 40, 20, 40, 10, 50]}, 1000).interpolation(bezier).start() 11 | * @memberof TWEEN 12 | */ 13 | const Interpolation = { 14 | Linear (v, k, value) { 15 | const m = v.length - 1 16 | const f = m * k 17 | const i = Math.floor(f) 18 | const fn = Interpolation.Utils.Linear 19 | 20 | if (k < 0) { 21 | return fn(v[0], v[1], f, value) 22 | } 23 | if (k > 1) { 24 | return fn(v[m], v[m - 1], m - f, value) 25 | } 26 | return fn(v[i], v[i + 1 > m ? m : i + 1], f - i, value) 27 | }, 28 | 29 | Bezier (v, k, value) { 30 | let b = Interpolation.Utils.Reset(value) 31 | let n = v.length - 1 32 | let pw = Math.pow 33 | let fn = Interpolation.Utils.Bernstein 34 | 35 | let isBArray = Array.isArray(b) 36 | 37 | for (let i = 0; i <= n; i++) { 38 | if (typeof b === 'number') { 39 | b += pw(1 - k, n - i) * pw(k, i) * v[i] * fn(n, i) 40 | } else if (isBArray) { 41 | for (let p = 0, len = b.length; p < len; p++) { 42 | if (typeof b[p] === 'number') { 43 | b[p] += pw(1 - k, n - i) * pw(k, i) * v[i][p] * fn(n, i) 44 | } else { 45 | b[p] = v[i][p] 46 | } 47 | } 48 | } else if (typeof b === 'object') { 49 | for (let p in b) { 50 | if (typeof b[p] === 'number') { 51 | b[p] += pw(1 - k, n - i) * pw(k, i) * v[i][p] * fn(n, i) 52 | } else { 53 | b[p] = v[i][p] 54 | } 55 | } 56 | } else if (typeof b === 'string') { 57 | let STRING_BUFFER = '' 58 | let idx = Math.round(n * k) 59 | let vCurr = v[idx] 60 | for (let ks = 1, len = vCurr.length; ks < len; ks++) { 61 | STRING_BUFFER += vCurr[ks] 62 | } 63 | return STRING_BUFFER 64 | } 65 | } 66 | 67 | return b 68 | }, 69 | 70 | CatmullRom (v, k, value) { 71 | const m = v.length - 1 72 | let f = m * k 73 | let i = Math.floor(f) 74 | const fn = Interpolation.Utils.CatmullRom 75 | 76 | if (v[0] === v[m]) { 77 | if (k < 0) { 78 | i = Math.floor((f = m * (1 + k))) 79 | } 80 | 81 | return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i, value) 82 | } else { 83 | if (k < 0) { 84 | return fn(v[1], v[1], v[0], v[0], -k, value) 85 | } 86 | 87 | if (k > 1) { 88 | return fn(v[m - 1], v[m - 1], v[m], v[m], (k | 0) - k, value) 89 | } 90 | 91 | return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i, value) 92 | } 93 | }, 94 | 95 | Utils: { 96 | Linear (p0, p1, t, v) { 97 | if (p0 === p1 || typeof p0 === 'string') { 98 | // Quick return for performance reason 99 | if (p1.length && p1.splice && p1.isString) { 100 | p1 = '' 101 | for (let i = 0, len = p0.length; i < len; i++) { 102 | p1 += p0[i] 103 | } 104 | } 105 | return p1 106 | } else if (typeof p0 === 'number') { 107 | return typeof p0 === 'function' ? p0(t) : p0 + (p1 - p0) * t 108 | } else if (typeof p0 === 'object') { 109 | if (p0.length !== undefined) { 110 | const isForceStringProp = typeof p0[0] === 'string' || p0.isString 111 | if (isForceStringProp || p0[0] === STRING_PROP) { 112 | let STRING_BUFFER = '' 113 | for (let i = isForceStringProp ? 0 : 1, len = p0.length; i < len; i++) { 114 | let currentValue = 115 | t === 0 ? p0[i] : t === 1 ? p1[i] : typeof p0[i] === 'number' ? p0[i] + (p1[i] - p0[i]) * t : p1[i] 116 | if ((t > 0 && t < 1 && isRGBColor(p0, i)) || isRGBColor(p0, i, RGBA)) { 117 | currentValue |= 0 118 | } 119 | STRING_BUFFER += currentValue 120 | } 121 | return STRING_BUFFER 122 | } else if (v && v.length && v.splice) { 123 | for (let p = 0, len = v.length; p < len; p++) { 124 | v[p] = Interpolation.Utils.Linear(p0[p], p1[p], t, v[p]) 125 | } 126 | } 127 | } else { 128 | for (const p in v) { 129 | v[p] = Interpolation.Utils.Linear(p0[p], p1[p], t, v[p]) 130 | } 131 | } 132 | return v 133 | } 134 | }, 135 | 136 | Reset (value) { 137 | if (Array.isArray(value)) { 138 | for (let i = 0, len = value.length; i < len; i++) { 139 | value[i] = Interpolation.Utils.Reset(value[i]) 140 | } 141 | return value 142 | } else if (typeof value === 'object') { 143 | for (let i in value) { 144 | value[i] = Interpolation.Utils.Reset(value[i]) 145 | } 146 | return value 147 | } else if (typeof value === 'number') { 148 | return 0 149 | } 150 | return value 151 | }, 152 | 153 | Bernstein (n, i) { 154 | const fc = Interpolation.Utils.Factorial 155 | 156 | return fc(n) / fc(i) / fc(n - i) 157 | }, 158 | 159 | Factorial: (function () { 160 | const a = [1] 161 | 162 | return (n) => { 163 | let s = 1 164 | 165 | if (a[n]) { 166 | return a[n] 167 | } 168 | 169 | for (let i = n; i > 1; i--) { 170 | s *= i 171 | } 172 | 173 | a[n] = s 174 | return s 175 | } 176 | })(), 177 | 178 | CatmullRom (p0, p1, p2, p3, t, v) { 179 | if (typeof p0 === 'string') { 180 | return p1 181 | } else if (typeof p0 === 'number') { 182 | const v0 = (p2 - p0) * 0.5 183 | const v1 = (p3 - p1) * 0.5 184 | const t2 = t * t 185 | const t3 = t * t2 186 | 187 | return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1 188 | } else if (typeof p0 === 'object') { 189 | if (p0.length !== undefined) { 190 | if (p0[0] === STRING_PROP) { 191 | let STRING_BUFFER = '' 192 | for (let i = 1, len = p0.length; i < len; i++) { 193 | let currentValue = 194 | typeof p0[i] === 'number' ? Interpolation.Utils.CatmullRom(p0[i], p1[i], p2[i], p3[i], t) : p3[i] 195 | if (isRGBColor(p0, i) || isRGBColor(p0, i, RGBA)) { 196 | currentValue |= 0 197 | } 198 | STRING_BUFFER += currentValue 199 | } 200 | return STRING_BUFFER 201 | } 202 | for (let p = 0, len = v.length; p < len; p++) { 203 | v[p] = Interpolation.Utils.CatmullRom(p0[p], p1[p], p2[p], p3[p], t, v[p]) 204 | } 205 | } else { 206 | for (const p in v) { 207 | v[p] = Interpolation.Utils.CatmullRom(p0[p], p1[p], p2[p], p3[p], t, v[p]) 208 | } 209 | } 210 | return v 211 | } 212 | } 213 | } 214 | } 215 | 216 | export default Interpolation 217 | -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | /* global process */ 2 | import { requestAnimationFrame, cancelAnimationFrame, root } from './shim' 3 | 4 | /** 5 | * Get browser/Node.js current time-stamp 6 | * @return Normalised current time-stamp in milliseconds 7 | * @memberof TWEEN 8 | * @example 9 | * TWEEN.now 10 | */ 11 | const now = (function () { 12 | if ( 13 | typeof process !== 'undefined' && 14 | process.hrtime !== undefined && 15 | (!process.versions || process.versions.electron === undefined) 16 | ) { 17 | return function () { 18 | const time = process.hrtime() 19 | 20 | // Convert [seconds, nanoseconds] to milliseconds. 21 | return time[0] * 1000 + time[1] / 1000000 22 | } 23 | // In a browser, use window.performance.now if it is available. 24 | } else if (root.performance !== undefined && root.performance.now !== undefined) { 25 | // This must be bound, because directly assigning this function 26 | // leads to an invocation exception in Chrome. 27 | return root.performance.now.bind(root.performance) 28 | // Use Date.now if it is available. 29 | } else { 30 | const offset = 31 | root.performance && root.performance.timing && root.performance.timing.navigationStart 32 | ? root.performance.timing.navigationStart 33 | : Date.now() 34 | return function () { 35 | return Date.now() - offset 36 | } 37 | } 38 | })() 39 | 40 | /** 41 | * Lightweight, effecient and modular ES6 version of tween.js 42 | * @copyright 2019 @dalisoft and es6-tween contributors 43 | * @license MIT 44 | * @namespace TWEEN 45 | * @example 46 | * // ES6 47 | * const {add, remove, isRunning, autoPlay} = TWEEN 48 | */ 49 | const _tweens = [] 50 | let isStarted = false 51 | let _autoPlay = false 52 | let _onRequestTick = [] 53 | const _ticker = requestAnimationFrame 54 | let emptyFrame = 0 55 | let powerModeThrottle = 120 56 | let _tick 57 | let handleLag = true 58 | 59 | const onRequestTick = (fn) => { 60 | _onRequestTick.push(fn) 61 | } 62 | 63 | const _requestTick = () => { 64 | for (let i = 0; i < _onRequestTick.length; i++) { 65 | _onRequestTick[i]() 66 | } 67 | } 68 | 69 | /** 70 | * Adds tween to list 71 | * @param {Tween} tween Tween instance 72 | * @memberof TWEEN 73 | * @example 74 | * let tween = new Tween({x:0}) 75 | * tween.to({x:200}, 1000) 76 | * TWEEN.add(tween) 77 | */ 78 | const add = (tween) => { 79 | let i = _tweens.indexOf(tween) 80 | 81 | if (i > -1) { 82 | _tweens.splice(i, 1) 83 | } 84 | 85 | _tweens.push(tween) 86 | 87 | emptyFrame = 0 88 | 89 | if (_autoPlay && !isStarted) { 90 | _tick = _ticker(update) 91 | isStarted = true 92 | } 93 | } 94 | 95 | /** 96 | * Adds ticker like event 97 | * @param {Function} fn callback 98 | * @memberof TWEEN 99 | * @example 100 | * TWEEN.onTick(time => console.log(time)) 101 | */ 102 | const onTick = (fn) => _tweens.push({ update: fn }) 103 | 104 | /** 105 | * Sets after how much frames empty updating should stop 106 | * @param {number} frameCount=120 count of frames that should stop after all tweens removed 107 | * @memberof TWEEN 108 | * @example 109 | * TWEEN.FrameThrottle(60) 110 | */ 111 | const FrameThrottle = (frameCount = 120) => { 112 | powerModeThrottle = frameCount * 1.05 113 | } 114 | 115 | /** 116 | * Handle lag, useful if you have rendering Canvas or DOM objects or using es6-tween plugins 117 | * @param {number} state=true handle lag state 118 | * @memberof TWEEN 119 | * @example 120 | * TWEEN.ToggleLagSmoothing(false) 121 | */ 122 | const ToggleLagSmoothing = (_state = true) => { 123 | handleLag = _state 124 | } 125 | 126 | /** 127 | * @returns {Array} List of tweens in Array 128 | * @memberof TWEEN 129 | * TWEEN.getAll() // list of tweens 130 | */ 131 | const getAll = () => _tweens 132 | 133 | /** 134 | * Runs update loop automaticlly 135 | * @param {Boolean} state State of auto-run of update loop 136 | * @example TWEEN.autoPlay(true) 137 | * @memberof TWEEN 138 | */ 139 | const autoPlay = (state) => { 140 | _autoPlay = state 141 | } 142 | 143 | /** 144 | * Removes all tweens from list 145 | * @example TWEEN.removeAll() // removes all tweens, stored in global tweens list 146 | * @memberof TWEEN 147 | */ 148 | const removeAll = () => { 149 | _tweens.length = 0 150 | cancelAnimationFrame(_tick) 151 | isStarted = false 152 | } 153 | 154 | /** 155 | * @param {Tween} tween Tween Instance to be matched 156 | * @return {Tween} Matched tween 157 | * @memberof TWEEN 158 | * @example 159 | * TWEEN.get(tween) 160 | */ 161 | const get = (tween) => { 162 | for (let i = 0; i < _tweens.length; i++) { 163 | if (tween === _tweens[i]) { 164 | return _tweens[i] 165 | } 166 | } 167 | 168 | return null 169 | } 170 | 171 | /** 172 | * @param {Tween} tween Tween Instance to be matched 173 | * @return {Boolean} Status of Exists tween or not 174 | * @memberof TWEEN 175 | * @example 176 | * TWEEN.has(tween) 177 | */ 178 | const has = (tween) => { 179 | return get(tween) !== null 180 | } 181 | /** 182 | * Removes tween from list 183 | * @param {Tween} tween Tween instance 184 | * @memberof TWEEN 185 | * @example 186 | * TWEEN.remove(tween) 187 | */ 188 | const remove = (tween) => { 189 | const i = _tweens.indexOf(tween) 190 | if (i !== -1) { 191 | _tweens.splice(i, 1) 192 | } 193 | if (_tweens.length === 0) { 194 | cancelAnimationFrame(_tick) 195 | isStarted = false 196 | } 197 | } 198 | 199 | /** 200 | * Updates global tweens by given time 201 | * @param {number=} time Timestamp 202 | * @param {Boolean=} preserve Prevents tween to be removed after finish 203 | * @memberof TWEEN 204 | * @example 205 | * TWEEN.update(500) 206 | */ 207 | 208 | const update = (time = now(), preserve) => { 209 | if (emptyFrame >= powerModeThrottle && handleLag) { 210 | isStarted = false 211 | emptyFrame = 0 212 | cancelAnimationFrame(_tick) 213 | return false 214 | } 215 | 216 | if (_autoPlay && isStarted) { 217 | _tick = _ticker(update) 218 | } else { 219 | _requestTick() 220 | } 221 | 222 | if (!_tweens.length) { 223 | emptyFrame++ 224 | } 225 | 226 | let i = 0 227 | let length = _tweens.length 228 | while (i < length) { 229 | _tweens[i++].update(time, preserve) 230 | 231 | if (length > _tweens.length) { 232 | // The tween has been removed, keep same index 233 | i-- 234 | } 235 | 236 | length = _tweens.length 237 | } 238 | 239 | return true 240 | } 241 | 242 | /** 243 | * The state of ticker running 244 | * @return {Boolean} Status of running updates on all tweens 245 | * @memberof TWEEN 246 | * @example TWEEN.isRunning() 247 | */ 248 | const isRunning = () => isStarted 249 | 250 | /** 251 | * Returns state of lag smoothing handling 252 | * @return {Boolean} Status of lag smoothing state 253 | * @memberof TWEEN 254 | * @example TWEEN.isRunning() 255 | */ 256 | const isLagSmoothing = () => handleLag 257 | 258 | /** 259 | * The plugins store object 260 | * @namespace TWEEN.Plugins 261 | * @memberof TWEEN 262 | * @example 263 | * let num = Plugins.num = function (node, start, end) { 264 | * return t => start + (end - start) * t 265 | * } 266 | * 267 | * @static 268 | */ 269 | const Plugins = {} 270 | 271 | export { 272 | Plugins, 273 | get, 274 | has, 275 | getAll, 276 | removeAll, 277 | remove, 278 | add, 279 | now, 280 | update, 281 | autoPlay, 282 | onTick, 283 | onRequestTick, 284 | isRunning, 285 | isLagSmoothing, 286 | FrameThrottle, 287 | ToggleLagSmoothing 288 | } 289 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # es6-tween 2 | 3 | ## This project development suspended due of no support from community and no financial support to author 4 | 5 | - High-performant animations without headaches 6 | - Simple, modular and functional animation library for web and node 7 | - Tweening library that needs to use where performance matter 8 | - Flexible, extendable, modular and resource-efficient tweening library 9 | 10 | [![NPM Min Size][npm-min-size]][unpkg-url] 11 | [![NPM Gzip Size][npm-gzip-size]][unpkg-url] 12 | [![CDNJS][cdnjs-image]][cdnjs-url] 13 | [![NPM Version][npm-image]][npm-url] 14 | [![NPM Downloads][downloads-image]][npm-url] 15 | [![license](https://img.shields.io/github/license/tweenjs/es6-tween.svg)]() 16 | [![Travis tests][travis-image]][travis-url] 17 |
18 | [![NPM](https://nodei.co/npm/es6-tween.png?downloads=true&stars=true)](https://nodei.co/npm/es6-tween/) 19 | 20 | ## Frameworks 21 | 22 | - [react-es6-tween](https://github.com/dalisoft/react-es6-tween) 23 | 24 | ## Docs 25 | 26 | - [API documentation](./API.md) 27 | - [Wiki page](https://github.com/tweenjs/es6-tween/wiki) 28 | 29 | ```javascript 30 | TWEEN.autoPlay(true); // simplify the your code 31 | 32 | let coords = { x: 0, y: 0 }; 33 | let tween = new TWEEN.Tween(coords) 34 | .to({ x: 100, y: 100 }, 1000) 35 | .on('update', ({ x, y }) => { 36 | console.log(`The values is x: ${x} and y: ${y}`); 37 | }) 38 | .start(); 39 | ``` 40 | 41 | ## Plugins 42 | 43 | Starting at `v3`, we provide excluded plugins from core, so our core becomes lighter and faster. [Here our plugins list](https://www.npmjs.com/browse/keyword/es6-tween) 44 | 45 | ## Demos 46 | 47 | - Demo #1 [Morphing SVG Shape + Cross-browser SVG Transform](https://codepen.io/dalisoft/pen/mMJmxX) 48 | - Demo #2 [Morphing SVG Shape](https://codepen.io/dalisoft/pen/BdLydv) 49 | - Collection on the [Codepen](https://codepen.io/collection/DapBmv/) 50 | 51 | ## Installation 52 | 53 | Download the [library](https://unpkg.com/es6-tween/bundled/Tween.js) and include it in your code: 54 | 55 | ```html 56 | 57 | ``` 58 | 59 | ### CDN-Hosted version 60 | 61 | - See [cdnjs-hosted version](https://cdnjs.com/libraries/es6-tween) for get which result you want 62 | - NOTE: `@latest` suffix sometimes saves life by loading latest, because sometimes CDN services will not load the latest 63 | 64 | - Now you can load from CDN 65 | 66 | ```html 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | ``` 76 | 77 | ### More advanced users might want to 78 | 79 | #### Using `import` 80 | 81 | ```javascript 82 | import { Easing, Tween, autoPlay } from 'es6-tween'; 83 | ``` 84 | 85 | #### Using [getlibs](https://github.com/activewidgets/getlibs) 86 | 87 | ```html 88 | 89 | 97 | ``` 98 | 99 | #### Using `npm` or `yarn` 100 | 101 | ```bash 102 | $ yarn add es6-tween 103 | # or 104 | $ npm install es6-tween 105 | ``` 106 | 107 | Then include the Tween.js module with the standard node.js `require`: 108 | 109 | ```javascript 110 | const { Tween, Easing, autoPlay } = require('es6-tween'); 111 | ``` 112 | 113 | And you can use Tween.js as in all other examples--for example: 114 | 115 | ```javascript 116 | const t = new Tween(/* etc */); 117 | t.start(); 118 | ``` 119 | 120 | You can run script commands to build modules into single `UMD` compatible file: 121 | 122 | #### Using commands 123 | 124 | ```bash 125 | $ yarn build # builds production files 126 | # or 127 | $ yarn dev # builds and watchs development files 128 | ``` 129 | 130 | Then reference the library source: 131 | 132 | ```html 133 | 134 | ``` 135 | 136 | ## Features 137 | 138 | - Tweens everything you give them, string, number, number of arrays, number of object, all-to, interpolators and much more. Endless possibilites 139 | - Can use CSS units (e.g. appending `px`) 140 | - Can interpolate colours 141 | - Easing functions are reusable outside of Tween 142 | - Can also use custom easing functions 143 | - Much of easings 144 | 145 | ## Compatiblity Testing 146 | 147 | Thanks to BrowserStack for providing us testing in a real devices to make it cross-browser, bug-free and better. 148 | BrowserStack saved my countless hours, before i spent on testing much of time, now it's very easy. I recommend to others use this service. 149 | I sure, BrowserStack helps us to make it, so i am linking to BrowserStack as our sponsor. 150 | [Browser Stack Logo](https://www.browserstack.com/) 151 | 152 | ## Tests 153 | 154 | ```bash 155 | yarn test 156 | ``` 157 | 158 | or you can go [here](https://travis-ci.org/tweenjs/es6-tween) for more information, tests and etc... 159 | 160 | every time you want to run the tests. 161 | 162 | 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. 163 | 164 | ## People 165 | 166 | - [All contributors](https://github.com/tweenjs/es6-tween/contributors). 167 | - [es6-tween contributors](https://github.com/tweenjs/es6-tween/graphs/contributors)/ 168 | 169 | ## Thanks to 170 | 171 | these tools developers and to their community and without these tools maybe this library wouldn't be possible 172 | 173 | - [GitHub](https://github.com/) 174 | - [Travis CI](http://travis-ci.org) 175 | - [BrowserStack](https://www.browserstack.com/) 176 | - [Node.js](https://nodejs.org/en/) 177 | - [ESLint](http://eslint.org) 178 | - [jsDoc](http://usejsdoc.org) ([docdash theme](https://github.com/clenemt/docdash)) 179 | - [Rollup](https://rollupjs.org/guide/en) 180 | - [Babel](https://babeljs.io) 181 | - [Ava](https://github.com/avajs/ava) 182 | - [Puppeteer](https://pptr.dev) 183 | - [UglifyJS v3](https://github.com/mishoo/UglifyJS2) 184 | - [Husky](https://github.com/typicode/husky) 185 | 186 | ## Projects using es6-tween 187 | 188 | - [ft](https://github.com/2players/ft) 189 | - [react-heartwood-components](https://www.npmjs.com/package/@sprucelabs/react-heartwood-components) 190 | - [el-controls](https://github.com/eljs/el-controls) 191 | - [lightweight-pixijs-engine](https://github.com/dgzornoza/lightweight-pixijs-engine#readme) 192 | - [vue-sliderx](https://www.npmjs.com/package/vue-sliderx) 193 | - [vue-mapbox-feature](https://cityseer.github.io/vue-mapbox-feature) 194 | - [vuxtras](https://github.com/homerjam/vuxtras#readme) 195 | - [Slye](https://github.com/Slye3D/slye#readme) 196 | - [react-3d-globe](https://chrisrzhou.github.io/react-3d-globe/) 197 | 198 | It's great to see this library to be used in production and/or library, thank you! 199 | 200 | If you have projects using es6-tween, please make issue or PR, i will add here your project too :) 201 | 202 | [npm-min-size]: https://img.shields.io/bundlephobia/min/es6-tween.svg 203 | [npm-gzip-size]: https://img.badgesize.io/https://unpkg.com/es6-tween?compression=gzip 204 | [npm-image]: https://img.shields.io/npm/v/es6-tween.svg 205 | [npm-url]: https://npmjs.org/package/es6-tween 206 | [downloads-image]: https://img.shields.io/npm/dm/es6-tween.svg 207 | [travis-image]: https://travis-ci.org/tweenjs/es6-tween.svg?branch=master 208 | [travis-url]: https://travis-ci.org/tweenjs/es6-tween 209 | [cdnjs-image]: https://img.shields.io/cdnjs/v/es6-tween.svg 210 | [cdnjs-url]: https://cdnjs.com/libraries/es6-tween 211 | [unpkg-url]: https://unpkg.com/es6-tween 212 | -------------------------------------------------------------------------------- /src/Timeline.js: -------------------------------------------------------------------------------- 1 | import { add, now, remove, isRunning, isLagSmoothing } from './core' 2 | import PlaybackPosition from './PlaybackPosition' 3 | import Tween from './Tween' 4 | import { 5 | EVENT_START, 6 | EVENT_COMPLETE, 7 | EVENT_REPEAT, 8 | EVENT_REVERSE, 9 | EVENT_RESTART, 10 | EVENT_UPDATE, 11 | FRAME_MS, 12 | TOO_LONG_FRAME_MS 13 | } from './constants' 14 | import Selector from './selector' 15 | 16 | export const shuffle = (a) => { 17 | let j 18 | let x 19 | let i 20 | for (i = a.length; i; i -= 1) { 21 | j = Math.floor(Math.random() * i) 22 | x = a[i - 1] 23 | a[i - 1] = a[j] 24 | a[j] = x 25 | } 26 | return a 27 | } 28 | 29 | let _id = 0 30 | /** 31 | * Timeline main constructor. 32 | * 33 | * It works same as `Tween` instance, using `.repeat`, `.restart` or `etc` works like a `Tween`, so please see `Tween` class for methods 34 | * @constructor 35 | * @class 36 | * @namespace TWEEN.Timeline 37 | * @param {Object=} params Default params for new tweens 38 | * @example let tl = new Timeline({delay:200}) 39 | * @extends Tween 40 | */ 41 | class Timeline extends Tween { 42 | constructor (params) { 43 | super() 44 | this._duration = 0 45 | this._startTime = params && params.startTime !== undefined ? params.startTime : now() 46 | this._tweens = [] 47 | this.elapsed = 0 48 | this._id = _id++ 49 | this._defaultParams = params 50 | this.position = new PlaybackPosition() 51 | this.position.addLabel('afterLast', this._duration) 52 | this.position.addLabel('afterInit', this._startTime) 53 | this._onStartCallbackFired = false 54 | 55 | return this 56 | } 57 | mapTotal (fn) { 58 | fn.call(this, this._tweens) 59 | return this 60 | } 61 | timingOrder (fn) { 62 | const timing = fn(this._tweens.map((t) => t._startTime)) 63 | this._tweens.map((tween, i) => { 64 | tween._startTime = timing[i] 65 | }) 66 | return this 67 | } 68 | getTiming (mode, nodes, params, offset = 0) { 69 | if (mode === 'reverse') { 70 | const { stagger } = params 71 | const totalStagger = (stagger || 0) * (nodes.length - 1) 72 | return nodes.map((node, i) => totalStagger - (stagger || 0) * i + offset) 73 | } else if (mode === 'async') { 74 | return nodes.map((node) => offset) 75 | } else if (mode === 'sequence' || mode === 'delayed') { 76 | let { stagger } = params 77 | if (!stagger) { 78 | stagger = (params.duration || 1000) / (nodes.length - 1) 79 | } 80 | return nodes.map((node, i) => stagger * i + offset) 81 | } else if (mode === 'oneByOne') { 82 | return nodes.map((node) => params.duration) 83 | } else if (mode === 'shuffle') { 84 | const { stagger } = params 85 | return shuffle(nodes.map((node, i) => (stagger || 0) * i + offset)) 86 | } else { 87 | const { stagger } = params 88 | return nodes.map((node, i) => (stagger || 0) * i + offset) 89 | } 90 | } 91 | 92 | /** 93 | * @param {Array} nodes DOM Elements Collection (converted to Array) 94 | * @param {object} from - Initial value 95 | * @param {object} to - Target value 96 | * @param {object} params - Options of tweens 97 | * @example tl.fromTo(nodes, {x:0}, {x:200}, {duration:1000, stagger:200}) 98 | * @memberof Timeline 99 | * @static 100 | */ 101 | fromTo (nodes, from, to, params) { 102 | nodes = Selector(nodes, true, true) 103 | if (nodes && nodes.length) { 104 | if (this._defaultParams) { 105 | params = params ? { ...this._defaultParams, ...params } : this._defaultParams 106 | } 107 | const position = params.label 108 | const offset = 109 | typeof position === 'number' 110 | ? position 111 | : this.position.parseLabel(typeof position !== 'undefined' ? position : 'afterLast', null) 112 | const mode = this.getTiming(params.mode, nodes, params, offset) 113 | for (let i = 0, node, len = nodes.length; i < len; i++) { 114 | node = nodes[i] 115 | this.add( 116 | Tween.fromTo( 117 | node, 118 | typeof from === 'function' 119 | ? from(i, nodes.length) 120 | : typeof from === 'object' && !!from 121 | ? { ...from } 122 | : null, 123 | typeof to === 'function' ? to(i, nodes.length) : to, 124 | typeof params === 'function' ? params(i, nodes.length) : params 125 | ), 126 | mode[i] 127 | ) 128 | } 129 | } 130 | return this.start() 131 | } 132 | 133 | /** 134 | * @param {Array} nodes DOM Elements Collection (converted to Array) 135 | * @param {object} from - Initial value 136 | * @param {object} params - Options of tweens 137 | * @example tl.from(nodes, {x:200}, {duration:1000, stagger:200}) 138 | * @memberof Timeline 139 | * @static 140 | */ 141 | from (nodes, from, params) { 142 | return this.fromTo(nodes, from, null, params) 143 | } 144 | 145 | /** 146 | * @param {Array} nodes DOM Elements Collection (converted to Array) 147 | * @param {object} to - Target value 148 | * @param {object} params - Options of tweens 149 | * @example tl.to(nodes, {x:200}, {duration:1000, stagger:200}) 150 | * @memberof Timeline 151 | * @static 152 | */ 153 | to (nodes, to, params) { 154 | return this.fromTo(nodes, null, to, params) 155 | } 156 | 157 | /** 158 | * Add label to Timeline 159 | * @param {string} name Label name 160 | * @param {any} offset Label value, can be `number` and/or `string` 161 | * @example tl.add('label1', 200) 162 | * @memberof Timeline 163 | */ 164 | addLabel (name, offset) { 165 | this.position.addLabel(name, offset) 166 | return this 167 | } 168 | 169 | map (fn) { 170 | for (let i = 0, len = this._tweens.length; i < len; i++) { 171 | const _tween = this._tweens[i] 172 | fn(_tween, i) 173 | this._duration = Math.max(this._duration, _tween._duration + _tween._startTime) 174 | } 175 | return this 176 | } 177 | 178 | /** 179 | * Add tween to Timeline 180 | * @param {Tween} tween Tween instance 181 | * @param {position} position Can be label name, number or relative number to label 182 | * @example tl.add(new Tween(node, {x:0}).to({x:200}, 200)) 183 | * @memberof Timeline 184 | */ 185 | add (tween, position) { 186 | if (Array.isArray(tween)) { 187 | tween.map((_tween) => { 188 | this.add(_tween, position) 189 | }) 190 | return this 191 | } else if (typeof tween === 'object' && !(tween instanceof Tween)) { 192 | tween = new Tween(tween.from).to(tween.to, tween) 193 | } 194 | 195 | const { _defaultParams, _duration } = this 196 | 197 | if (_defaultParams) { 198 | for (const method in _defaultParams) { 199 | if (typeof tween[method] === 'function') { 200 | tween[method](_defaultParams[method]) 201 | } 202 | } 203 | } 204 | 205 | const offset = 206 | typeof position === 'number' 207 | ? position 208 | : this.position.parseLabel(typeof position !== 'undefined' ? position : 'afterLast', null) 209 | tween._startTime = Math.max(this._startTime, tween._delayTime, offset) 210 | tween._delayTime = offset 211 | tween._isPlaying = true 212 | this._duration = Math.max(_duration, Math.max(tween._startTime + tween._delayTime, tween._duration)) 213 | this._tweens.push(tween) 214 | this.position.setLabel('afterLast', this._duration) 215 | return this 216 | } 217 | 218 | restart () { 219 | this._startTime += now() 220 | 221 | add(this) 222 | 223 | return this.emit(EVENT_RESTART) 224 | } 225 | 226 | easing (easing) { 227 | return this.map((tween) => tween.easing(easing)) 228 | } 229 | 230 | interpolation (interpolation) { 231 | return this.map((tween) => tween.interpolation(interpolation)) 232 | } 233 | 234 | update (time) { 235 | const { 236 | _tweens, 237 | _duration, 238 | _reverseDelayTime, 239 | _startTime, 240 | _reversed, 241 | _yoyo, 242 | _repeat, 243 | _isFinite, 244 | _isPlaying, 245 | _prevTime, 246 | _onStartCallbackFired 247 | } = this 248 | 249 | let elapsed 250 | 251 | time = time !== undefined ? time : now() 252 | 253 | let delta = time - _prevTime 254 | this._prevTime = time 255 | if (delta > TOO_LONG_FRAME_MS && isRunning() && isLagSmoothing()) { 256 | time -= delta - FRAME_MS 257 | } 258 | 259 | if (!_isPlaying || time < _startTime) { 260 | return true 261 | } 262 | 263 | elapsed = (time - _startTime) / _duration 264 | elapsed = elapsed > 1 ? 1 : elapsed 265 | elapsed = _reversed ? 1 - elapsed : elapsed 266 | 267 | this.elapsed = elapsed 268 | 269 | if (!_onStartCallbackFired) { 270 | this.emit(EVENT_START) 271 | this._onStartCallbackFired = true 272 | } 273 | 274 | const timing = time - _startTime 275 | const _timing = _reversed ? _duration - timing : timing 276 | 277 | let i = 0 278 | while (i < _tweens.length) { 279 | _tweens[i].update(_timing) 280 | i++ 281 | } 282 | 283 | this.emit(EVENT_UPDATE, elapsed, timing) 284 | 285 | if (elapsed === 1 || (_reversed && elapsed === 0)) { 286 | if (_repeat) { 287 | if (_isFinite) { 288 | this._repeat-- 289 | } 290 | 291 | this.emit(_reversed ? EVENT_REVERSE : EVENT_REPEAT) 292 | 293 | if (_yoyo) { 294 | this._reversed = !_reversed 295 | this.timingOrder((timing) => timing.reverse()) 296 | } 297 | 298 | if (_reversed && _reverseDelayTime) { 299 | this._startTime = time + _reverseDelayTime 300 | } else { 301 | this._startTime = time 302 | } 303 | 304 | i = 0 305 | while (i < _tweens.length) { 306 | _tweens[i].reassignValues(time) 307 | i++ 308 | } 309 | 310 | return true 311 | } else { 312 | this.emit(EVENT_COMPLETE) 313 | this._repeat = this._r 314 | 315 | remove(this) 316 | this._isPlaying = false 317 | 318 | return false 319 | } 320 | } 321 | 322 | return true 323 | } 324 | 325 | progress (value) { 326 | return value !== undefined ? this.update(value * this._duration) : this.elapsed 327 | } 328 | } 329 | export default Timeline 330 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | // Frame lag-fix constants 2 | export const FRAME_MS = 50 / 3 3 | export const TOO_LONG_FRAME_MS = 250 4 | 5 | export const CHAINED_TWEENS = '_chainedTweens' 6 | 7 | // Event System 8 | export const EVENT_CALLBACK = 'Callback' 9 | export const EVENT_UPDATE = 'update' 10 | export const EVENT_COMPLETE = 'complete' 11 | export const EVENT_START = 'start' 12 | export const EVENT_REPEAT = 'repeat' 13 | export const EVENT_REVERSE = 'reverse' 14 | export const EVENT_PAUSE = 'pause' 15 | export const EVENT_PLAY = 'play' 16 | export const EVENT_RESTART = 'restart' 17 | export const EVENT_STOP = 'stop' 18 | export const EVENT_SEEK = 'seek' 19 | 20 | // For String tweening stuffs 21 | export const STRING_PROP = 'STRING_PROP' 22 | // Also RegExp's for string tweening 23 | export const NUM_REGEX = /\s+|([A-Za-z?().,{}:""[\]#%]+)|([-+]=+)?([-+]+)?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]=?\d+)?/g 24 | 25 | // Copies everything, duplicates, no shallow-copy 26 | export function deepCopy (source) { 27 | if ((source && source.nodeType) || source === undefined || typeof source !== 'object') { 28 | return source 29 | } else if (Array.isArray(source)) { 30 | return [].concat(source) 31 | } else if (typeof source === 'object') { 32 | let target = {} 33 | for (let prop in source) { 34 | target[prop] = deepCopy(source[prop]) 35 | } 36 | return target 37 | } 38 | return source 39 | } 40 | 41 | const isNaNForST = (v) => isNaN(+v) || ((v[0] === '+' || v[0] === '-') && v[1] === '=') || v === '' || v === ' ' 42 | 43 | const hexColor = /^#([0-9a-f]{6}|[0-9a-f]{3})$/gi 44 | const hex2rgb = (all, hex) => { 45 | let r 46 | let g 47 | let b 48 | if (hex.length === 3) { 49 | r = hex[0] 50 | g = hex[1] 51 | b = hex[2] 52 | hex = r + r + g + g + b + b 53 | } 54 | let color = parseInt(hex, 16) 55 | r = (color >> 16) & 255 56 | g = (color >> 8) & 255 57 | b = color & 255 58 | return 'rgb(' + r + ', ' + g + ', ' + b + ')' 59 | } 60 | 61 | export function decomposeString (fromValue) { 62 | if (fromValue && fromValue.splice && fromValue.isString) { 63 | return fromValue 64 | } 65 | if (typeof fromValue !== 'string') { 66 | return fromValue 67 | } 68 | if (fromValue.charAt(1) === '=') { 69 | return fromValue 70 | } 71 | const value = fromValue 72 | .replace(hexColor, hex2rgb) 73 | .match(NUM_REGEX) 74 | .map((v) => (isNaNForST(v) ? v : +v)) 75 | value.isString = true 76 | return value 77 | } 78 | 79 | // Decompose value, now for only `string` that required 80 | export function decompose (prop, obj, from, to) { 81 | const fromValue = from[prop] 82 | const toValue = to[prop] 83 | 84 | if (fromValue === toValue) { 85 | return true 86 | } else if (Array.isArray(fromValue) && Array.isArray(toValue) && fromValue.length === toValue.length) { 87 | for (let i = 0, len = toValue.length; i < len; i++) { 88 | const a = fromValue[i] 89 | const b = toValue[i] 90 | 91 | if (a === b || (typeof a === 'number' && typeof b === 'number')) { 92 | continue 93 | } else { 94 | decompose(i, obj[prop], fromValue, toValue) 95 | } 96 | } 97 | } 98 | if (typeof fromValue === 'number' && typeof toValue === 'number') { 99 | // 100 | } else if (fromValue && fromValue.splice && fromValue.isString && toValue && toValue.splice && toValue.isString) { 101 | } else if (typeof fromValue === 'string' && Array.isArray(toValue)) { 102 | const fromValue1 = decomposeString(fromValue) 103 | const toValues = toValue.map(decomposeString) 104 | 105 | from[prop] = fromValue1 106 | to[prop] = toValues 107 | return true 108 | } else if (typeof fromValue === 'string' || typeof toValue === 'string') { 109 | let fromValue1 = Array.isArray(fromValue) && fromValue[0] === STRING_PROP ? fromValue : decomposeString(fromValue) 110 | let toValue1 = Array.isArray(toValue) && toValue[0] === STRING_PROP ? toValue : decomposeString(toValue) 111 | 112 | if (fromValue1 === undefined) { 113 | return 114 | } 115 | let i = 1 116 | while (i < fromValue1.length) { 117 | if (fromValue1[i] === toValue1[i] && typeof fromValue1[i - 1] === 'string') { 118 | fromValue1.splice(i - 1, 2, fromValue1[i - 1] + fromValue1[i]) 119 | toValue1.splice(i - 1, 2, toValue1[i - 1] + toValue1[i]) 120 | } else { 121 | i++ 122 | } 123 | } 124 | 125 | i = 0 126 | 127 | if (fromValue1[0] === STRING_PROP) { 128 | fromValue1.shift() 129 | } 130 | if (toValue1[0] === STRING_PROP) { 131 | toValue1.shift() 132 | } 133 | 134 | from[prop] = fromValue1 135 | to[prop] = toValue1 136 | return true 137 | } else if (typeof fromValue === 'object' && typeof toValue === 'object') { 138 | if (Array.isArray(fromValue) && !fromValue.isString) { 139 | return fromValue.map((v, i) => decompose(i, obj[prop], fromValue, toValue)) 140 | } else { 141 | for (let prop2 in toValue) { 142 | decompose(prop2, obj[prop], fromValue, toValue) 143 | } 144 | } 145 | return true 146 | } 147 | return false 148 | } 149 | 150 | // Recompose value 151 | export const RGB = 'rgb(' 152 | export const RGBA = 'rgba(' 153 | 154 | export const isRGBColor = (v, i, r = RGB) => 155 | typeof v[i] === 'number' && (v[i - 1] === r || v[i - 3] === r || v[i - 5] === r) 156 | export function recompose (prop, obj, from, to, t, originalT, stringBuffer) { 157 | const fromValue = stringBuffer ? from : from[prop] 158 | let toValue = stringBuffer ? to : to[prop] 159 | if (toValue === undefined) { 160 | return fromValue 161 | } 162 | if (fromValue === undefined || typeof fromValue === 'string' || fromValue === toValue) { 163 | return toValue 164 | } else if (typeof fromValue === 'object' && typeof toValue === 'object') { 165 | if (!fromValue || !toValue) { 166 | return obj[prop] 167 | } 168 | if ( 169 | typeof fromValue === 'object' && 170 | !!fromValue && 171 | fromValue.isString && 172 | toValue && 173 | toValue.splice && 174 | toValue.isString 175 | ) { 176 | let STRING_BUFFER = '' 177 | for (let i = 0, len = fromValue.length; i < len; i++) { 178 | if (fromValue[i] !== toValue[i] || typeof fromValue[i] !== 'number' || typeof toValue[i] === 'number') { 179 | const isRelative = typeof fromValue[i] === 'number' && typeof toValue[i] === 'string' && toValue[i][1] === '=' 180 | let currentValue = 181 | typeof fromValue[i] !== 'number' 182 | ? fromValue[i] 183 | : isRelative 184 | ? fromValue[i] + parseFloat(toValue[i][0] + toValue[i].substr(2)) * t 185 | : fromValue[i] + (toValue[i] - fromValue[i]) * t 186 | if (isRGBColor(fromValue, i) || isRGBColor(fromValue, i, RGBA)) { 187 | currentValue |= 0 188 | } 189 | STRING_BUFFER += currentValue 190 | if (isRelative && originalT === 1) { 191 | fromValue[i] = fromValue[i] + parseFloat(toValue[i][0] + toValue[i].substr(2)) 192 | } 193 | } else { 194 | STRING_BUFFER += fromValue[i] 195 | } 196 | } 197 | if (!stringBuffer) { 198 | obj[prop] = STRING_BUFFER 199 | } 200 | return STRING_BUFFER 201 | } else if (Array.isArray(fromValue) && fromValue[0] !== STRING_PROP) { 202 | for (let i = 0, len = fromValue.length; i < len; i++) { 203 | if (fromValue[i] === toValue[i] || typeof obj[prop] === 'string') { 204 | continue 205 | } 206 | recompose(i, obj[prop], fromValue, toValue, t, originalT) 207 | } 208 | } else if (typeof fromValue === 'object' && !!fromValue && !fromValue.isString) { 209 | for (let i in fromValue) { 210 | if (fromValue[i] === toValue[i]) { 211 | continue 212 | } 213 | recompose(i, obj[prop], fromValue, toValue, t, originalT) 214 | } 215 | } 216 | } else if (typeof fromValue === 'number') { 217 | const isRelative = typeof toValue === 'string' 218 | obj[prop] = isRelative 219 | ? fromValue + parseFloat(toValue[0] + toValue.substr(2)) * t 220 | : fromValue + (toValue - fromValue) * t 221 | if (isRelative && originalT === 1) { 222 | from[prop] = obj[prop] 223 | } 224 | } else if (typeof toValue === 'function') { 225 | obj[prop] = toValue(t) 226 | } 227 | return obj[prop] 228 | } 229 | 230 | // Dot notation => Object structure converter 231 | // example 232 | // {'scale.x.y.z':'VALUE'} => {scale:{x:{y:{z:'VALUE'}}}} 233 | // Only works for 3-level parsing, after 3-level, parsing dot-notation not works as it's not affects 234 | const propRegExp = /([.[])/g 235 | const replaceBrace = /\]/g 236 | const propExtract = function (obj, property) { 237 | const value = obj[property] 238 | const props = property.replace(replaceBrace, '').split(propRegExp) 239 | const propsLastIndex = props.length - 1 240 | let lastArr = Array.isArray(obj) 241 | let lastObj = typeof obj === 'object' && !lastArr 242 | if (lastObj) { 243 | obj[property] = null 244 | delete obj[property] 245 | } else if (lastArr) { 246 | obj.splice(property, 1) 247 | } 248 | return props.reduce((nested, prop, index) => { 249 | if (lastArr) { 250 | if (prop !== '.' && prop !== '[') { 251 | prop *= 1 252 | } 253 | } 254 | let nextProp = props[index + 1] 255 | let nextIsArray = nextProp === '[' 256 | if (prop === '.' || prop === '[') { 257 | if (prop === '.') { 258 | lastObj = true 259 | lastArr = false 260 | } else if (prop === '[') { 261 | lastObj = false 262 | lastArr = true 263 | } 264 | return nested 265 | } else if (nested[prop] === undefined) { 266 | if (lastArr || lastObj) { 267 | nested[prop] = index === propsLastIndex ? value : lastArr || nextIsArray ? [] : lastObj ? {} : null 268 | lastObj = lastArr = false 269 | return nested[prop] 270 | } 271 | } else if (nested[prop] !== undefined) { 272 | if (index === propsLastIndex) { 273 | nested[prop] = value 274 | } 275 | return nested[prop] 276 | } 277 | return nested 278 | }, obj) 279 | } 280 | 281 | export const SET_NESTED = function (nested) { 282 | if (typeof nested === 'object' && !!nested) { 283 | for (let prop in nested) { 284 | if (prop.indexOf('.') !== -1 || prop.indexOf('[') !== -1) { 285 | propExtract(nested, prop) 286 | } else if (typeof nested[prop] === 'object' && !!nested[prop]) { 287 | let nested2 = nested[prop] 288 | for (let prop2 in nested2) { 289 | if (prop2.indexOf('.') !== -1 || prop2.indexOf('[') !== -1) { 290 | propExtract(nested2, prop2) 291 | } else if (typeof nested2[prop2] === 'object' && !!nested2[prop2]) { 292 | let nested3 = nested2[prop2] 293 | for (let prop3 in nested3) { 294 | if (prop3.indexOf('.') !== -1 || prop3.indexOf('[') !== -1) { 295 | propExtract(nested3, prop3) 296 | } 297 | } 298 | } 299 | } 300 | } 301 | } 302 | } 303 | return nested 304 | } 305 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* global TWEEN */ 2 | 3 | import test from 'ava' 4 | 5 | import { Easing, Tween, Timeline, update, getAll, removeAll } from './src' 6 | 7 | import browserTestMiddleware from './withPage' 8 | 9 | test('Events', (t) => { 10 | let tween = new Tween({ x: 0 }) 11 | .to({ x: 100 }, 100) 12 | .repeat(2) 13 | .yoyo(true) 14 | .start(0) 15 | 16 | t.plan(9) 17 | 18 | tween.on('start', () => { 19 | t.log('on:start was called successfully') 20 | t.pass() 21 | }) 22 | 23 | tween.on('update', () => { 24 | t.log('on:update was called successfully') 25 | t.pass() 26 | }) 27 | 28 | tween.on('repeat', () => { 29 | t.log('on:repeat was called successfully') 30 | t.pass() 31 | }) 32 | 33 | tween.on('reverse', () => { 34 | t.log('on:reverse was called successfully') 35 | t.pass() 36 | }) 37 | 38 | tween.on('complete', () => { 39 | t.log('on:complete was called successfully') 40 | t.pass() 41 | }) 42 | 43 | update(0) 44 | update(50) 45 | update(100) 46 | update(200) 47 | update(300) 48 | }) 49 | 50 | test('Value Interpolation', (t) => { 51 | const m = ['rgb(', 0, ', 204, ', 255, ')'] 52 | 53 | let obj = { 54 | a: 0, 55 | b: 'B value 1', 56 | c: { x: 2 }, 57 | d: [3], 58 | _e: 4, 59 | g: 5, 60 | h: 0, 61 | j: 0, 62 | k: '#000', 63 | l: '#0cf', 64 | m 65 | } 66 | 67 | Object.defineProperty(obj, 'e', { 68 | get () { 69 | return obj._e 70 | }, 71 | set (x) { 72 | obj._e = x 73 | } 74 | }) 75 | 76 | const m2 = ['rgb(', 255, ', 204, ', 0, ')'] 77 | new Tween(obj) 78 | .to( 79 | { 80 | a: 1, 81 | b: 'B value 2', 82 | c: { x: 3 }, 83 | d: [4], 84 | _e: 5, 85 | g: '+=1', 86 | h: 250000, 87 | j: [1, 2], 88 | k: ['rgb(100, 100, 100)', 'rgb(200, 200, 200)'], 89 | l: '#fc0', 90 | m: m2 91 | }, 92 | 100 93 | ) 94 | .start(0) 95 | 96 | update(0) 97 | 98 | t.is(obj.a, 0) 99 | t.is(obj.b, 'B value 1') 100 | t.is(obj.c.x, 2) 101 | t.is(obj.d[0], 3) 102 | t.is(obj.e, 4) 103 | t.is(obj.g, 5) 104 | t.is(obj.h, 0) 105 | t.is(obj.j, 0) 106 | t.is(obj.k, 'rgb(0, 0, 0)') 107 | t.is(obj.l, 'rgb(0, 204, 255)') 108 | t.deepEqual(obj.m, ['rgb(', 0, ', 204, ', 255, ')']) 109 | 110 | update(50) 111 | 112 | t.is(obj.a, 0.5, 'Number interpolation not worked as excepted') 113 | t.log('Number interpolation worked as excepted') 114 | 115 | t.is(obj.b, 'B value 1.5', 'String interpolation not worked as excepted') 116 | t.log('String interpolation worked as excepted') 117 | 118 | t.is(obj.c.x, 2.5, 'Object interpolation not worked as excepted') 119 | t.log('Object interpolation worked as excepted') 120 | 121 | t.is(obj.d[0], 3.5, 'Array interpolation not worked as excepted') 122 | t.log('Array interpolation worked as excepted') 123 | 124 | t.is(obj.e, 4.5, 'Getter/Setter interpolation not worked as excepted') 125 | t.log('Getter/Setter interpolation worked as excepted') 126 | 127 | t.is(obj.g, 5.5, 'Relative number interpolation not worked as excepted') 128 | t.log('Relative number interpolation worked as excepted') 129 | 130 | t.is(obj.h, 125000, 'Big number interpolation not worked as excepted') 131 | t.log('Big number interpolation worked as excepted') 132 | 133 | t.is(obj.j, 1, 'Multi-Interpolation not worked as excepted') 134 | t.log('Multi-Interpolation worked as excepted') 135 | 136 | t.is(obj.k, 'rgb(100, 100, 100)', 'Multi-Interpolation not worked as excepted') 137 | t.log('Multi-Interpolation worked as excepted') 138 | 139 | update(100) 140 | 141 | t.is(obj.a, 1) 142 | t.is(obj.b, 'B value 2') 143 | t.is(obj.c.x, 3) 144 | t.is(obj.d[0], 4) 145 | t.is(obj.e, 5) 146 | t.is(obj.g, 6) 147 | t.is(obj.h, 250000, 'Big number interpolation ending value not worked as excepted') 148 | t.is(obj.j, 2, 'Multi-Interpolation not worked as excepted') 149 | t.is(obj.k, 'rgb(200, 200, 200)', 'Multi-Interpolation not worked as excepted') 150 | t.is(obj.l, 'rgb(255, 204, 0)', 'String interpolation not worked as excepted') 151 | t.deepEqual(obj.m, m2, 'Array interpolation not worked as excepted') 152 | }) 153 | 154 | test('Value Array-based Interpolation', (t) => { 155 | let obj = { x: 0 } 156 | new Tween(obj).to({ x: [1, 3, 5] }, 100).start(0) 157 | 158 | t.is(obj.x, 0) 159 | 160 | update(50) 161 | 162 | t.is(obj.x, 2, 'Interpolation failed') 163 | t.log('End-value interpolation was done') 164 | 165 | t.log('Start-value interpolation was done') 166 | 167 | update(100) 168 | }) 169 | 170 | test('Tweens List Controlling', (t) => { 171 | let tween = new Tween({ x: 0 }) 172 | .to({ x: 100 }, 100) 173 | .repeat(2) 174 | .yoyo(true) 175 | .start(0) 176 | 177 | t.is(getAll().length, 1, 'Tween added in tweens list') 178 | t.log('Tweens adding was done') 179 | 180 | tween.stop() 181 | 182 | t.is(getAll().length, 0, 'Tween removed from tweens list') 183 | t.log('Tween removing was done') 184 | 185 | tween.restart() 186 | 187 | t.is(getAll().length, 1, 'Tween added in tweens list') 188 | t.log('Tweens restart and re-add to tweens list was done') 189 | 190 | removeAll() 191 | 192 | t.is(getAll().length, 0, 'All Tweens was removed from tweens list') 193 | t.log('Tween removeAll was worked fine') 194 | }) 195 | 196 | test('Easing', (t) => { 197 | const { Quadratic, Elastic, Linear } = Easing 198 | 199 | const { InOut: QuadraticInOut } = Quadratic 200 | const { InOut: ElasticInOut } = Elastic 201 | const { None } = Linear 202 | 203 | t.is(None(0.67), 0.67, 'Linear.None was not eased as excepted') 204 | t.log('Linear.None was eased as excepted') 205 | 206 | t.not(QuadraticInOut(0.77), 0.77, 'Quadratic.InOut was not eased as excepted') 207 | t.log('Quadratic.InOut was eased as excepted') 208 | 209 | t.not(ElasticInOut(0.6), 0.6, 'Elastic.InOut was not eased as excepted') 210 | t.log('Elastic.InOut was eased as excepted') 211 | }) 212 | 213 | test('Tween update should be run against all tween each time', (t) => { 214 | const order = [] 215 | 216 | new Tween({ x: 0 }) 217 | .to({ x: 100 }, 100) 218 | .start(0) 219 | .on('complete', () => { 220 | order.push(0) 221 | }) 222 | new Tween({ x: 0 }) 223 | .to({ x: 100 }, 100) 224 | .delay(10) 225 | .start(0) 226 | .on('complete', () => { 227 | order.push(1) 228 | }) 229 | new Tween({ x: 0 }) 230 | .to({ x: 100 }, 100) 231 | .delay(20) 232 | .start(0) 233 | .on('complete', () => { 234 | order.push(2) 235 | }) 236 | 237 | t.plan(1) 238 | 239 | update(0) 240 | update(200) 241 | 242 | t.deepEqual(order, [0, 1, 2]) 243 | }) 244 | 245 | test('Tween should work after recall when using power saving feature', (t) => { 246 | const init = (obj) => 247 | new Tween(obj) 248 | .to({ z: 360 }, 1000) 249 | .on('complete', init) 250 | .start(0) 251 | 252 | const obj1 = { z: 0 } 253 | init(obj1) 254 | 255 | update(500) 256 | t.is(obj1.z, 180, 'Tweening update does not work as excepted') 257 | update(1000) 258 | t.is(obj1.z, 360, 'Tweening update does not work as excepted') 259 | 260 | update(0) 261 | 262 | const obj2 = { z: 0 } 263 | init(obj2) 264 | 265 | update(500) 266 | t.is(obj2.z, 180, 'Tweening update does not work as excepted') 267 | update(1000) 268 | t.is(obj2.z, 360, 'Tweening update does not work as excepted') 269 | }) 270 | 271 | test('Headless tests', browserTestMiddleware, (t, page) => { 272 | return page 273 | .evaluate(() => { 274 | const deepArrayCopy = (arr) => 275 | arr.map((child) => 276 | Array.isArray(child) 277 | ? deepArrayCopy(child) 278 | : Object.keys(child) && Object.keys(child).length 279 | ? Object.assign({}, child) 280 | : child 281 | ) 282 | 283 | const tests = [] 284 | 285 | const obj = { x: 0, y: [50, 'String test 100'] } 286 | const obj2 = { x: 0 } 287 | const arr1 = [[100], { f: 200 }] 288 | 289 | const tween1 = new TWEEN.Tween(obj) 290 | .to({ x: 200, y: [100, 'String test 200'] }, 2000) 291 | .on('start', () => { 292 | tests.push({ 293 | method: 'log', 294 | successMessage: 'on:start event works as excepted' 295 | }) 296 | }) 297 | .on('update', (obj, elapsed) => { 298 | tests.push({ 299 | method: 'log', 300 | successMessage: 'on:update event works as excepted with elapsed ' + elapsed 301 | }) 302 | }) 303 | .on('complete', () => { 304 | tests.push({ 305 | method: 'log', 306 | successMessage: 'on:complete event works as excepted' 307 | }) 308 | }) 309 | const tween2 = new TWEEN.Tween(obj2).to({ x: 200 }, 4000).easing(TWEEN.Easing.Elastic.InOut) 310 | const tween3 = new TWEEN.Tween(arr1).to([[0], { f: 100 }], 2000) 311 | 312 | tween1.start(0) 313 | tween2.start(0) 314 | tween3.start(0) 315 | 316 | tests.push({ 317 | method: 'is', 318 | a: obj.x, 319 | b: 0, 320 | failMessage: 'ID: ObjX_24U' 321 | }) 322 | tests.push({ 323 | method: 'is', 324 | a: obj.y[0], 325 | b: 50, 326 | failMessage: 'ID: ObjY_25C' 327 | }) 328 | tests.push({ 329 | method: 'is', 330 | a: obj.y[1], 331 | b: 'String test 100', 332 | failMessage: 'ID: ObjY_26G' 333 | }) 334 | tests.push({ 335 | method: 'is', 336 | a: obj2.x, 337 | b: 0, 338 | failMessage: 'ID: ObjX_26Z' 339 | }) 340 | tests.push({ 341 | method: 'is', 342 | a: arr1[0][0], 343 | b: 100, 344 | failMessage: 'ID: ObjA_27L' 345 | }) 346 | tests.push({ 347 | method: 'is', 348 | a: arr1[1].f, 349 | b: 200, 350 | failMessage: 'ID: ObjF_27X' 351 | }) 352 | 353 | TWEEN.update(1000) 354 | 355 | tests.push({ 356 | method: 'is', 357 | a: obj.x, 358 | b: 100, 359 | failMessage: 'There something wrong with number interpolation', 360 | successMessage: 'Number interpolation works as excepted' 361 | }) 362 | tests.push({ 363 | method: 'is', 364 | a: obj.y[0], 365 | b: 75, 366 | failMessage: 'There something wrong with Array number interpolation', 367 | successMessage: 'Array Number interpolation works as excepted' 368 | }) 369 | tests.push({ 370 | method: 'is', 371 | a: obj.y[1], 372 | b: 'String test 150', 373 | failMessage: 'There something wrong with Array string interpolation', 374 | successMessage: 'Array string interpolation works as excepted' 375 | }) 376 | tests.push({ 377 | method: 'not', 378 | a: obj2.x, 379 | b: 50, 380 | failMessage: 'Easing not works properly or tween instance not handles easing function properly', 381 | successMessage: 'Easing function works properly' 382 | }) 383 | tests.push({ 384 | method: 'deepEqual', 385 | a: deepArrayCopy(arr1), 386 | b: [[50], { f: 150 }], 387 | failMessage: 'Array-based tween failed due of internal processor/instance and/or something failed within core', 388 | successMessage: 'Array-based tweens works properly' 389 | }) 390 | 391 | TWEEN.update(2000) 392 | 393 | tests.push({ 394 | method: 'is', 395 | a: obj.x, 396 | b: 200, 397 | failMessage: 'ID: ObjX_32R' 398 | }) 399 | tests.push({ 400 | method: 'is', 401 | a: obj.y[0], 402 | b: 100, 403 | failMessage: 'ID: ObjY_33V' 404 | }) 405 | tests.push({ 406 | method: 'is', 407 | a: obj.y[1], 408 | b: 'String test 200', 409 | failMessage: 'ID: ObjY_33S' 410 | }) 411 | tests.push({ 412 | method: 'not', 413 | a: obj2.x, 414 | b: 200, 415 | failMessage: 'ID: ObjY_34I' 416 | }) 417 | tests.push({ 418 | method: 'is', 419 | a: arr1[0][0], 420 | b: 0, 421 | failMessage: 'ID: ObjA_34P' 422 | }) 423 | tests.push({ 424 | method: 'is', 425 | a: arr1[1].f, 426 | b: 100, 427 | failMessage: 'ID: ObjF_35T' 428 | }) 429 | 430 | TWEEN.update(4000) 431 | 432 | tests.push({ 433 | method: 'is', 434 | a: obj2.x, 435 | b: 200, 436 | failMessage: 'ID: ObjY_36K' 437 | }) 438 | 439 | return tests 440 | }) 441 | .then((tests) => { 442 | tests.map(({ method, a, b, failMessage, successMessage }) => { 443 | if (method === 'log') { 444 | t.log(successMessage) 445 | } else { 446 | t[method](a, b, failMessage) 447 | successMessage && t.log(successMessage) 448 | } 449 | }) 450 | }) 451 | }) 452 | 453 | test('Timeline', (t) => { 454 | const obj = { x: 0 } 455 | const obj2 = { x: 0 } 456 | const obj3 = { x: 0 } 457 | 458 | const from = [obj, obj2, obj3] 459 | const to = { x: 200 } 460 | 461 | const tl = new Timeline({ duration: 1000, startTime: 0, stagger: 1000 }).to(from, to) 462 | 463 | tl.on('start', () => { 464 | t.pass() 465 | t.log('on:start event works as excepted') 466 | }) 467 | tl.on('update', (elapsed) => { 468 | t.pass() 469 | t.log('on:update event works as excepted with elapsed ' + elapsed) 470 | }) 471 | tl.on('complete', () => { 472 | t.pass() 473 | t.log('on:complete event works as excepted') 474 | }) 475 | 476 | tl.start(0) 477 | 478 | t.is(obj.x, 0) 479 | t.is(obj2.x, 0) 480 | t.is(obj3.x, 0) 481 | 482 | update(1000) 483 | 484 | t.is(obj.x, 200) 485 | t.is(obj2.x, 0) 486 | t.is(obj3.x, 0) 487 | 488 | update(2000) 489 | 490 | t.is(obj.x, 200) 491 | t.is(obj2.x, 200) 492 | t.is(obj3.x, 0) 493 | 494 | update(3000) 495 | 496 | t.is(obj.x, 200) 497 | t.is(obj2.x, 200) 498 | t.is(obj3.x, 200) 499 | 500 | update(4000) 501 | 502 | t.is(obj.x, 200) 503 | t.is(obj2.x, 200) 504 | t.is(obj3.x, 200) 505 | 506 | t.log('All values interpolation works as excepted') 507 | }) 508 | -------------------------------------------------------------------------------- /logo/es6-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | RealDRAW 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## TWEEN : object 4 | Lightweight, effecient and modular ES6 version of tween.js 5 | 6 | **Kind**: global namespace 7 | **License**: MIT 8 | **Copyright**: 2019 @dalisoft and es6-tween contributors 9 | **Example** 10 | ```js 11 | // ES6 12 | const {add, remove, isRunning, autoPlay} = TWEEN 13 | ``` 14 | 15 | * [TWEEN](#TWEEN) : object 16 | * [.Easing](#TWEEN.Easing) : object 17 | * [.Interpolation](#TWEEN.Interpolation) : object 18 | * [.Interpolator](#TWEEN.Interpolator) ⇒ function 19 | * [.Timeline](#TWEEN.Timeline) : object 20 | * [.Tween](#TWEEN.Tween) : object 21 | * [.Tween#setMaxListener(count)](#TWEEN.Tween.Tween+setMaxListener) 22 | * [.Tween#on(event, callback)](#TWEEN.Tween.Tween+on) 23 | * [.Tween#once(event, callback)](#TWEEN.Tween.Tween+once) 24 | * [.Tween#off(event, callback)](#TWEEN.Tween.Tween+off) 25 | * [.Tween#emit(event)](#TWEEN.Tween.Tween+emit) 26 | * [.Tween#isPlaying()](#TWEEN.Tween.Tween+isPlaying) ⇒ boolean 27 | * [.Tween#isStarted()](#TWEEN.Tween.Tween+isStarted) ⇒ boolean 28 | * [.Tween#reverse([state])](#TWEEN.Tween.Tween+reverse) 29 | * [.Tween#reversed()](#TWEEN.Tween.Tween+reversed) ⇒ boolean 30 | * [.Tween#pause()](#TWEEN.Tween.Tween+pause) 31 | * [.Tween#play()](#TWEEN.Tween.Tween+play) 32 | * [.Tween#restart([noDelay])](#TWEEN.Tween.Tween+restart) 33 | * ~~[.Tween#seek(time, [keepPlaying])](#TWEEN.Tween.Tween+seek)~~ 34 | * ~~[.Tween#duration(amount)](#TWEEN.Tween.Tween+duration)~~ 35 | * [.Tween#to(properties, [duration])](#TWEEN.Tween.Tween+to) 36 | * [.Tween#start(time)](#TWEEN.Tween.Tween+start) 37 | * [.Tween#stop()](#TWEEN.Tween.Tween+stop) 38 | * [.Tween#delay(amount)](#TWEEN.Tween.Tween+delay) 39 | * [.Tween#chainedTweens(arguments)](#TWEEN.Tween.Tween+chainedTweens) 40 | * [.Tween#repeat(amount)](#TWEEN.Tween.Tween+repeat) 41 | * [.Tween#reverseDelay(amount)](#TWEEN.Tween.Tween+reverseDelay) 42 | * [.Tween#yoyo(state, [_easingReverse])](#TWEEN.Tween.Tween+yoyo) 43 | * [.Tween#easing(_easingFunction)](#TWEEN.Tween.Tween+easing) 44 | * [.Tween#interpolation(_interpolationFunction)](#TWEEN.Tween.Tween+interpolation) 45 | * [.Tween#update(time, [preserve], [forceTime])](#TWEEN.Tween.Tween+update) 46 | * [.Plugins](#TWEEN.Plugins) : object 47 | * [.now](#TWEEN.now) ⇒ 48 | * [.add(tween)](#TWEEN.add) 49 | * [.onTick(fn)](#TWEEN.onTick) 50 | * [.FrameThrottle(frameCount)](#TWEEN.FrameThrottle) 51 | * [.ToggleLagSmoothing(state)](#TWEEN.ToggleLagSmoothing) 52 | * [.autoPlay(state)](#TWEEN.autoPlay) 53 | * [.removeAll()](#TWEEN.removeAll) 54 | * [.get(tween)](#TWEEN.get) ⇒ Tween 55 | * [.has(tween)](#TWEEN.has) ⇒ Boolean 56 | * [.remove(tween)](#TWEEN.remove) 57 | * [.update([time], [preserve])](#TWEEN.update) 58 | * [.isRunning()](#TWEEN.isRunning) ⇒ Boolean 59 | * [.isLagSmoothing()](#TWEEN.isLagSmoothing) ⇒ Boolean 60 | 61 | 62 | 63 | ### TWEEN.Easing : object 64 | List of full easings 65 | 66 | **Kind**: static namespace of [TWEEN](#TWEEN) 67 | **Example** 68 | ```js 69 | import {Tween, Easing} from 'es6-tween' 70 | 71 | // then set via new Tween({x:0}).to({x:100}, 1000).easing(Easing.Quadratic.InOut).start() 72 | ``` 73 | 74 | 75 | ### TWEEN.Interpolation : object 76 | List of full Interpolation 77 | 78 | **Kind**: static namespace of [TWEEN](#TWEEN) 79 | **Example** 80 | ```js 81 | import {Interpolation, Tween} from 'es6-tween' 82 | 83 | let bezier = Interpolation.Bezier 84 | new Tween({x:0}).to({x:[0, 4, 8, 12, 15, 20, 30, 40, 20, 40, 10, 50]}, 1000).interpolation(bezier).start() 85 | ``` 86 | 87 | 88 | ### TWEEN.Interpolator ⇒ function 89 | Tween helper for plugins 90 | 91 | **Kind**: static namespace of [TWEEN](#TWEEN) 92 | **Returns**: function - Returns function that accepts number between `0-1` 93 | 94 | | Param | Type | Description | 95 | | --- | --- | --- | 96 | | a | any | Initial position | 97 | | b | any | End position | 98 | 99 | 100 | 101 | ### TWEEN.Timeline : object 102 | Timeline main constructor. 103 | 104 | It works same as `Tween` instance, using `.repeat`, `.restart` or `etc` works like a `Tween`, so please see `Tween` class for methods 105 | 106 | **Kind**: static namespace of [TWEEN](#TWEEN) 107 | **Extends**: Tween 108 | 109 | | Param | Type | Description | 110 | | --- | --- | --- | 111 | | [params] | Object | Default params for new tweens | 112 | 113 | **Example** 114 | ```js 115 | let tl = new Timeline({delay:200}) 116 | ``` 117 | 118 | 119 | ### TWEEN.Tween : object 120 | Tween main constructor 121 | 122 | **Kind**: static namespace of [TWEEN](#TWEEN) 123 | 124 | | Param | Type | Description | 125 | | --- | --- | --- | 126 | | node | Object \| Element | Node Element or Tween initial object | 127 | | [object] | Object | If Node Element is using, second argument is used for Tween initial object | 128 | 129 | **Example** 130 | ```js 131 | let tween = new Tween(myNode, {width:'100px'}).to({width:'300px'}, 2000).start() 132 | ``` 133 | 134 | * [.Tween](#TWEEN.Tween) : object 135 | * [.Tween#setMaxListener(count)](#TWEEN.Tween.Tween+setMaxListener) 136 | * [.Tween#on(event, callback)](#TWEEN.Tween.Tween+on) 137 | * [.Tween#once(event, callback)](#TWEEN.Tween.Tween+once) 138 | * [.Tween#off(event, callback)](#TWEEN.Tween.Tween+off) 139 | * [.Tween#emit(event)](#TWEEN.Tween.Tween+emit) 140 | * [.Tween#isPlaying()](#TWEEN.Tween.Tween+isPlaying) ⇒ boolean 141 | * [.Tween#isStarted()](#TWEEN.Tween.Tween+isStarted) ⇒ boolean 142 | * [.Tween#reverse([state])](#TWEEN.Tween.Tween+reverse) 143 | * [.Tween#reversed()](#TWEEN.Tween.Tween+reversed) ⇒ boolean 144 | * [.Tween#pause()](#TWEEN.Tween.Tween+pause) 145 | * [.Tween#play()](#TWEEN.Tween.Tween+play) 146 | * [.Tween#restart([noDelay])](#TWEEN.Tween.Tween+restart) 147 | * ~~[.Tween#seek(time, [keepPlaying])](#TWEEN.Tween.Tween+seek)~~ 148 | * ~~[.Tween#duration(amount)](#TWEEN.Tween.Tween+duration)~~ 149 | * [.Tween#to(properties, [duration])](#TWEEN.Tween.Tween+to) 150 | * [.Tween#start(time)](#TWEEN.Tween.Tween+start) 151 | * [.Tween#stop()](#TWEEN.Tween.Tween+stop) 152 | * [.Tween#delay(amount)](#TWEEN.Tween.Tween+delay) 153 | * [.Tween#chainedTweens(arguments)](#TWEEN.Tween.Tween+chainedTweens) 154 | * [.Tween#repeat(amount)](#TWEEN.Tween.Tween+repeat) 155 | * [.Tween#reverseDelay(amount)](#TWEEN.Tween.Tween+reverseDelay) 156 | * [.Tween#yoyo(state, [_easingReverse])](#TWEEN.Tween.Tween+yoyo) 157 | * [.Tween#easing(_easingFunction)](#TWEEN.Tween.Tween+easing) 158 | * [.Tween#interpolation(_interpolationFunction)](#TWEEN.Tween.Tween+interpolation) 159 | * [.Tween#update(time, [preserve], [forceTime])](#TWEEN.Tween.Tween+update) 160 | 161 | 162 | 163 | #### Tween.Tween#setMaxListener(count) 164 | Sets max `event` listener's count to Events system 165 | 166 | **Kind**: static method of [Tween](#TWEEN.Tween) 167 | 168 | | Param | Type | Default | Description | 169 | | --- | --- | --- | --- | 170 | | count | number | 15 | Event listener's count | 171 | 172 | 173 | 174 | #### Tween.Tween#on(event, callback) 175 | Adds `event` to Events system 176 | 177 | **Kind**: static method of [Tween](#TWEEN.Tween) 178 | 179 | | Param | Type | Description | 180 | | --- | --- | --- | 181 | | event | string | Event listener name | 182 | | callback | function | Event listener callback | 183 | 184 | 185 | 186 | #### Tween.Tween#once(event, callback) 187 | Adds `event` to Events system. 188 | Removes itself after fired once 189 | 190 | **Kind**: static method of [Tween](#TWEEN.Tween) 191 | 192 | | Param | Type | Description | 193 | | --- | --- | --- | 194 | | event | string | Event listener name | 195 | | callback | function | Event listener callback | 196 | 197 | 198 | 199 | #### Tween.Tween#off(event, callback) 200 | Removes `event` from Events system 201 | 202 | **Kind**: static method of [Tween](#TWEEN.Tween) 203 | 204 | | Param | Type | Description | 205 | | --- | --- | --- | 206 | | event | string | Event listener name | 207 | | callback | function | Event listener callback | 208 | 209 | 210 | 211 | #### Tween.Tween#emit(event) 212 | Emits/Fired/Trigger `event` from Events system listeners 213 | 214 | **Kind**: static method of [Tween](#TWEEN.Tween) 215 | 216 | | Param | Type | Description | 217 | | --- | --- | --- | 218 | | event | string | Event listener name | 219 | 220 | 221 | 222 | #### Tween.Tween#isPlaying() ⇒ boolean 223 | **Kind**: static method of [Tween](#TWEEN.Tween) 224 | **Returns**: boolean - State of playing of tween 225 | **Example** 226 | ```js 227 | tween.isPlaying() // returns `true` if tween in progress 228 | ``` 229 | 230 | 231 | #### Tween.Tween#isStarted() ⇒ boolean 232 | **Kind**: static method of [Tween](#TWEEN.Tween) 233 | **Returns**: boolean - State of started of tween 234 | **Example** 235 | ```js 236 | tween.isStarted() // returns `true` if tween in started 237 | ``` 238 | 239 | 240 | #### Tween.Tween#reverse([state]) 241 | Reverses the tween state/direction 242 | 243 | **Kind**: static method of [Tween](#TWEEN.Tween) 244 | 245 | | Param | Type | Description | 246 | | --- | --- | --- | 247 | | [state] | boolean | Set state of current reverse | 248 | 249 | **Example** 250 | ```js 251 | tween.reverse() 252 | ``` 253 | 254 | 255 | #### Tween.Tween#reversed() ⇒ boolean 256 | **Kind**: static method of [Tween](#TWEEN.Tween) 257 | **Returns**: boolean - State of reversed 258 | **Example** 259 | ```js 260 | tween.reversed() // returns `true` if tween in reversed state 261 | ``` 262 | 263 | 264 | #### Tween.Tween#pause() 265 | Pauses tween 266 | 267 | **Kind**: static method of [Tween](#TWEEN.Tween) 268 | **Example** 269 | ```js 270 | tween.pause() 271 | ``` 272 | 273 | 274 | #### Tween.Tween#play() 275 | Play/Resume the tween 276 | 277 | **Kind**: static method of [Tween](#TWEEN.Tween) 278 | **Example** 279 | ```js 280 | tween.play() 281 | ``` 282 | 283 | 284 | #### Tween.Tween#restart([noDelay]) 285 | Restarts tween from initial value 286 | 287 | **Kind**: static method of [Tween](#TWEEN.Tween) 288 | 289 | | Param | Type | Description | 290 | | --- | --- | --- | 291 | | [noDelay] | boolean | If this param is set to `true`, restarts tween without `delay` | 292 | 293 | **Example** 294 | ```js 295 | tween.restart() 296 | ``` 297 | 298 | 299 | #### ~~Tween.Tween#seek(time, [keepPlaying])~~ 300 | ***Deprecated*** 301 | 302 | Seek tween value by `time`. Note: Not works as excepted. PR are welcome 303 | 304 | **Kind**: static method of [Tween](#TWEEN.Tween) 305 | 306 | | Param | Type | Description | 307 | | --- | --- | --- | 308 | | time | Time | Tween update time | 309 | | [keepPlaying] | boolean | When this param is set to `false`, tween pausing after seek | 310 | 311 | **Example** 312 | ```js 313 | tween.seek(500) 314 | ``` 315 | 316 | 317 | #### ~~Tween.Tween#duration(amount)~~ 318 | ***Deprecated*** 319 | 320 | Sets tween duration 321 | 322 | **Kind**: static method of [Tween](#TWEEN.Tween) 323 | 324 | | Param | Type | Description | 325 | | --- | --- | --- | 326 | | amount | number | Duration is milliseconds | 327 | 328 | **Example** 329 | ```js 330 | tween.duration(2000) 331 | ``` 332 | 333 | 334 | #### Tween.Tween#to(properties, [duration]) 335 | Sets target value and duration 336 | 337 | **Kind**: static method of [Tween](#TWEEN.Tween) 338 | 339 | | Param | Type | Default | Description | 340 | | --- | --- | --- | --- | 341 | | properties | object | | Target value (to value) | 342 | | [duration] | number \| Object | 1000 | Duration of tween | 343 | 344 | **Example** 345 | ```js 346 | let tween = new Tween({x:0}).to({x:100}, 2000) 347 | ``` 348 | 349 | 350 | #### Tween.Tween#start(time) 351 | Start the tweening 352 | 353 | **Kind**: static method of [Tween](#TWEEN.Tween) 354 | 355 | | Param | Type | Description | 356 | | --- | --- | --- | 357 | | time | number \| string | setting manual time instead of Current browser timestamp or like `+1000` relative to current timestamp | 358 | 359 | **Example** 360 | ```js 361 | tween.start() 362 | ``` 363 | 364 | 365 | #### Tween.Tween#stop() 366 | Stops the tween 367 | 368 | **Kind**: static method of [Tween](#TWEEN.Tween) 369 | **Example** 370 | ```js 371 | tween.stop() 372 | ``` 373 | 374 | 375 | #### Tween.Tween#delay(amount) 376 | Set delay of tween 377 | 378 | **Kind**: static method of [Tween](#TWEEN.Tween) 379 | 380 | | Param | Type | Description | 381 | | --- | --- | --- | 382 | | amount | number | Sets tween delay / wait duration | 383 | 384 | **Example** 385 | ```js 386 | tween.delay(500) 387 | ``` 388 | 389 | 390 | #### Tween.Tween#chainedTweens(arguments) 391 | Chained tweens 392 | 393 | **Kind**: static method of [Tween](#TWEEN.Tween) 394 | 395 | | Param | Type | Description | 396 | | --- | --- | --- | 397 | | arguments | any | Arguments list | 398 | 399 | **Example** 400 | ```js 401 | tween.chainedTweens(tween1, tween2) 402 | ``` 403 | 404 | 405 | #### Tween.Tween#repeat(amount) 406 | Sets how times tween is repeating 407 | 408 | **Kind**: static method of [Tween](#TWEEN.Tween) 409 | 410 | | Param | Type | Description | 411 | | --- | --- | --- | 412 | | amount | amount | the times of repeat | 413 | 414 | **Example** 415 | ```js 416 | tween.repeat(5) 417 | ``` 418 | 419 | 420 | #### Tween.Tween#reverseDelay(amount) 421 | Set delay of each repeat alternate of tween 422 | 423 | **Kind**: static method of [Tween](#TWEEN.Tween) 424 | 425 | | Param | Type | Description | 426 | | --- | --- | --- | 427 | | amount | number | Sets tween repeat alternate delay / repeat alternate wait duration | 428 | 429 | **Example** 430 | ```js 431 | tween.reverseDelay(500) 432 | ``` 433 | 434 | 435 | #### Tween.Tween#yoyo(state, [_easingReverse]) 436 | Set `yoyo` state (enables reverse in repeat) 437 | 438 | **Kind**: static method of [Tween](#TWEEN.Tween) 439 | 440 | | Param | Type | Description | 441 | | --- | --- | --- | 442 | | state | boolean | Enables alternate direction for repeat | 443 | | [_easingReverse] | function | Easing function in reverse direction | 444 | 445 | **Example** 446 | ```js 447 | tween.yoyo(true) 448 | ``` 449 | 450 | 451 | #### Tween.Tween#easing(_easingFunction) 452 | Set easing 453 | 454 | **Kind**: static method of [Tween](#TWEEN.Tween) 455 | 456 | | Param | Type | Description | 457 | | --- | --- | --- | 458 | | _easingFunction | function | Easing function, applies in non-reverse direction if Tween#yoyo second argument is applied | 459 | 460 | **Example** 461 | ```js 462 | tween.easing(Easing.Elastic.InOut) 463 | ``` 464 | 465 | 466 | #### Tween.Tween#interpolation(_interpolationFunction) 467 | Set interpolation 468 | 469 | **Kind**: static method of [Tween](#TWEEN.Tween) 470 | 471 | | Param | Type | Description | 472 | | --- | --- | --- | 473 | | _interpolationFunction | function | Interpolation function | 474 | 475 | **Example** 476 | ```js 477 | tween.interpolation(Interpolation.Bezier) 478 | ``` 479 | 480 | 481 | #### Tween.Tween#update(time, [preserve], [forceTime]) 482 | Updates initial object to target value by given `time` 483 | 484 | **Kind**: static method of [Tween](#TWEEN.Tween) 485 | 486 | | Param | Type | Description | 487 | | --- | --- | --- | 488 | | time | Time | Current time | 489 | | [preserve] | boolean | Prevents from removing tween from store | 490 | | [forceTime] | boolean | Forces to be frame rendered, even mismatching time | 491 | 492 | **Example** 493 | ```js 494 | tween.update(100) 495 | ``` 496 | 497 | 498 | ### TWEEN.Plugins : object 499 | The plugins store object 500 | 501 | **Kind**: static namespace of [TWEEN](#TWEEN) 502 | **Example** 503 | ```js 504 | let num = Plugins.num = function (node, start, end) { 505 | return t => start + (end - start) * t 506 | } 507 | ``` 508 | 509 | 510 | ### TWEEN.now ⇒ 511 | Get browser/Node.js current time-stamp 512 | 513 | **Kind**: static constant of [TWEEN](#TWEEN) 514 | **Returns**: Normalised current time-stamp in milliseconds 515 | **Example** 516 | ```js 517 | TWEEN.now 518 | ``` 519 | 520 | 521 | ### TWEEN.add(tween) 522 | Adds tween to list 523 | 524 | **Kind**: static method of [TWEEN](#TWEEN) 525 | 526 | | Param | Type | Description | 527 | | --- | --- | --- | 528 | | tween | Tween | Tween instance | 529 | 530 | **Example** 531 | ```js 532 | let tween = new Tween({x:0}) 533 | tween.to({x:200}, 1000) 534 | TWEEN.add(tween) 535 | ``` 536 | 537 | 538 | ### TWEEN.onTick(fn) 539 | Adds ticker like event 540 | 541 | **Kind**: static method of [TWEEN](#TWEEN) 542 | 543 | | Param | Type | Description | 544 | | --- | --- | --- | 545 | | fn | function | callback | 546 | 547 | **Example** 548 | ```js 549 | TWEEN.onTick(time => console.log(time)) 550 | ``` 551 | 552 | 553 | ### TWEEN.FrameThrottle(frameCount) 554 | Sets after how much frames empty updating should stop 555 | 556 | **Kind**: static method of [TWEEN](#TWEEN) 557 | 558 | | Param | Type | Default | Description | 559 | | --- | --- | --- | --- | 560 | | frameCount | number | 120 | count of frames that should stop after all tweens removed | 561 | 562 | **Example** 563 | ```js 564 | TWEEN.FrameThrottle(60) 565 | ``` 566 | 567 | 568 | ### TWEEN.ToggleLagSmoothing(state) 569 | Handle lag, useful if you have rendering Canvas or DOM objects or using es6-tween plugins 570 | 571 | **Kind**: static method of [TWEEN](#TWEEN) 572 | 573 | | Param | Type | Default | Description | 574 | | --- | --- | --- | --- | 575 | | state | number | true | handle lag state | 576 | 577 | **Example** 578 | ```js 579 | TWEEN.ToggleLagSmoothing(false) 580 | ``` 581 | 582 | 583 | ### TWEEN.autoPlay(state) 584 | Runs update loop automaticlly 585 | 586 | **Kind**: static method of [TWEEN](#TWEEN) 587 | 588 | | Param | Type | Description | 589 | | --- | --- | --- | 590 | | state | Boolean | State of auto-run of update loop | 591 | 592 | **Example** 593 | ```js 594 | TWEEN.autoPlay(true) 595 | ``` 596 | 597 | 598 | ### TWEEN.removeAll() 599 | Removes all tweens from list 600 | 601 | **Kind**: static method of [TWEEN](#TWEEN) 602 | **Example** 603 | ```js 604 | TWEEN.removeAll() // removes all tweens, stored in global tweens list 605 | ``` 606 | 607 | 608 | ### TWEEN.get(tween) ⇒ Tween 609 | **Kind**: static method of [TWEEN](#TWEEN) 610 | **Returns**: Tween - Matched tween 611 | 612 | | Param | Type | Description | 613 | | --- | --- | --- | 614 | | tween | Tween | Tween Instance to be matched | 615 | 616 | **Example** 617 | ```js 618 | TWEEN.get(tween) 619 | ``` 620 | 621 | 622 | ### TWEEN.has(tween) ⇒ Boolean 623 | **Kind**: static method of [TWEEN](#TWEEN) 624 | **Returns**: Boolean - Status of Exists tween or not 625 | 626 | | Param | Type | Description | 627 | | --- | --- | --- | 628 | | tween | Tween | Tween Instance to be matched | 629 | 630 | **Example** 631 | ```js 632 | TWEEN.has(tween) 633 | ``` 634 | 635 | 636 | ### TWEEN.remove(tween) 637 | Removes tween from list 638 | 639 | **Kind**: static method of [TWEEN](#TWEEN) 640 | 641 | | Param | Type | Description | 642 | | --- | --- | --- | 643 | | tween | Tween | Tween instance | 644 | 645 | **Example** 646 | ```js 647 | TWEEN.remove(tween) 648 | ``` 649 | 650 | 651 | ### TWEEN.update([time], [preserve]) 652 | Updates global tweens by given time 653 | 654 | **Kind**: static method of [TWEEN](#TWEEN) 655 | 656 | | Param | Type | Description | 657 | | --- | --- | --- | 658 | | [time] | number | Timestamp | 659 | | [preserve] | Boolean | Prevents tween to be removed after finish | 660 | 661 | **Example** 662 | ```js 663 | TWEEN.update(500) 664 | ``` 665 | 666 | 667 | ### TWEEN.isRunning() ⇒ Boolean 668 | The state of ticker running 669 | 670 | **Kind**: static method of [TWEEN](#TWEEN) 671 | **Returns**: Boolean - Status of running updates on all tweens 672 | **Example** 673 | ```js 674 | TWEEN.isRunning() 675 | ``` 676 | 677 | 678 | ### TWEEN.isLagSmoothing() ⇒ Boolean 679 | Returns state of lag smoothing handling 680 | 681 | **Kind**: static method of [TWEEN](#TWEEN) 682 | **Returns**: Boolean - Status of lag smoothing state 683 | **Example** 684 | ```js 685 | TWEEN.isRunning() 686 | ``` 687 | -------------------------------------------------------------------------------- /src/Tween.js: -------------------------------------------------------------------------------- 1 | import { add, now, Plugins, remove, isRunning, isLagSmoothing } from './core' 2 | import Easing from './Easing' 3 | import Interpolation from './Interpolation' 4 | import NodeCache, { Store } from './NodeCache' 5 | import Selector from './selector' 6 | import { 7 | decompose, 8 | decomposeString, 9 | recompose, 10 | deepCopy, 11 | SET_NESTED, 12 | EVENT_CALLBACK, 13 | CHAINED_TWEENS, 14 | EVENT_UPDATE, 15 | EVENT_COMPLETE, 16 | EVENT_START, 17 | EVENT_REPEAT, 18 | EVENT_REVERSE, 19 | EVENT_PAUSE, 20 | EVENT_PLAY, 21 | EVENT_RESTART, 22 | EVENT_STOP, 23 | EVENT_SEEK, 24 | FRAME_MS, 25 | TOO_LONG_FRAME_MS 26 | } from './constants' 27 | 28 | let _id = 0 // Unique ID 29 | const defaultEasing = Easing.Linear.None 30 | 31 | /** 32 | * Tween main constructor 33 | * @constructor 34 | * @class 35 | * @namespace TWEEN.Tween 36 | * @param {Object|Element} node Node Element or Tween initial object 37 | * @param {Object=} object If Node Element is using, second argument is used for Tween initial object 38 | * @example let tween = new Tween(myNode, {width:'100px'}).to({width:'300px'}, 2000).start() 39 | */ 40 | class Tween { 41 | /** 42 | * Easier way to call the Tween 43 | * @param {Element} node DOM Element 44 | * @param {object} object - Initial value 45 | * @param {object} to - Target value 46 | * @param {object} params - Options of tweens 47 | * @example Tween.fromTo(node, {x:0}, {x:200}, {duration:1000}) 48 | * @memberof TWEEN.Tween 49 | * @static 50 | */ 51 | static fromTo (node, object, to, params = {}) { 52 | params.quickRender = params.quickRender ? params.quickRender : !to 53 | const tween = new Tween(node, object).to(to, params) 54 | if (params.quickRender) { 55 | tween.render().update(tween._startTime) 56 | tween._rendered = false 57 | tween._onStartCallbackFired = false 58 | } 59 | return tween 60 | } 61 | /** 62 | * Easier way calling constructor only applies the `to` value, useful for CSS Animation 63 | * @param {Element} node DOM Element 64 | * @param {object} to - Target value 65 | * @param {object} params - Options of tweens 66 | * @example Tween.to(node, {x:200}, {duration:1000}) 67 | * @memberof TWEEN.Tween 68 | * @static 69 | */ 70 | static to (node, to, params) { 71 | return Tween.fromTo(node, null, to, params) 72 | } 73 | /** 74 | * Easier way calling constructor only applies the `from` value, useful for CSS Animation 75 | * @param {Element} node DOM Element 76 | * @param {object} from - Initial value 77 | * @param {object} params - Options of tweens 78 | * @example Tween.from(node, {x:200}, {duration:1000}) 79 | * @memberof TWEEN.Tween 80 | * @static 81 | */ 82 | static from (node, from, params) { 83 | return Tween.fromTo(node, from, null, params) 84 | } 85 | constructor (node, object) { 86 | this.id = _id++ 87 | if (!!node && typeof node === 'object' && !object && !node.nodeType) { 88 | object = this.object = node 89 | node = null 90 | } else if (!!node && (node.nodeType || node.length || typeof node === 'string')) { 91 | node = this.node = Selector(node) 92 | object = this.object = NodeCache(node, object, this) 93 | } 94 | this._valuesEnd = null 95 | this._valuesStart = Array.isArray(object) ? [] : {} 96 | 97 | this._duration = 1000 98 | this._easingFunction = defaultEasing 99 | this._easingReverse = defaultEasing 100 | this._interpolationFunction = Interpolation.Linear 101 | 102 | this._startTime = 0 103 | this._initTime = 0 104 | this._delayTime = 0 105 | this._repeat = 0 106 | this._r = 0 107 | this._isPlaying = false 108 | this._yoyo = false 109 | this._reversed = false 110 | 111 | this._onStartCallbackFired = false 112 | this._pausedTime = null 113 | this._isFinite = true 114 | this._maxListener = 15 115 | this._chainedTweensCount = 0 116 | this._prevTime = null 117 | 118 | return this 119 | } 120 | 121 | /** 122 | * Sets max `event` listener's count to Events system 123 | * @param {number} count - Event listener's count 124 | * @memberof TWEEN.Tween 125 | */ 126 | setMaxListener (count = 15) { 127 | this._maxListener = count 128 | return this 129 | } 130 | 131 | /** 132 | * Adds `event` to Events system 133 | * @param {string} event - Event listener name 134 | * @param {Function} callback - Event listener callback 135 | * @memberof TWEEN.Tween 136 | */ 137 | on (event, callback) { 138 | const { _maxListener } = this 139 | const callbackName = event + EVENT_CALLBACK 140 | for (let i = 0; i < _maxListener; i++) { 141 | const callbackId = callbackName + i 142 | if (!this[callbackId]) { 143 | this[callbackId] = callback 144 | break 145 | } 146 | } 147 | return this 148 | } 149 | 150 | /** 151 | * Adds `event` to Events system. 152 | * Removes itself after fired once 153 | * @param {string} event - Event listener name 154 | * @param {Function} callback - Event listener callback 155 | * @memberof TWEEN.Tween 156 | */ 157 | once (event, callback) { 158 | const { _maxListener } = this 159 | const callbackName = event + EVENT_CALLBACK 160 | for (let i = 0; i < _maxListener; i++) { 161 | const callbackId = callbackName + i 162 | if (!this[callbackId]) { 163 | this[callbackId] = (...args) => { 164 | callback.apply(this, args) 165 | this[callbackId] = null 166 | } 167 | break 168 | } 169 | } 170 | return this 171 | } 172 | 173 | /** 174 | * Removes `event` from Events system 175 | * @param {string} event - Event listener name 176 | * @param {Function} callback - Event listener callback 177 | * @memberof TWEEN.Tween 178 | */ 179 | off (event, callback) { 180 | const { _maxListener } = this 181 | const callbackName = event + EVENT_CALLBACK 182 | for (let i = 0; i < _maxListener; i++) { 183 | const callbackId = callbackName + i 184 | if (this[callbackId] === callback) { 185 | this[callbackId] = null 186 | } 187 | } 188 | return this 189 | } 190 | 191 | /** 192 | * Emits/Fired/Trigger `event` from Events system listeners 193 | * @param {string} event - Event listener name 194 | * @memberof TWEEN.Tween 195 | */ 196 | emit (event, arg1, arg2, arg3) { 197 | const { _maxListener } = this 198 | const callbackName = event + EVENT_CALLBACK 199 | 200 | if (!this[callbackName + 0]) { 201 | return this 202 | } 203 | for (let i = 0; i < _maxListener; i++) { 204 | const callbackId = callbackName + i 205 | if (this[callbackId]) { 206 | this[callbackId](arg1, arg2, arg3) 207 | } 208 | } 209 | return this 210 | } 211 | 212 | /** 213 | * @return {boolean} State of playing of tween 214 | * @example tween.isPlaying() // returns `true` if tween in progress 215 | * @memberof TWEEN.Tween 216 | */ 217 | isPlaying () { 218 | return this._isPlaying 219 | } 220 | 221 | /** 222 | * @return {boolean} State of started of tween 223 | * @example tween.isStarted() // returns `true` if tween in started 224 | * @memberof TWEEN.Tween 225 | */ 226 | isStarted () { 227 | return this._onStartCallbackFired 228 | } 229 | 230 | /** 231 | * Reverses the tween state/direction 232 | * @example tween.reverse() 233 | * @param {boolean=} state Set state of current reverse 234 | * @memberof TWEEN.Tween 235 | */ 236 | reverse (state) { 237 | const { _reversed } = this 238 | 239 | this._reversed = state !== undefined ? state : !_reversed 240 | 241 | return this 242 | } 243 | 244 | /** 245 | * @return {boolean} State of reversed 246 | * @example tween.reversed() // returns `true` if tween in reversed state 247 | * @memberof TWEEN.Tween 248 | */ 249 | reversed () { 250 | return this._reversed 251 | } 252 | 253 | /** 254 | * Pauses tween 255 | * @example tween.pause() 256 | * @memberof TWEEN.Tween 257 | */ 258 | pause () { 259 | if (!this._isPlaying) { 260 | return this 261 | } 262 | 263 | this._isPlaying = false 264 | 265 | remove(this) 266 | this._pausedTime = now() 267 | 268 | return this.emit(EVENT_PAUSE, this.object) 269 | } 270 | 271 | /** 272 | * Play/Resume the tween 273 | * @example tween.play() 274 | * @memberof TWEEN.Tween 275 | */ 276 | play () { 277 | if (this._isPlaying) { 278 | return this 279 | } 280 | 281 | this._isPlaying = true 282 | 283 | this._startTime += now() - this._pausedTime 284 | this._initTime = this._startTime 285 | add(this) 286 | this._pausedTime = now() 287 | 288 | return this.emit(EVENT_PLAY, this.object) 289 | } 290 | 291 | /** 292 | * Restarts tween from initial value 293 | * @param {boolean=} noDelay If this param is set to `true`, restarts tween without `delay` 294 | * @example tween.restart() 295 | * @memberof TWEEN.Tween 296 | */ 297 | restart (noDelay) { 298 | this._repeat = this._r 299 | this.reassignValues() 300 | 301 | add(this) 302 | 303 | return this.emit(EVENT_RESTART, this.object) 304 | } 305 | 306 | /** 307 | * Seek tween value by `time`. Note: Not works as excepted. PR are welcome 308 | * @param {Time} time Tween update time 309 | * @param {boolean=} keepPlaying When this param is set to `false`, tween pausing after seek 310 | * @example tween.seek(500) 311 | * @memberof TWEEN.Tween 312 | * @deprecated Not works as excepted, so we deprecated this method 313 | */ 314 | seek (time, keepPlaying) { 315 | const { _duration, _initTime, _startTime, _reversed } = this 316 | 317 | let updateTime = _initTime + time 318 | this._isPlaying = true 319 | 320 | if (updateTime < _startTime && _startTime >= _initTime) { 321 | this._startTime -= _duration 322 | this._reversed = !_reversed 323 | } 324 | 325 | this.update(time, false) 326 | 327 | this.emit(EVENT_SEEK, time, this.object) 328 | 329 | return keepPlaying ? this : this.pause() 330 | } 331 | 332 | /** 333 | * Sets tween duration 334 | * @param {number} amount Duration is milliseconds 335 | * @example tween.duration(2000) 336 | * @memberof TWEEN.Tween 337 | * @deprecated Not works as excepted and useless, so we deprecated this method 338 | */ 339 | duration (amount) { 340 | this._duration = typeof amount === 'function' ? amount(this._duration) : amount 341 | 342 | return this 343 | } 344 | 345 | /** 346 | * Sets target value and duration 347 | * @param {object} properties Target value (to value) 348 | * @param {number|Object=} [duration=1000] Duration of tween 349 | * @example let tween = new Tween({x:0}).to({x:100}, 2000) 350 | * @memberof TWEEN.Tween 351 | */ 352 | to (properties, duration = 1000, maybeUsed) { 353 | this._valuesEnd = properties 354 | 355 | if (typeof duration === 'number' || typeof duration === 'function') { 356 | this._duration = typeof duration === 'function' ? duration(this._duration) : duration 357 | } else if (typeof duration === 'object') { 358 | for (const prop in duration) { 359 | if (typeof this[prop] === 'function') { 360 | const [arg1 = null, arg2 = null, arg3 = null, arg4 = null] = Array.isArray(duration[prop]) 361 | ? duration[prop] 362 | : [duration[prop]] 363 | this[prop](arg1, arg2, arg3, arg4) 364 | } 365 | } 366 | } 367 | 368 | return this 369 | } 370 | 371 | /** 372 | * Renders and computes value at first render 373 | * @private 374 | * @memberof TWEEN.Tween 375 | */ 376 | render () { 377 | if (this._rendered) { 378 | return this 379 | } 380 | let { _valuesStart, _valuesEnd, object, node, InitialValues } = this 381 | 382 | SET_NESTED(object) 383 | SET_NESTED(_valuesEnd) 384 | 385 | if (node && node.queueID && Store[node.queueID]) { 386 | const prevTweenByNode = Store[node.queueID] 387 | if (prevTweenByNode.propNormaliseRequired && prevTweenByNode.tween !== this) { 388 | for (const property in _valuesEnd) { 389 | if (prevTweenByNode.tween._valuesEnd[property] !== undefined) { 390 | // delete prevTweenByNode.tween._valuesEnd[property]; 391 | } 392 | } 393 | prevTweenByNode.normalisedProp = true 394 | prevTweenByNode.propNormaliseRequired = false 395 | } 396 | } 397 | 398 | if (node && InitialValues) { 399 | if (!object || Object.keys(object).length === 0) { 400 | object = this.object = NodeCache(node, InitialValues(node, _valuesEnd), this) 401 | } else if (!_valuesEnd || Object.keys(_valuesEnd).length === 0) { 402 | _valuesEnd = this._valuesEnd = InitialValues(node, object) 403 | } 404 | } 405 | if (!_valuesStart.processed) { 406 | for (const property in _valuesEnd) { 407 | let start = object && object[property] && deepCopy(object[property]) 408 | let end = _valuesEnd[property] 409 | if (Plugins[property] && Plugins[property].init) { 410 | Plugins[property].init.call(this, start, end, property, object) 411 | if (start === undefined && _valuesStart[property]) { 412 | start = _valuesStart[property] 413 | } 414 | if (Plugins[property].skipProcess) { 415 | continue 416 | } 417 | } 418 | if ( 419 | (typeof start === 'number' && isNaN(start)) || 420 | start === null || 421 | end === null || 422 | start === false || 423 | end === false || 424 | start === undefined || 425 | end === undefined || 426 | start === end 427 | ) { 428 | continue 429 | } 430 | _valuesStart[property] = start 431 | if (Array.isArray(end)) { 432 | if (!Array.isArray(start)) { 433 | end.unshift(start) 434 | for (let i = 0, len = end.length; i < len; i++) { 435 | if (typeof end[i] === 'string') { 436 | end[i] = decomposeString(end[i]) 437 | } 438 | } 439 | } else { 440 | if (end.isString && object[property].isString && !start.isString) { 441 | start.isString = true 442 | } else { 443 | decompose(property, object, _valuesStart, _valuesEnd) 444 | } 445 | } 446 | } else { 447 | decompose(property, object, _valuesStart, _valuesEnd) 448 | } 449 | if (typeof start === 'number' && typeof end === 'string' && end[1] === '=') { 450 | continue 451 | } 452 | } 453 | _valuesStart.processed = true 454 | } 455 | 456 | if (Tween.Renderer && this.node && Tween.Renderer.init) { 457 | Tween.Renderer.init.call(this, object, _valuesStart, _valuesEnd) 458 | this.__render = true 459 | } 460 | 461 | this._rendered = true 462 | 463 | return this 464 | } 465 | 466 | /** 467 | * Start the tweening 468 | * @param {number|string} time setting manual time instead of Current browser timestamp or like `+1000` relative to current timestamp 469 | * @example tween.start() 470 | * @memberof TWEEN.Tween 471 | */ 472 | start (time) { 473 | this._startTime = time !== undefined ? (typeof time === 'string' ? now() + parseFloat(time) : time) : now() 474 | this._startTime += this._delayTime 475 | this._initTime = this._prevTime = this._startTime 476 | 477 | this._onStartCallbackFired = false 478 | this._rendered = false 479 | this._isPlaying = true 480 | 481 | add(this) 482 | 483 | return this 484 | } 485 | 486 | /** 487 | * Stops the tween 488 | * @example tween.stop() 489 | * @memberof TWEEN.Tween 490 | */ 491 | stop () { 492 | let { _isPlaying, _isFinite, object, _startTime, _duration, _r, _yoyo, _reversed } = this 493 | 494 | if (!_isPlaying) { 495 | return this 496 | } 497 | 498 | let atStart = _isFinite ? (_r + 1) % 2 === 1 : !_reversed 499 | 500 | this._reversed = false 501 | 502 | if (_yoyo && atStart) { 503 | this.update(_startTime) 504 | } else { 505 | this.update(_startTime + _duration) 506 | } 507 | remove(this) 508 | 509 | return this.emit(EVENT_STOP, object) 510 | } 511 | 512 | /** 513 | * Set delay of tween 514 | * @param {number} amount Sets tween delay / wait duration 515 | * @example tween.delay(500) 516 | * @memberof TWEEN.Tween 517 | */ 518 | delay (amount) { 519 | this._delayTime = typeof amount === 'function' ? amount(this._delayTime) : amount 520 | 521 | return this 522 | } 523 | 524 | /** 525 | * Chained tweens 526 | * @param {any} arguments Arguments list 527 | * @example tween.chainedTweens(tween1, tween2) 528 | * @memberof TWEEN.Tween 529 | */ 530 | chainedTweens () { 531 | this._chainedTweensCount = arguments.length 532 | if (!this._chainedTweensCount) { 533 | return this 534 | } 535 | for (let i = 0, len = this._chainedTweensCount; i < len; i++) { 536 | this[CHAINED_TWEENS + i] = arguments[i] 537 | } 538 | 539 | return this 540 | } 541 | 542 | /** 543 | * Sets how times tween is repeating 544 | * @param {amount} amount the times of repeat 545 | * @example tween.repeat(5) 546 | * @memberof TWEEN.Tween 547 | */ 548 | repeat (amount) { 549 | this._repeat = !this._duration ? 0 : typeof amount === 'function' ? amount(this._repeat) : amount 550 | this._r = this._repeat 551 | this._isFinite = isFinite(amount) 552 | 553 | return this 554 | } 555 | 556 | /** 557 | * Set delay of each repeat alternate of tween 558 | * @param {number} amount Sets tween repeat alternate delay / repeat alternate wait duration 559 | * @example tween.reverseDelay(500) 560 | * @memberof TWEEN.Tween 561 | */ 562 | reverseDelay (amount) { 563 | this._reverseDelayTime = typeof amount === 'function' ? amount(this._reverseDelayTime) : amount 564 | 565 | return this 566 | } 567 | 568 | /** 569 | * Set `yoyo` state (enables reverse in repeat) 570 | * @param {boolean} state Enables alternate direction for repeat 571 | * @param {Function=} _easingReverse Easing function in reverse direction 572 | * @example tween.yoyo(true) 573 | * @memberof TWEEN.Tween 574 | */ 575 | yoyo (state, _easingReverse) { 576 | this._yoyo = typeof state === 'function' ? state(this._yoyo) : state === null ? this._yoyo : state 577 | if (!state) { 578 | this._reversed = false 579 | } 580 | this._easingReverse = _easingReverse || null 581 | 582 | return this 583 | } 584 | 585 | /** 586 | * Set easing 587 | * @param {Function} _easingFunction Easing function, applies in non-reverse direction if Tween#yoyo second argument is applied 588 | * @example tween.easing(Easing.Elastic.InOut) 589 | * @memberof TWEEN.Tween 590 | */ 591 | easing (_easingFunction) { 592 | this._easingFunction = _easingFunction 593 | 594 | return this 595 | } 596 | 597 | /** 598 | * Set interpolation 599 | * @param {Function} _interpolationFunction Interpolation function 600 | * @example tween.interpolation(Interpolation.Bezier) 601 | * @memberof TWEEN.Tween 602 | */ 603 | interpolation (_interpolationFunction) { 604 | if (typeof _interpolationFunction === 'function') { 605 | this._interpolationFunction = _interpolationFunction 606 | } 607 | 608 | return this 609 | } 610 | 611 | /** 612 | * Reassigns value for rare-case like Tween#restart or for Timeline 613 | * @private 614 | * @memberof TWEEN.Tween 615 | */ 616 | reassignValues (time) { 617 | const { _valuesStart, object, _delayTime } = this 618 | 619 | this._isPlaying = true 620 | this._startTime = time !== undefined ? time : now() 621 | this._startTime += _delayTime 622 | this._reversed = false 623 | add(this) 624 | 625 | for (const property in _valuesStart) { 626 | const start = _valuesStart[property] 627 | 628 | object[property] = start 629 | } 630 | 631 | return this 632 | } 633 | 634 | /** 635 | * Updates initial object to target value by given `time` 636 | * @param {Time} time Current time 637 | * @param {boolean=} preserve Prevents from removing tween from store 638 | * @param {boolean=} forceTime Forces to be frame rendered, even mismatching time 639 | * @example tween.update(100) 640 | * @memberof TWEEN.Tween 641 | */ 642 | update (time, preserve, forceTime) { 643 | let { 644 | _onStartCallbackFired, 645 | _easingFunction, 646 | _interpolationFunction, 647 | _easingReverse, 648 | _repeat, 649 | _delayTime, 650 | _reverseDelayTime, 651 | _yoyo, 652 | _reversed, 653 | _startTime, 654 | _prevTime, 655 | _duration, 656 | _valuesStart, 657 | _valuesEnd, 658 | object, 659 | _isFinite, 660 | _isPlaying, 661 | __render, 662 | _chainedTweensCount 663 | } = this 664 | 665 | let elapsed 666 | let currentEasing 667 | let property 668 | let propCount = 0 669 | 670 | if (!_duration) { 671 | elapsed = 1 672 | _repeat = 0 673 | } else { 674 | time = time !== undefined ? time : now() 675 | 676 | let delta = time - _prevTime 677 | this._prevTime = time 678 | if (delta > TOO_LONG_FRAME_MS && isRunning() && isLagSmoothing()) { 679 | time -= delta - FRAME_MS 680 | } 681 | 682 | if (!_isPlaying || (time < _startTime && !forceTime)) { 683 | return true 684 | } 685 | 686 | elapsed = (time - _startTime) / _duration 687 | elapsed = elapsed > 1 ? 1 : elapsed 688 | elapsed = _reversed ? 1 - elapsed : elapsed 689 | } 690 | 691 | if (!_onStartCallbackFired) { 692 | if (!this._rendered) { 693 | this.render() 694 | this._rendered = true 695 | } 696 | 697 | this.emit(EVENT_START, object) 698 | 699 | this._onStartCallbackFired = true 700 | } 701 | 702 | currentEasing = _reversed ? _easingReverse || _easingFunction : _easingFunction 703 | 704 | if (!object) { 705 | return true 706 | } 707 | 708 | for (property in _valuesEnd) { 709 | const start = _valuesStart[property] 710 | if ((start === undefined || start === null) && !(Plugins[property] && Plugins[property].update)) { 711 | continue 712 | } 713 | const end = _valuesEnd[property] 714 | const value = currentEasing[property] 715 | ? currentEasing[property](elapsed) 716 | : typeof currentEasing === 'function' 717 | ? currentEasing(elapsed) 718 | : defaultEasing(elapsed) 719 | const _interpolationFunctionCall = _interpolationFunction[property] 720 | ? _interpolationFunction[property] 721 | : typeof _interpolationFunction === 'function' 722 | ? _interpolationFunction 723 | : Interpolation.Linear 724 | 725 | if (typeof end === 'number') { 726 | object[property] = start + (end - start) * value 727 | } else if (Array.isArray(end) && !end.isString && !Array.isArray(start)) { 728 | object[property] = _interpolationFunctionCall(end, value, object[property]) 729 | } else if (end && end.update) { 730 | end.update(value) 731 | } else if (typeof end === 'function') { 732 | object[property] = end(value) 733 | } else if (typeof end === 'string' && typeof start === 'number') { 734 | object[property] = start + parseFloat(end[0] + end.substr(2)) * value 735 | } else { 736 | recompose(property, object, _valuesStart, _valuesEnd, value, elapsed) 737 | } 738 | if (Plugins[property] && Plugins[property].update) { 739 | Plugins[property].update.call(this, object[property], start, end, value, elapsed, property) 740 | } 741 | propCount++ 742 | } 743 | 744 | if (!propCount) { 745 | remove(this) 746 | return false 747 | } 748 | 749 | if (__render && Tween.Renderer && Tween.Renderer.update) { 750 | Tween.Renderer.update.call(this, object, elapsed) 751 | } 752 | 753 | this.emit(EVENT_UPDATE, object, elapsed, time) 754 | 755 | if (elapsed === 1 || (_reversed && elapsed === 0)) { 756 | if (_repeat > 0 && _duration > 0) { 757 | if (_isFinite) { 758 | this._repeat-- 759 | } 760 | 761 | if (_yoyo) { 762 | this._reversed = !_reversed 763 | } else { 764 | for (property in _valuesEnd) { 765 | let end = _valuesEnd[property] 766 | if (typeof end === 'string' && typeof _valuesStart[property] === 'number') { 767 | _valuesStart[property] += parseFloat(end[0] + end.substr(2)) 768 | } 769 | } 770 | } 771 | 772 | this.emit(_yoyo && !_reversed ? EVENT_REVERSE : EVENT_REPEAT, object) 773 | 774 | if (_reversed && _reverseDelayTime) { 775 | this._startTime = time - _reverseDelayTime 776 | } else { 777 | this._startTime = time + _delayTime 778 | } 779 | 780 | return true 781 | } else { 782 | if (!preserve) { 783 | this._isPlaying = false 784 | remove(this) 785 | _id-- 786 | } 787 | this.emit(EVENT_COMPLETE, object) 788 | this._repeat = this._r 789 | 790 | if (_chainedTweensCount) { 791 | for (let i = 0; i < _chainedTweensCount; i++) { 792 | this[CHAINED_TWEENS + i].start(time + _duration) 793 | } 794 | } 795 | 796 | return false 797 | } 798 | } 799 | 800 | return true 801 | } 802 | } 803 | 804 | export default Tween 805 | -------------------------------------------------------------------------------- /performance/kute.js: -------------------------------------------------------------------------------- 1 | /* KUTE.js - The Light Tweening Engine 2 | * by dnp_theme 3 | * Licensed under MIT-License 4 | */ 5 | (function (root, factory) { 6 | if (typeof define === 'function' && define.amd) { 7 | define([], factory) // AMD. Register as an anonymous module. 8 | } else if (typeof exports === 'object') { 9 | module.exports = factory() // Node, not strict CommonJS 10 | } else { 11 | root.KUTE = factory() 12 | } 13 | }(this, function () { 14 | 'use strict' 15 | 16 | // set a custom scope for KUTE.js 17 | var g = typeof global !== 'undefined' ? global : window, time = g.performance, 18 | body = document.body, tweens = [], tick = null, // tick must be null!! 19 | 20 | // strings 21 | length = 'length', 22 | split = 'split', 23 | indexOf = 'indexOf', 24 | replace = 'replace', 25 | 26 | offsetWidth = 'offsetWidth', 27 | offsetHeight = 'offsetHeight', 28 | 29 | options = 'options', 30 | valuesStart = 'valuesStart', 31 | valuesEnd = 'valuesEnd', 32 | valuesRepeat = 'valuesRepeat', 33 | 34 | element = 'element', 35 | playing = 'playing', 36 | 37 | duration = 'duration', 38 | delay = 'delay', 39 | offset = 'offset', 40 | repeat = 'repeat', 41 | repeatDelay = 'repeatDelay', 42 | yoyo = 'yoyo', 43 | easing = 'easing', 44 | chain = 'chain', 45 | keepHex = 'keepHex', 46 | 47 | style = 'style', 48 | dataTweening = 'data-tweening', 49 | getElementsByTagName = 'getElementsByTagName', 50 | addEventListener = 'addEventListener', 51 | removeEventListener = 'removeEventListener' 52 | 53 | // supported properties 54 | var colorProps = ['color', 'backgroundColor'], // 'hex', 'rgb', 'rgba' '#fff' 'rgb(0,0,0)' / 'rgba(0,0,0,0)' 'red' (IE9+) 55 | boxModelProps = ['top', 'left', 'width', 'height'], 56 | transformFunctions = ['translate3d', 'translateX', 'translateY', 'translateZ', 'rotate', 'translate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'scale'], 57 | scrollProp = ['scroll'], // has no default value, it's calculated on tween start 58 | opacityProp = ['opacity'], // opacity 59 | coreProps = colorProps.concat(opacityProp, boxModelProps, transformFunctions), 60 | defaultPropsValues = {} 61 | 62 | // populate default values object 63 | for (var propertyIndex = 0, allCorePropLength = coreProps[length], coreProp; propertyIndex < allCorePropLength; propertyIndex++) { 64 | coreProp = coreProps[propertyIndex] 65 | if (colorProps[indexOf](coreProp) !== -1) { 66 | defaultPropsValues[coreProp] = 'rgba(0,0,0,0)' // defaultPropsValues[coreProp] = {r:0,g:0,b:0,a:1}; 67 | } else if (boxModelProps[indexOf](coreProp) !== -1) { 68 | defaultPropsValues[coreProp] = 0 69 | } else if (coreProp === 'translate3d') { // px 70 | defaultPropsValues[coreProp] = [0, 0, 0] 71 | } else if (coreProp === 'translate') { // px 72 | defaultPropsValues[coreProp] = [0, 0] 73 | } else if (coreProp === 'rotate' || /X|Y|Z/.test(coreProp)) { // deg 74 | defaultPropsValues[coreProp] = 0 75 | } else if (coreProp === 'scale' || coreProp === 'opacity') { // unitless 76 | defaultPropsValues[coreProp] = 1 77 | } 78 | } 79 | 80 | // default tween options, since 1.6.1 81 | var defaultOptions = { 82 | duration: 700, 83 | delay: 0, 84 | offset: 0, 85 | repeat: 0, 86 | repeatDelay: 0, 87 | yoyo: false, 88 | easing: 'linear', 89 | keepHex: false 90 | }, 91 | // tools / utils 92 | getPrefix = function () { // returns browser prefix 93 | var prefixes = ['Moz', 'moz', 'Webkit', 'webkit', 'O', 'o', 'Ms', 'ms'], thePrefix 94 | for (var pIndex = 0, pfl = prefixes[length]; pIndex < pfl; pIndex++) { 95 | if (prefixes[pIndex] + 'Transform' in body[style]) { thePrefix = prefixes[pIndex]; break } 96 | } 97 | return thePrefix 98 | }, 99 | property = function (propertyToPrefix) { // returns prefixed property | property 100 | var prefixRequired = (!(propertyToPrefix in body[style])), prefix = getPrefix() // is prefix required for property | prefix 101 | return prefixRequired ? prefix + (propertyToPrefix.charAt(0).toUpperCase() + propertyToPrefix.slice(1)) : propertyToPrefix 102 | }, 103 | selector = function (el, multi) { // a public selector utility 104 | var requestedElem 105 | if (multi) { 106 | requestedElem = el instanceof Object || typeof el === 'object' ? el : document.querySelectorAll(el) 107 | } else { 108 | requestedElem = typeof el === 'object' ? el : document.querySelector(el) 109 | } 110 | if (requestedElem === null && el !== 'window') throw new TypeError('Element not found or incorrect selector: ' + el) 111 | return requestedElem 112 | }, 113 | radToDeg = function (a) { return a * 180 / Math.PI }, 114 | trueDimension = function (dimValue, isAngle) { // true dimension returns { v = value, u = unit } 115 | var intValue = parseInt(dimValue) || 0, mUnits = ['px', '%', 'deg', 'rad', 'em', 'rem', 'vh', 'vw'], theUnit 116 | for (var mIndex = 0; mIndex < mUnits[length]; mIndex++) { 117 | if (typeof dimValue === 'string' && dimValue[indexOf](mUnits[mIndex]) !== -1) { 118 | theUnit = mUnits[mIndex]; break 119 | } 120 | } 121 | theUnit = theUnit !== undefined ? theUnit : (isAngle ? 'deg' : 'px') 122 | return { v: intValue, u: theUnit } 123 | }, 124 | trueColor = function (colorString) { // replace transparent and transform any color to rgba()/rgb() 125 | if (/rgb|rgba/.test(colorString)) { // first check if it's a rgb string 126 | var vrgb = colorString[replace](/\s|\)/, '')[split]('(')[1][split](','), colorAlpha = vrgb[3] ? vrgb[3] : null 127 | if (!colorAlpha) { 128 | return { r: parseInt(vrgb[0]), g: parseInt(vrgb[1]), b: parseInt(vrgb[2]) } 129 | } else { 130 | return { r: parseInt(vrgb[0]), g: parseInt(vrgb[1]), b: parseInt(vrgb[2]), a: parseFloat(colorAlpha) } 131 | } 132 | } else if (/^#/.test(colorString)) { 133 | var fromHex = hexToRGB(colorString); return { r: fromHex.r, g: fromHex.g, b: fromHex.b } 134 | } else if (/transparent|none|initial|inherit/.test(colorString)) { 135 | return { r: 0, g: 0, b: 0, a: 0 } 136 | } else if (!/^#|^rgb/.test(colorString)) { // maybe we can check for web safe colors 137 | var siteHead = document[getElementsByTagName]('head')[0]; siteHead[style].color = colorString 138 | var webColor = g.getComputedStyle(siteHead, null).color; webColor = /rgb/.test(webColor) ? webColor[replace](/[^\d,]/g, '')[split](',') : [0, 0, 0] 139 | siteHead[style].color = ''; return { r: parseInt(webColor[0]), g: parseInt(webColor[1]), b: parseInt(webColor[2]) } 140 | } 141 | }, 142 | rgbToHex = function (r, g, b) { // transform rgb to hex or vice-versa | webkit browsers ignore HEX, always use RGB/RGBA 143 | return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) 144 | }, 145 | hexToRGB = function (hex) { 146 | var hexShorthand = /^#?([a-f\d])([a-f\d])([a-f\d])$/i // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 147 | hex = hex[replace](hexShorthand, function (m, r, g, b) { 148 | return r + r + g + g + b + b 149 | }) 150 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) 151 | return result ? { 152 | r: parseInt(result[1], 16), 153 | g: parseInt(result[2], 16), 154 | b: parseInt(result[3], 16) 155 | } : null 156 | }, 157 | getInlineStyle = function (el) { // get transform style for element from cssText for .to() method 158 | if (!el) return // if the scroll applies to `window` it returns as it has no styling 159 | var css = el[style].cssText[replace](/\s/g, '')[split](';'), transformObject = {} // the cssText | the resulting transform object 160 | 161 | // if we have any inline style in the cssText attribute, usually it has higher priority 162 | for (var i = 0, csl = css[length]; i < csl; i++) { 163 | if (/transform/i.test(css[i])) { 164 | var tps = css[i][split](':')[1][split](')') // all transform properties 165 | for (var k = 0, tpl = tps[length] - 1; k < tpl; k++) { 166 | var tpv = tps[k][split]('('), tp = tpv[0], tv = tpv[1] // each transform property, the sp is for transform property 167 | if (transformFunctions[indexOf](tp) !== -1) { 168 | transformObject[tp] = /translate3d/.test(tp) ? tv[split](',') : tv 169 | } 170 | } 171 | } 172 | } 173 | return transformObject 174 | }, 175 | getCurrentStyle = function (elem, propertyName) { // get computed style property for element for .to() method 176 | var styleAttribute = elem[style], computedStyle = g.getComputedStyle(elem, null) || elem.currentStyle, 177 | prefixedProp = property(propertyName), // the computed style | prefixed property 178 | styleValue = styleAttribute[propertyName] && !/auto|initial|none|unset/.test(styleAttribute[propertyName]) ? styleAttribute[propertyName] : computedStyle[prefixedProp] 179 | if (propertyName !== 'transform' && (prefixedProp in computedStyle || prefixedProp in styleAttribute)) { 180 | if (styleValue) { 181 | if (prefixedProp === 'filter') { // handle IE8 opacity 182 | var filterValue = parseInt(styleValue[split]('=')[1][replace](')', '')) 183 | return parseFloat(filterValue / 100) 184 | } else { 185 | return styleValue 186 | } 187 | } else { 188 | return defaultPropsValues[propertyName] 189 | } 190 | } 191 | }, 192 | 193 | // more internals 194 | getAll = function () { return tweens }, 195 | removeAll = function () { tweens = [] }, 196 | add = function (tw) { tweens.push(tw) }, 197 | remove = function (tw) { var i = tweens[indexOf](tw); if (i !== -1) { tweens.splice(i, 1) } }, 198 | stop = function () { if (tick) { _cancelAnimationFrame(tick); tick = null } }, 199 | 200 | canTouch = ('ontouchstart' in g || navigator && navigator.msMaxTouchPoints) || false, // support Touch? 201 | touchOrWheel = canTouch ? 'touchstart' : 'mousewheel', mouseEnter = 'mouseenter', // events to prevent on scroll 202 | _requestAnimationFrame = g.requestAnimationFrame || g.webkitRequestAnimationFrame || function (c) { return setTimeout(c, 16) }, 203 | _cancelAnimationFrame = g.cancelAnimationFrame || g.webkitCancelRequestAnimationFrame || function (c) { return clearTimeout(c) }, 204 | transformProperty = property('transform'), 205 | 206 | // true scroll container 207 | html = document[getElementsByTagName]('HTML')[0], 208 | scrollContainer = navigator && /webkit/i.test(navigator.userAgent) || document.compatMode == 'BackCompat' ? body : html, 209 | 210 | // browser detection 211 | isIE = navigator && (new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})').exec(navigator.userAgent) !== null) ? parseFloat(RegExp.$1) : false, 212 | isIE8 = isIE === 8, // check IE8/IE 213 | isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) // we optimize morph depending on device type 214 | 215 | // KUTE.js INTERPOLATORS 216 | var interpolate = g.Interpolate = {}, 217 | number = interpolate.number = function (a, b, v) { // number1, number2, progress 218 | a = +a; b -= a; return a + b * v 219 | }, 220 | unit = interpolate.unit = function (a, b, u, v) { // number1, number2, unit, progress 221 | a = +a; b -= a; return (a + b * v) + u 222 | }, 223 | color = interpolate.color = function (a, b, v, toHex) { // rgba1, rgba2, progress, convertToHex(true/false) 224 | var _c = {}, c, ep = ')', cm = ',', rgb = 'rgb(', rgba = 'rgba(' 225 | for (c in b) { _c[c] = c !== 'a' ? (number(a[c], b[c], v) >> 0 || 0) : (a[c] && b[c]) ? (number(a[c], b[c], v) * 100 >> 0) / 100 : null } 226 | return toHex ? rgbToHex(_c.r, _c.g, _c.b) : !_c.a ? rgb + _c.r + cm + _c.g + cm + _c.b + ep : rgba + _c.r + cm + _c.g + cm + _c.b + cm + _c.a + ep 227 | }, 228 | translate = interpolate.translate = isMobile ? function (a, b, u, v) { 229 | var translation = {} 230 | for (var ax in b) { 231 | translation[ax] = (a[ax] === b[ax] ? b[ax] : (a[ax] + (b[ax] - a[ax]) * v) >> 0) + u 232 | } 233 | return translation.x || translation.y ? 'translate(' + translation.x + ',' + translation.y + ')' 234 | : 'translate3d(' + translation.translateX + ',' + translation.translateY + ',' + translation.translateZ + ')' 235 | } : function (a, b, u, v) { 236 | var translation = {} 237 | for (var ax in b) { 238 | translation[ax] = (a[ax] === b[ax] ? b[ax] : ((a[ax] + (b[ax] - a[ax]) * v) * 100 >> 0) / 100) + u 239 | } 240 | return translation.x || translation.y ? 'translate(' + translation.x + ',' + translation.y + ')' 241 | : 'translate3d(' + translation.translateX + ',' + translation.translateY + ',' + translation.translateZ + ')' 242 | }, 243 | rotate = interpolate.rotate = function (a, b, u, v) { 244 | var rotation = {} 245 | for (var rx in b) { 246 | rotation[rx] = rx === 'z' ? ('rotate(' + (((a[rx] + (b[rx] - a[rx]) * v) * 100 >> 0) / 100) + u + ')') 247 | : (rx + '(' + (((a[rx] + (b[rx] - a[rx]) * v) * 100 >> 0) / 100) + u + ')') 248 | } 249 | return rotation.z ? rotation.z : (rotation.rotateX || '') + (rotation.rotateY || '') + (rotation.rotateZ || '') 250 | }, 251 | skew = interpolate.skew = function (a, b, u, v) { 252 | var skewProp = {} 253 | for (var sx in b) { 254 | skewProp[sx] = sx + '(' + (((a[sx] + (b[sx] - a[sx]) * v) * 10 >> 0) / 10) + u + ')' 255 | } 256 | return (skewProp.skewX || '') + (skewProp.skewY || '') 257 | }, 258 | scale = interpolate.scale = function (a, b, v) { 259 | return 'scale(' + (((a + (b - a) * v) * 1000 >> 0) / 1000) + ')' 260 | }, 261 | 262 | // KUTE.js DOM update functions 263 | DOM = {}, 264 | ticker = function (t) { 265 | var i = 0 266 | while (i < tweens[length]) { 267 | if (update.call(tweens[i], t)) { 268 | i++ 269 | } else { 270 | tweens.splice(i, 1) 271 | } 272 | } 273 | tick = _requestAnimationFrame(ticker) 274 | }, 275 | update = function (t) { 276 | t = t || time.now() 277 | if (t < this._startTime && this[playing]) { return true } 278 | 279 | var elapsed = Math.min((t - this._startTime) / this[options][duration], 1), progress = this[options][easing](elapsed) // calculate progress 280 | 281 | for (var tweenProp in this[valuesEnd]) { // render the DOM update 282 | DOM[tweenProp](this[element], tweenProp, this[valuesStart][tweenProp], this[valuesEnd][tweenProp], progress, this[options]) 283 | } 284 | 285 | if (this[options].update) { this[options].update.call() } // fire the updateCallback 286 | 287 | if (elapsed === 1) { 288 | if (this[options][repeat] > 0) { 289 | if (isFinite(this[options][repeat])) { this[options][repeat]-- } 290 | 291 | if (this[options][yoyo]) { // handle yoyo 292 | this.reversed = !this.reversed 293 | reverse.call(this) 294 | } 295 | 296 | this._startTime = (this[options][yoyo] && !this.reversed) ? t + this[options][repeatDelay] : t // set the right time for delay 297 | return true 298 | } else { 299 | if (this[options].complete) { this[options].complete.call() } 300 | 301 | scrollOut.call(this) // unbind preventing scroll when scroll tween finished 302 | 303 | for (var i = 0, ctl = this[options][chain][length]; i < ctl; i++) { // start animating chained tweens 304 | this[options][chain][i].start() 305 | } 306 | 307 | // stop ticking when finished 308 | close.call(this) 309 | } 310 | return false 311 | } 312 | return true 313 | }, 314 | 315 | // applies the transform origin and perspective 316 | perspective = function () { 317 | var el = this[element], ops = this[options] 318 | if (ops.perspective !== undefined && transformProperty in this[valuesEnd]) { // element perspective 319 | this[valuesStart][transformProperty]['perspective'] = this[valuesEnd][transformProperty]['perspective'] 320 | } 321 | // element transform origin / we filter it out for svgTransform to fix the Firefox transformOrigin bug https://bugzilla.mozilla.org/show_bug.cgi?id=923193 322 | if (ops.transformOrigin !== undefined && (!('svgTransform' in this[valuesEnd]))) { el[style][property('transformOrigin')] = ops.transformOrigin } // set transformOrigin for CSS3 transforms only 323 | if (ops.perspectiveOrigin !== undefined) { el[style][property('perspectiveOrigin')] = ops.perspectiveOrigin } // element perspective origin 324 | if (ops.parentPerspective !== undefined) { el.parentNode[style][property('perspective')] = ops.parentPerspective + 'px' } // parent perspective 325 | if (ops.parentPerspectiveOrigin !== undefined) { el.parentNode[style][property('perspectiveOrigin')] = ops.parentPerspectiveOrigin } // parent perspective origin 326 | }, 327 | 328 | // plugin connector objects 329 | prepareStart = {}, // check current property value when .to() method is used 330 | crossCheck = {}, // checks for differences between start and end value, try to make sure start unit and end unit are same as well as consistent, stack transforms, process SVG paths 331 | 332 | // parse properties object 333 | // string parsing and property specific value processing 334 | parseProperty = { // we already start working on core supported properties 335 | boxModel: function (tweenProp, inputValue) { 336 | if (!(tweenProp in DOM)) { 337 | DOM[tweenProp] = function (elem, tweenProp, a, b, v) { 338 | elem[style][tweenProp] = (v > 0.99 || v < 0.01 ? ((number(a, b, v) * 10) >> 0) / 10 : (number(a, b, v)) >> 0) + 'px' 339 | } 340 | } 341 | var boxValue = trueDimension(inputValue), offsetProp = tweenProp === 'height' ? offsetHeight : offsetWidth 342 | return boxValue.u === '%' ? boxValue.v * this[element][offsetProp] / 100 : boxValue.v 343 | }, 344 | transform: function (tweenProp, inputValue) { 345 | if (!(transformProperty in DOM)) { 346 | DOM[transformProperty] = function (elem, tweenProp, a, b, v, o) { 347 | elem[style][tweenProp] = (a.perspective || '') + 348 | ('translate' in a ? translate(a.translate, b.translate, 'px', v) : '') + 349 | ('rotate' in a ? rotate(a.rotate, b.rotate, 'deg', v) : '') + 350 | ('skew' in a ? skew(a.skew, b.skew, 'deg', v) : '') + 351 | ('scale' in a ? scale(a.scale, b.scale, v) : '') 352 | } 353 | } 354 | 355 | // process each transform property 356 | if (/translate/.test(tweenProp)) { 357 | if (tweenProp === 'translate3d') { 358 | var t3d = inputValue[split](','), t3d0 = trueDimension(t3d[0]), t3d1 = trueDimension(t3d[1], t3d2 = trueDimension(t3d[2])) 359 | return { 360 | translateX: t3d0.u === '%' ? (t3d0.v * this[element][offsetWidth] / 100) : t3d0.v, 361 | translateY: t3d1.u === '%' ? (t3d1.v * this[element][offsetHeight] / 100) : t3d1.v, 362 | translateZ: t3d2.u === '%' ? (t3d2.v * (this[element][offsetHeight] + this[element][offsetWidth]) / 200) : t3d2.v // to be changed with something like element and/or parent perspective 363 | } 364 | } else if (/^translate(?:[XYZ])$/.test(tweenProp)) { 365 | var t1d = trueDimension(inputValue), percentOffset = /X/.test(tweenProp) ? this[element][offsetWidth] / 100 : /Y/.test(tweenProp) ? this[element][offsetHeight] / 100 : (this[element][offsetWidth] + this[element][offsetHeight]) / 200 366 | 367 | return t1d.u === '%' ? (t1d.v * percentOffset) : t1d.v 368 | } else if (tweenProp === 'translate') { 369 | var tv = typeof inputValue === 'string' ? inputValue[split](',') : inputValue, t2d = {}, t2dv, 370 | t2d0 = trueDimension(tv[0]), t2d1 = tv[length] ? trueDimension(tv[1]) : {v: 0, u: 'px'} 371 | if (tv instanceof Array) { 372 | t2d.x = t2d0.u === '%' ? (t2d0.v * this[element][offsetWidth] / 100) : t2d0.v, 373 | t2d.y = t2d1.u === '%' ? (t2d1.v * this[element][offsetHeight] / 100) : t2d1.v 374 | } else { 375 | t2dv = trueDimension(tv) 376 | t2d.x = t2dv.u === '%' ? (t2dv.v * this[element][offsetWidth] / 100) : t2dv.v, 377 | t2d.y = 0 378 | } 379 | 380 | return t2d 381 | } 382 | } else if (/rotate|skew/.test(tweenProp)) { 383 | if (/^rotate(?:[XYZ])$|skew(?:[XY])$/.test(tweenProp)) { 384 | var r3d = trueDimension(inputValue, true) 385 | return r3d.u === 'rad' ? radToDeg(r3d.v) : r3d.v 386 | } else if (tweenProp === 'rotate') { 387 | var r2d = {}, r2dv = trueDimension(inputValue, true) 388 | r2d.z = r2dv.u === 'rad' ? radToDeg(r2dv.v) : r2dv.v 389 | return r2d 390 | } 391 | } else if (tweenProp === 'scale') { 392 | return parseFloat(inputValue) // this must be parseFloat(v) 393 | } 394 | }, 395 | unitless: function (tweenProp, inputValue) { // scroll | opacity 396 | if (/scroll/.test(tweenProp) && !(tweenProp in DOM)) { 397 | DOM[tweenProp] = function (elem, tweenProp, a, b, v) { 398 | elem.scrollTop = (number(a, b, v)) >> 0 399 | } 400 | } else if (tweenProp === 'opacity') { 401 | if (!(tweenProp in DOM)) { 402 | if (isIE8) { 403 | DOM[tweenProp] = function (elem, tweenProp, a, b, v) { 404 | var st = 'alpha(opacity=', ep = ')' 405 | elem[style].filter = st + ((number(a, b, v) * 100) >> 0) + ep 406 | } 407 | } else { 408 | DOM[tweenProp] = function (elem, tweenProp, a, b, v) { 409 | elem[style].opacity = ((number(a, b, v) * 100) >> 0) / 100 410 | } 411 | } 412 | } 413 | } 414 | return parseFloat(inputValue) 415 | }, 416 | colors: function (tweenProp, inputValue) { // colors 417 | if (!(tweenProp in DOM)) { 418 | DOM[tweenProp] = function (elem, tweenProp, a, b, v, o) { 419 | elem[style][tweenProp] = color(a, b, v, o[keepHex]) 420 | } 421 | } 422 | return trueColor(inputValue) 423 | } 424 | }, 425 | 426 | // process properties for endValues and startValues or one of them 427 | preparePropertiesObject = function (obj, fn) { // this, props object, type: start/end 428 | var propertiesObject = fn === 'start' ? this[valuesStart] : this[valuesEnd], 429 | skewObject = {}, rotateObject = {}, translateObject = {}, transformObject = {} 430 | 431 | for (var x in obj) { 432 | if (transformFunctions[indexOf](x) !== -1) { // transform object gets built here 433 | var prepAxis = ['X', 'Y', 'Z'] // coordinates // translate[x] = pp(x, obj[x]); 434 | if (/^translate(?:[XYZ]|3d)$/.test(x)) { // process translate3d 435 | for (var fnIndex = 0; fnIndex < 3; fnIndex++) { 436 | var translateAxis = prepAxis[fnIndex] 437 | if (/3d/.test(x)) { 438 | translateObject['translate' + translateAxis] = parseProperty.transform.call(this, 'translate' + translateAxis, obj[x][fnIndex]) 439 | } else { 440 | translateObject['translate' + translateAxis] = ('translate' + translateAxis in obj) ? parseProperty.transform.call(this, 'translate' + translateAxis, obj['translate' + translateAxis]) : 0 441 | } 442 | } 443 | transformObject['translate'] = translateObject 444 | } else if (/^rotate(?:[XYZ])$|^skew(?:[XY])$/.test(x)) { // process rotation/skew 445 | var objectName = /rotate/.test(x) ? 'rotate' : 'skew', 446 | rotationOrSkew = objectName === 'rotate' ? rotateObject : skewObject 447 | for (var rIndex = 0; rIndex < 3; rIndex++) { 448 | var oneAxis = prepAxis[rIndex] 449 | if (obj[objectName + oneAxis] !== undefined && x !== 'skewZ') { 450 | rotationOrSkew[objectName + oneAxis] = parseProperty.transform.call(this, objectName + oneAxis, obj[objectName + oneAxis]) 451 | } 452 | } 453 | transformObject[objectName] = rotationOrSkew 454 | } else if (/(rotate|translate|scale)$/.test(x)) { // process 2d translation / rotation 455 | transformObject[x] = parseProperty.transform.call(this, x, obj[x]) 456 | } 457 | propertiesObject[transformProperty] = transformObject 458 | } else { 459 | if (boxModelProps[indexOf](x) !== -1) { 460 | propertiesObject[x] = parseProperty.boxModel.call(this, x, obj[x]) 461 | } else if (opacityProp[indexOf](x) !== -1 || x === 'scroll') { 462 | propertiesObject[x] = parseProperty.unitless.call(this, x, obj[x]) 463 | } else if (colorProps[indexOf](x) !== -1) { 464 | propertiesObject[x] = parseProperty.colors.call(this, x, obj[x]) 465 | } else if (x in parseProperty) { // or any other property from css/ attr / svg / third party plugins 466 | propertiesObject[x] = parseProperty[x].call(this, x, obj[x]) 467 | } 468 | } 469 | } 470 | }, 471 | reverse = function () { 472 | if (this[options][yoyo]) { 473 | for (var reverseProp in this[valuesEnd]) { 474 | var tmp = this[valuesRepeat][reverseProp] 475 | this[valuesRepeat][reverseProp] = this[valuesEnd][reverseProp] 476 | this[valuesEnd][reverseProp] = tmp 477 | this[valuesStart][reverseProp] = this[valuesRepeat][reverseProp] 478 | } 479 | } 480 | }, 481 | close = function () { // when animation is finished reset repeat, yoyo&reversed tweens 482 | if (this[repeat] > 0) { this[options][repeat] = this[repeat] } 483 | if (this[options][yoyo] && this.reversed === true) { reverse.call(this); this.reversed = false } 484 | this[playing] = false 485 | 486 | setTimeout(function () { if (!tweens[length]) { stop() } }, 48) // when all animations are finished, stop ticking after ~3 frames 487 | }, 488 | preventScroll = function (eventObj) { // prevent mousewheel or touch events while tweening scroll 489 | var data = body.getAttribute(dataTweening) 490 | if (data && data === 'scroll') { eventObj.preventDefault() } 491 | }, 492 | scrollOut = function () { // prevent scroll when tweening scroll 493 | if ('scroll' in this[valuesEnd] && body.getAttribute(dataTweening)) { 494 | document[removeEventListener](touchOrWheel, preventScroll, false) 495 | document[removeEventListener](mouseEnter, preventScroll, false) 496 | body.removeAttribute(dataTweening) 497 | } 498 | }, 499 | scrollIn = function () { 500 | if ('scroll' in this[valuesEnd] && !body.getAttribute(dataTweening)) { 501 | document[addEventListener](touchOrWheel, preventScroll, false) 502 | document[addEventListener](mouseEnter, preventScroll, false) 503 | body.setAttribute(dataTweening, 'scroll') 504 | } 505 | }, 506 | processEasing = function (fn) { 507 | if (typeof fn === 'function') { 508 | return fn 509 | } else if (typeof fn === 'string') { 510 | return easingFn[fn] // regular Robert Penner Easing Functions 511 | } 512 | }, 513 | getStartValues = function () { // stack transform props for .to() chains 514 | var startValues = {}, currentStyle = getInlineStyle(this[element]), 515 | degreeProps = ['rotate', 'skew'], startAxis = ['X', 'Y', 'Z'] 516 | 517 | for (var tweenProperty in this[valuesStart]) { 518 | if (transformFunctions[indexOf](tweenProperty) !== -1) { 519 | var r2d = (/(rotate|translate|scale)$/.test(tweenProperty)) 520 | if (/translate/.test(tweenProperty) && tweenProperty !== 'translate') { 521 | startValues['translate3d'] = currentStyle['translate3d'] || defaultPropsValues[tweenProperty] 522 | } else if (r2d) { // 2d transforms 523 | startValues[tweenProperty] = currentStyle[tweenProperty] || defaultPropsValues[tweenProperty] 524 | } else if (!r2d && /rotate|skew/.test(tweenProperty)) { // all angles 525 | for (var degIndex = 0; degIndex < 2; degIndex++) { 526 | for (var axisIndex = 0; axisIndex < 3; axisIndex++) { 527 | var s = degreeProps[degIndex] + startAxis[axisIndex] 528 | if (transformFunctions[indexOf](s) !== -1 && (s in this[valuesStart])) { startValues[s] = currentStyle[s] || defaultPropsValues[s] } 529 | } 530 | } 531 | } 532 | } else { 533 | if (tweenProperty !== 'scroll') { 534 | if (tweenProperty === 'opacity' && isIE8) { // handle IE8 opacity 535 | var currentOpacity = getCurrentStyle(this[element], 'filter') 536 | startValues['opacity'] = typeof currentOpacity === 'number' ? currentOpacity : defaultPropsValues['opacity'] 537 | } else { 538 | if (coreProps[indexOf](tweenProperty) !== -1) { 539 | startValues[tweenProperty] = getCurrentStyle(this[element], tweenProperty) || d[tweenProperty] 540 | } else { // plugins register here 541 | startValues[tweenProperty] = tweenProperty in prepareStart ? prepareStart[tweenProperty].call(this, tweenProperty, this[valuesStart][tweenProperty]) : 0 542 | } 543 | } 544 | } else { 545 | startValues[tweenProperty] = this[element] === scrollContainer ? (g.pageYOffset || scrollContainer.scrollTop) : this[element].scrollTop 546 | } 547 | } 548 | } 549 | for (var currentProperty in currentStyle) { // also add to startValues values from previous tweens 550 | if (transformFunctions[indexOf](currentProperty) !== -1 && (!(currentProperty in this[valuesStart]))) { 551 | startValues[currentProperty] = currentStyle[currentProperty] || defaultPropsValues[currentProperty] 552 | } 553 | } 554 | 555 | this[valuesStart] = {} 556 | preparePropertiesObject.call(this, startValues, 'start') 557 | 558 | if (transformProperty in this[valuesEnd]) { // let's stack transform 559 | for (var sp in this[valuesStart][transformProperty]) { // sp is the object corresponding to the transform function objects translate / rotate / skew / scale 560 | if (sp !== 'perspective') { 561 | if (typeof this[valuesStart][transformProperty][sp] === 'object') { 562 | for (var spp in this[valuesStart][transformProperty][sp]) { // 3rd level 563 | if (typeof this[valuesEnd][transformProperty][sp] === 'undefined') { this[valuesEnd][transformProperty][sp] = {} } 564 | if (typeof this[valuesStart][transformProperty][sp][spp] === 'number' && typeof this[valuesEnd][transformProperty][sp][spp] === 'undefined') { 565 | this[valuesEnd][transformProperty][sp][spp] = this[valuesStart][transformProperty][sp][spp] 566 | } 567 | } 568 | } else if (typeof this[valuesStart][transformProperty][sp] === 'number') { 569 | if (typeof this[valuesEnd][transformProperty][sp] === 'undefined') { // scale 570 | this[valuesEnd][transformProperty][sp] = this[valuesStart][transformProperty][sp] 571 | } 572 | } 573 | } 574 | } 575 | } 576 | } 577 | 578 | // core easing functions 579 | var easingFn = g.Easing = {} 580 | easingFn.linear = function (t) { return t } 581 | easingFn.easingSinusoidalIn = function (t) { return -Math.cos(t * Math.PI / 2) + 1 } 582 | easingFn.easingSinusoidalOut = function (t) { return Math.sin(t * Math.PI / 2) } 583 | easingFn.easingSinusoidalInOut = function (t) { return -0.5 * (Math.cos(Math.PI * t) - 1) } 584 | easingFn.easingQuadraticIn = function (t) { return t * t } 585 | easingFn.easingQuadraticOut = function (t) { return t * (2 - t) } 586 | easingFn.easingQuadraticInOut = function (t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t } 587 | easingFn.easingCubicIn = function (t) { return t * t * t } 588 | easingFn.easingCubicOut = function (t) { return (--t) * t * t + 1 } 589 | easingFn.easingCubicInOut = function (t) { return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1 } 590 | easingFn.easingQuarticIn = function (t) { return t * t * t * t } 591 | easingFn.easingQuarticOut = function (t) { return 1 - (--t) * t * t * t } 592 | easingFn.easingQuarticInOut = function (t) { return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t } 593 | easingFn.easingQuinticIn = function (t) { return t * t * t * t * t } 594 | easingFn.easingQuinticOut = function (t) { return 1 + (--t) * t * t * t * t } 595 | easingFn.easingQuinticInOut = function (t) { return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t } 596 | easingFn.easingCircularIn = function (t) { return -(Math.sqrt(1 - (t * t)) - 1) } 597 | easingFn.easingCircularOut = function (t) { return Math.sqrt(1 - (t = t - 1) * t) } 598 | easingFn.easingCircularInOut = function (t) { return ((t *= 2) < 1) ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1) } 599 | easingFn.easingExponentialIn = function (t) { return Math.pow(2, 10 * (t - 1)) - 0.001 } 600 | easingFn.easingExponentialOut = function (t) { return 1 - Math.pow(2, -10 * t) } 601 | easingFn.easingExponentialInOut = function (t) { return (t *= 2) < 1 ? 0.5 * Math.pow(2, 10 * (t - 1)) : 0.5 * (2 - Math.pow(2, -10 * (t - 1))) } 602 | easingFn.easingBackIn = function (t) { var s = 1.70158; return t * t * ((s + 1) * t - s) } 603 | easingFn.easingBackOut = function (t) { var s = 1.70158; return --t * t * ((s + 1) * t + s) + 1 } 604 | easingFn.easingBackInOut = function (t) { var s = 1.70158 * 1.525; if ((t *= 2) < 1) return 0.5 * (t * t * ((s + 1) * t - s)); return 0.5 * ((t -= 2) * t * ((s + 1) * t + s) + 2) } 605 | easingFn.easingElasticIn = function (t) { 606 | var s, _kea = 0.1, _kep = 0.4 607 | if (t === 0) return 0; if (t === 1) return 1 608 | if (!_kea || _kea < 1) { _kea = 1; s = _kep / 4 } else s = _kep * Math.asin(1 / _kea) / Math.PI * 2 609 | return -(_kea * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * Math.PI * 2 / _kep)) 610 | } 611 | easingFn.easingElasticOut = function (t) { 612 | var s, _kea = 0.1, _kep = 0.4 613 | if (t === 0) return 0; if (t === 1) return 1 614 | if (!_kea || _kea < 1) { _kea = 1; s = _kep / 4 } else s = _kep * Math.asin(1 / _kea) / Math.PI * 2 615 | return (_kea * Math.pow(2, -10 * t) * Math.sin((t - s) * Math.PI * 2 / _kep) + 1) 616 | } 617 | easingFn.easingElasticInOut = function (t) { 618 | var s, _kea = 0.1, _kep = 0.4 619 | if (t === 0) return 0; if (t === 1) return 1 620 | if (!_kea || _kea < 1) { _kea = 1; s = _kep / 4 } else s = _kep * Math.asin(1 / _kea) / Math.PI * 2 621 | if ((t *= 2) < 1) return -0.5 * (_kea * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * Math.PI * 2 / _kep)) 622 | return _kea * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * Math.PI * 2 / _kep) * 0.5 + 1 623 | } 624 | easingFn.easingBounceIn = function (t) { return 1 - easingFn.easingBounceOut(1 - t) } 625 | easingFn.easingBounceOut = function (t) { 626 | if (t < (1 / 2.75)) { return 7.5625 * t * t } else if (t < (2 / 2.75)) { return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75 } else if (t < (2.5 / 2.75)) { return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375 } else { return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375 } 627 | } 628 | easingFn.easingBounceInOut = function (t) { if (t < 0.5) return easingFn.easingBounceIn(t * 2) * 0.5; return easingFn.easingBounceOut(t * 2 - 1) * 0.5 + 0.5 } 629 | 630 | // single Tween object construct 631 | var Tween = function (targetElement, startObject, endObject, optionsObj) { 632 | this[element] = 'scroll' in endObject && (targetElement === undefined || targetElement === null) ? scrollContainer : targetElement // element animation is applied to 633 | 634 | this[playing] = false 635 | this.reversed = false 636 | this.paused = false 637 | 638 | this._startTime = null 639 | this._pauseTime = null 640 | 641 | this._startFired = false 642 | this[options] = {}; for (var o in optionsObj) { this[options][o] = optionsObj[o] } 643 | this[options].rpr = optionsObj.rpr || false // internal option to process inline/computed style at start instead of init true/false 644 | 645 | this[valuesRepeat] = {} // internal valuesRepeat 646 | this[valuesEnd] = {} // valuesEnd 647 | this[valuesStart] = {} // valuesStart 648 | 649 | preparePropertiesObject.call(this, endObject, 'end') // valuesEnd 650 | if (this[options].rpr) { this[valuesStart] = startObject } else { preparePropertiesObject.call(this, startObject, 'start') } // valuesStart 651 | 652 | if (this[options].perspective !== undefined && transformProperty in this[valuesEnd]) { // element transform perspective 653 | var perspectiveString = 'perspective(' + parseInt(this[options].perspective) + 'px)' 654 | this[valuesEnd][transformProperty].perspective = perspectiveString 655 | } 656 | 657 | for (var repeatProp in this[valuesEnd]) { 658 | if (repeatProp in crossCheck && !this[options].rpr) crossCheck[repeatProp].call(this) // this is where we do the valuesStart and valuesEnd check for fromTo() method 659 | } 660 | 661 | this[options][chain] = [] // chained Tweens 662 | this[options][easing] = processEasing(optionsObj[easing]) || easingFn[defaultOptions[easing]] || easingFn['linear'] // you can only set a core easing function as default 663 | this[options][repeat] = optionsObj[repeat] || defaultOptions[repeat] 664 | this[options][repeatDelay] = optionsObj[repeatDelay] || defaultOptions[repeatDelay] 665 | this[options][yoyo] = optionsObj[yoyo] || defaultOptions[yoyo] 666 | this[options][duration] = optionsObj[duration] || defaultOptions[duration] // duration option | default 667 | this[options][delay] = optionsObj[delay] || defaultOptions[delay] // delay option | default 668 | 669 | this[repeat] = this[options][repeat] // we cache the number of repeats to be able to put it back after all cycles finish 670 | }, 671 | // tween control and chain 672 | TweenProto = Tween.prototype = { 673 | // queue tween object to main frame update 674 | start: function (t) { // move functions that use the ticker outside the prototype to be in the same scope with it 675 | scrollIn.call(this) 676 | 677 | if (this[options].rpr) { getStartValues.apply(this) } // on start we reprocess the valuesStart for TO() method 678 | perspective.apply(this) // apply the perspective and transform origin 679 | 680 | for (var endProp in this[valuesEnd]) { 681 | if (endProp in crossCheck && this[options].rpr) crossCheck[endProp].call(this) // this is where we do the valuesStart and valuesEnd check for to() method 682 | this[valuesRepeat][endProp] = this[valuesStart][endProp] 683 | } 684 | 685 | // now it's a good time to start 686 | tweens.push(this) 687 | this[playing] = true 688 | this.paused = false 689 | this._startFired = false 690 | this._startTime = t || time.now() 691 | this._startTime += this[options][delay] 692 | 693 | if (!this._startFired) { 694 | if (this[options].start) { this[options].start.call() } 695 | this._startFired = true 696 | } 697 | !tick && ticker() 698 | return this 699 | }, 700 | play: function () { 701 | if (this.paused && this[playing]) { 702 | this.paused = false 703 | if (this[options].resume) { this[options].resume.call() } 704 | this._startTime += time.now() - this._pauseTime 705 | add(this) 706 | !tick && ticker() // restart ticking if stopped 707 | } 708 | return this 709 | }, 710 | resume: function () { return this.play() }, 711 | pause: function () { 712 | if (!this.paused && this[playing]) { 713 | remove(this) 714 | this.paused = true 715 | this._pauseTime = time.now() 716 | if (this[options].pause) { this[options].pause.call() } 717 | } 718 | return this 719 | }, 720 | stop: function () { 721 | if (!this.paused && this[playing]) { 722 | remove(this) 723 | this[playing] = false 724 | this.paused = false 725 | scrollOut.call(this) 726 | 727 | if (this[options].stop) { this[options].stop.call() } 728 | this.stopChainedTweens() 729 | close.call(this) 730 | } 731 | return this 732 | }, 733 | chain: function () { this[options][chain] = arguments; return this }, 734 | stopChainedTweens: function () { 735 | for (var i = 0, ctl = this[options][chain][length]; i < ctl; i++) { 736 | this[options][chain][i].stop() 737 | } 738 | } 739 | }, 740 | 741 | // the multi elements Tween constructs 742 | TweensTO = function (els, vE, o) { // .to 743 | this.tweens = []; var optionsObj = [] 744 | for (var i = 0, tl = els[length]; i < tl; i++) { 745 | optionsObj[i] = o || {}; o[delay] = o[delay] || defaultOptions[delay] 746 | optionsObj[i][delay] = i > 0 ? o[delay] + (o[offset] || defaultOptions[offset]) : o[delay] 747 | this.tweens.push(to(els[i], vE, optionsObj[i])) 748 | } 749 | }, 750 | TweensFT = function (els, vS, vE, o) { // .fromTo 751 | this.tweens = []; var optionsObj = [] 752 | for (var i = 0, l = els[length]; i < l; i++) { 753 | optionsObj[i] = o || {}; o[delay] = o[delay] || defaultOptions[delay] 754 | optionsObj[i][delay] = i > 0 ? o[delay] + (o[offset] || defaultOptions[offset]) : o[delay] 755 | this.tweens.push(fromTo(els[i], vS, vE, optionsObj[i])) 756 | } 757 | }, 758 | ws = TweensTO.prototype = TweensFT.prototype = { 759 | start: function (t) { 760 | t = t || time.now() 761 | for (var i = 0, tl = this.tweens[length]; i < tl; i++) { 762 | this.tweens[i].start(t) 763 | } 764 | return this 765 | }, 766 | stop: function () { for (var i = 0, tl = this.tweens[length]; i < tl; i++) { this.tweens[i].stop() } return this }, 767 | pause: function () { for (var i = 0, tl = this.tweens[length]; i < tl; i++) { this.tweens[i].pause() } return this }, 768 | chain: function () { this.tweens[this.tweens[length] - 1][options][chain] = arguments; return this }, 769 | play: function () { for (var i = 0, tl = this.tweens[length]; i < tl; i++) { this.tweens[i].play() } return this }, 770 | resume: function () { return this.play() } 771 | }, 772 | 773 | // main methods 774 | to = function (element, endObject, optionsObj) { 775 | optionsObj = optionsObj || {}; optionsObj.rpr = true 776 | return new Tween(selector(element), endObject, endObject, optionsObj) 777 | }, 778 | fromTo = function (element, startObject, endObject, optionsObj) { 779 | optionsObj = optionsObj || {} 780 | return new Tween(selector(element), startObject, endObject, optionsObj) 781 | }, 782 | 783 | // multiple elements tweening 784 | allTo = function (elements, endObject, optionsObj) { 785 | return new TweensTO(selector(elements, true), endObject, optionsObj) 786 | }, 787 | allFromTo = function (elements, startObject, endObject, optionsObj) { 788 | return new TweensFT(selector(elements, true), startObject, endObject, optionsObj) 789 | } 790 | 791 | return { // export core methods to public for plugins 792 | property: property, 793 | getPrefix: getPrefix, 794 | selector: selector, 795 | processEasing: processEasing, // utils 796 | defaultOptions: defaultOptions, // default tween options since 1.6.1 797 | to: to, 798 | fromTo: fromTo, 799 | allTo: allTo, 800 | allFromTo: allFromTo, // main methods 801 | ticker: ticker, 802 | tick: tick, 803 | tweens: tweens, 804 | update: update, 805 | dom: DOM, // update 806 | parseProperty: parseProperty, 807 | prepareStart: prepareStart, 808 | crossCheck: crossCheck, 809 | Tween: Tween, // property parsing & preparation | Tween | crossCheck 810 | truD: trueDimension, 811 | truC: trueColor, 812 | rth: rgbToHex, 813 | htr: hexToRGB, 814 | getCurrentStyle: getCurrentStyle // property parsing 815 | } 816 | })) 817 | --------------------------------------------------------------------------------