├── .gitignore ├── examples ├── font │ ├── icon.eot │ ├── icon.ttf │ ├── icon.woff │ └── icon.svg ├── img │ ├── space-ball.svg │ ├── defender.svg │ ├── rocket-01.svg │ ├── rocket-02.svg │ ├── space-planet.svg │ ├── alien.svg │ └── rocket-03.svg ├── scss │ ├── speed-control.scss │ ├── _example.scss │ ├── css-transitions-control.scss │ ├── _icon.scss │ ├── space-invaders.scss │ ├── _main.scss │ └── _normalize.scss ├── space-invaders.html ├── css-transitions-control.html ├── speed-control.html └── css │ ├── speed-control.css │ ├── css-transitions-control.css │ └── space-invaders.css ├── test └── vendor.js ├── .npmignore ├── src ├── label.js ├── callback.js ├── tweene-dummy.js ├── ticker.js ├── tweene-jquery.js ├── tweene-velocity.js ├── tweene-gsap.js ├── tween-pro.js ├── timeline-common.js └── tweene-transit.js ├── bower.json ├── package.json ├── karma.conf.js ├── README.md ├── gulpfile.js └── LICENSE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /nbproject/ 3 | /_dev/ 4 | /vendor/ 5 | /node_modules/ 6 | /coverage/ -------------------------------------------------------------------------------- /examples/font/icon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkidX/tweene/HEAD/examples/font/icon.eot -------------------------------------------------------------------------------- /examples/font/icon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkidX/tweene/HEAD/examples/font/icon.ttf -------------------------------------------------------------------------------- /examples/font/icon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkidX/tweene/HEAD/examples/font/icon.woff -------------------------------------------------------------------------------- /test/vendor.js: -------------------------------------------------------------------------------- 1 | 2 | window.$ = window.jQuery = require('jquery'); 3 | require('velocity-animate'); 4 | require('jquery.transit'); 5 | require('gsap'); 6 | 7 | 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Spotlight-V100 3 | .Trashes 4 | Thumbs.db 5 | ehthumbs.db 6 | Desktop.ini 7 | $RECYCLE.BIN/ 8 | test/ 9 | coverage/ 10 | _dev/ 11 | examples/ 12 | vendor/ 13 | nbproject/ 14 | karma.conf.js 15 | gulpfile.js 16 | bower.json 17 | .gitignore -------------------------------------------------------------------------------- /src/label.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | /** 15 | * Create a Label object, used internally by timelines when you add a label 16 | * @class 17 | * 18 | * @param {string} name 19 | */ 20 | var Label = function(name) 21 | { 22 | this.type = 'label'; 23 | this._id = name; 24 | this._name = name; 25 | this._position = null; 26 | 27 | 28 | /** 29 | * Return the unique identifier 30 | * 31 | * @returns {number} 32 | */ 33 | this.id = function() 34 | { 35 | return this._id; 36 | }; 37 | 38 | 39 | /** 40 | * Get/Set the time position inside the parent timeline 41 | * 42 | * @param {number} [value] 43 | * @returns {this} 44 | */ 45 | this.position = function(value) 46 | { 47 | if(value === void 0) 48 | { 49 | return this._position; 50 | } 51 | this._position = value; 52 | return this; 53 | }; 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /examples/img/space-ball.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/img/defender.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/scss/speed-control.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import "_example.scss"; 4 | 5 | 6 | .rocket 7 | { 8 | width: 30px; 9 | height: 50px; 10 | left: 10px; 11 | top: 175px; 12 | background-image: url('../img/rocket-03.svg'); 13 | } 14 | 15 | 16 | 17 | .planet 18 | { 19 | width: 160px; 20 | height: 90px; 21 | left: 160px; 22 | top: 200px; 23 | margin-left: -80px; 24 | margin-top: -45px; 25 | background-image: url('../img/space-planet.svg'); 26 | } 27 | 28 | 29 | .controls 30 | { 31 | position: absolute; 32 | right: 20px; 33 | top: 20px; 34 | width: 300px; 35 | 36 | 37 | .intro 38 | { 39 | font-family: $headingFontFamily; 40 | font-weight: 300; 41 | font-size: 20px; 42 | text-transform: uppercase; 43 | } 44 | 45 | ul 46 | { 47 | list-style: circle; 48 | } 49 | 50 | li 51 | { 52 | cursor: pointer; 53 | } 54 | 55 | li:hover 56 | { 57 | color: #FFFFFF; 58 | } 59 | 60 | .current 61 | { 62 | color: #DDDDDD; 63 | font-weight: 400; 64 | } 65 | } 66 | 67 | 68 | @media screen and (max-width: 767px) 69 | { 70 | .all 71 | { 72 | padding-left: 0; 73 | padding-right: 0; 74 | } 75 | 76 | .controls 77 | { 78 | right: auto; 79 | top: 500px; 80 | left: 10px; 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tweene", 3 | "version": "0.5.11", 4 | "description": "JavaScript Animation Proxy. It can work with GSAP, Velocity.js, Transit or jQuery.", 5 | "keywords": [ 6 | "tweene", 7 | "animation", 8 | "animate", 9 | "proxy", 10 | "tween", 11 | "tweening", 12 | "timeline", 13 | "control", 14 | "GSAP", 15 | "velocity", 16 | "transit", 17 | "jQuery", 18 | "CSS", 19 | "transition", 20 | "easing", 21 | "bezier", 22 | "transform" 23 | ], 24 | 25 | "main": "tweene.js", 26 | 27 | "homepage": "http://tweene.com", 28 | "authors": [ 29 | {"name": "Federico Orru'", "email": "federico@buzzler.com", "homepage": "http://buzzler.com"} 30 | ], 31 | 32 | "moduleType": [ 33 | "amd", 34 | "node" 35 | ], 36 | 37 | "dependencies": { 38 | "gsap": "^1.13.2", 39 | "jquery": "^2.1.1", 40 | "jquery.transit": "^0.9.12", 41 | "velocity": "^1.1.0" 42 | }, 43 | 44 | "license": "Artistic License 2.0", 45 | 46 | "repository": { 47 | "type": "git", 48 | "url": "https://github.com/SkidX/tweene.git" 49 | }, 50 | 51 | "ignore": [ 52 | "**/.*", 53 | "node_modules", 54 | "bower_components", 55 | "vendor", 56 | "examples", 57 | "test", 58 | "coverage", 59 | "_dev", 60 | "gulpfile.js", 61 | "Thumbs.db", 62 | "ehthumbs.db", 63 | "Desktop.ini", 64 | "$RECYCLE.BIN" 65 | 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /examples/scss/_example.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | $baseFontFamily: 'Roboto', arial, sans-serif; 4 | $headingFontFamily: 'Roboto Condensed', arial, sans-serif; 5 | 6 | $baseColor: #454545; 7 | $baseBgColor: #62ABBE; 8 | 9 | $baseLinkColor: #DDDDDD; 10 | $baseHoverColor: #FFFFFF; 11 | 12 | 13 | @import "_normalize.scss"; 14 | @import "_main.scss"; 15 | 16 | 17 | 18 | /* apply a natural box layout model to all elements */ 19 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } 20 | 21 | *:after, *:before { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } 22 | 23 | 24 | html, body, .all 25 | { 26 | height: 100%; 27 | margin: 0; 28 | padding: 0; 29 | } 30 | 31 | body 32 | { 33 | font-family: $baseFontFamily; 34 | font-weight: 300; 35 | font-size: 16px; 36 | color: $baseColor; 37 | background-color: $baseBgColor; 38 | line-height: 1.9; 39 | } 40 | 41 | p 42 | { 43 | margin: 0 0 0.7em 0; 44 | padding: 0; 45 | text-indent: 0; 46 | } 47 | 48 | a 49 | { 50 | color: $baseLinkColor; 51 | text-decoration: none; 52 | outline: none !important; 53 | } 54 | 55 | a:hover 56 | { 57 | color: $baseHoverColor; 58 | text-decoration: none; 59 | outline: none !important; 60 | } 61 | 62 | 63 | .all 64 | { 65 | position: relative; 66 | width: 100%; 67 | max-width: 960px; 68 | height: 100%; 69 | margin: auto; 70 | padding: 20px; 71 | } 72 | 73 | 74 | .object 75 | { 76 | position: absolute; 77 | backface-visibility: hidden; 78 | transform-style: preserve-3d; 79 | transform: translate3d(0, 0, 1px); 80 | background-size: 100% 100%; 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /examples/img/rocket-01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/scss/css-transitions-control.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import "_example.scss"; 4 | @import "_icon.scss"; 5 | 6 | 7 | .controls 8 | { 9 | position:relative; 10 | width: 320px; 11 | text-align: center; 12 | 13 | ul, li 14 | { 15 | list-style: none; 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | li 21 | { 22 | position: relative; 23 | display: inline-block; 24 | font-size: 36px; 25 | margin-right: 10px; 26 | } 27 | 28 | li:last-child 29 | { 30 | margin-right: 0; 31 | } 32 | 33 | span 34 | { 35 | display: inline-block; 36 | cursor: pointer; 37 | } 38 | 39 | span:hover 40 | { 41 | color: #FFFFFF; 42 | } 43 | 44 | .reverse 45 | { 46 | transform: rotate(180deg); 47 | } 48 | } 49 | 50 | 51 | .rocket 52 | { 53 | width: 32px; 54 | height: 41px; 55 | top: 380px; 56 | } 57 | 58 | .rocket-01 59 | { 60 | background-image: url('../img/rocket-01.svg'); 61 | left: 70px; 62 | } 63 | 64 | .rocket-02 65 | { 66 | background-image: url('../img/rocket-02.svg'); 67 | left: 230px; 68 | } 69 | 70 | .label 71 | { 72 | position: absolute; 73 | top: 440px; 74 | } 75 | 76 | .label-01 77 | { 78 | left: 65px; 79 | } 80 | 81 | .label-02 82 | { 83 | left: 160px; 84 | } 85 | 86 | 87 | .description 88 | { 89 | position: absolute; 90 | right: 20px; 91 | top: 40px; 92 | width: 300px; 93 | } 94 | 95 | 96 | 97 | @media screen and (max-width: 767px) 98 | { 99 | .all 100 | { 101 | padding-left: 0; 102 | padding-right: 0; 103 | } 104 | 105 | .description 106 | { 107 | right: auto; 108 | top: 500px; 109 | left: 10px; 110 | } 111 | } -------------------------------------------------------------------------------- /examples/img/rocket-02.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 37 | 41 | 42 | 45 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tweene", 3 | "title": "Tweene", 4 | "version": "0.5.11", 5 | "description": "JavaScript Animation Proxy. It can work with GSAP, Velocity.js, Transit or jQuery.", 6 | "keywords": [ 7 | "tweene", 8 | "animation", 9 | "animate", 10 | "proxy", 11 | "tween", 12 | "tweening", 13 | "timeline", 14 | "control", 15 | "GSAP", 16 | "velocity", 17 | "transit", 18 | "jQuery", 19 | "CSS", 20 | "transition", 21 | "easing", 22 | "bezier", 23 | "transform" 24 | ], 25 | "license": "Artistic License 2.0", 26 | "homepage": "http://tweene.com", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/SkidX/tweene.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/SkidX/tweene/issues" 33 | }, 34 | "authors": [ 35 | { 36 | "name": "Federico Orru'", 37 | "url": "http://buzzler.com" 38 | } 39 | ], 40 | "main": "./tweene.js", 41 | "dependencies": { 42 | "gsap": "^1.13.2", 43 | "jquery": ">=1.8.x", 44 | "jquery.transit": "^0.9.12", 45 | "velocity-animate": "^1.1.0" 46 | }, 47 | "devDependencies": { 48 | "gsap": "^1.17.0", 49 | "gulp": "^3.9.1", 50 | "gulp-autoprefixer": "^1.0.1", 51 | "gulp-browserify-thin": "^0.1.5", 52 | "gulp-concat": "^2.4.1", 53 | "gulp-header": "^1.1.1", 54 | "gulp-jshint": "^1.8.4", 55 | "gulp-jshint-file-reporter": "0.0.1", 56 | "gulp-karma": "0.0.4", 57 | "gulp-sass": "^2.2.0", 58 | "gulp-size": "^1.1.0", 59 | "gulp-sourcemaps": "^1.2.2", 60 | "gulp-uglifyjs": "^0.4.2", 61 | "gulp-wrapper": "^0.1.5", 62 | "jquery": "^2.1.4", 63 | "jquery.transit": "^0.9.12", 64 | "jshint-stylish": "^1.0.0", 65 | "karma": "^0.12.24", 66 | "karma-chrome-launcher": "^0.1.4", 67 | "karma-coverage": "^0.2.6", 68 | "karma-firefox-launcher": "^0.1.3", 69 | "karma-ie-launcher": "^0.1.5", 70 | "karma-jasmine": "^0.2.2", 71 | "karma-junit-reporter": "^0.2.2", 72 | "merge-stream": "^0.1.5", 73 | "node-sass": "^3.4.2", 74 | "velocity-animate": "^1.2.2", 75 | "vinyl-map": "^1.0.1" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/callback.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | /** 15 | * Create a Callback object, used internally by timelines when you add callbacks calls 16 | * @class 17 | * 18 | * @param {function} callback 19 | * @param {object} scope 20 | * @param {array} params 21 | * @param {number} dir - values: 1 | -1 | 0 22 | */ 23 | var Callback = function(callback, scope, params, dir, isPause) 24 | { 25 | this.type = 'callback'; 26 | // unique id 27 | this._id = ++ Tw._idCounter; 28 | 29 | this.isPause = !!isPause; 30 | 31 | dir = dir === 1? true : (dir === -1? false : null); 32 | var parent = null; 33 | 34 | /** 35 | * Get or set the parent timeline object 36 | * 37 | * @param {object} [value] - parent object 38 | * @returns {object|this} 39 | */ 40 | this.parent = function(value) 41 | { 42 | if(!value) 43 | { 44 | return parent; 45 | } 46 | parent = value; 47 | return this; 48 | }; 49 | 50 | 51 | /** 52 | * Return the unique identifier 53 | * 54 | * @returns {number} 55 | */ 56 | this.id = function() 57 | { 58 | return this._id; 59 | }; 60 | 61 | 62 | /** 63 | * Duration of a callback inside a timeline is always 0, this is needed because internally they are handled as tweens 64 | * 65 | * @returns {number} 66 | */ 67 | this.totalDuration = function() 68 | { 69 | return 0; 70 | }; 71 | 72 | 73 | /** 74 | * Execute the callback if the parent's direction is coherent with the callback's dir value 75 | * 76 | * @returns {this} 77 | */ 78 | this.resume = function() 79 | { 80 | if(callback && (dir === null || dir != parent.reversed())) 81 | { 82 | callback.apply(scope || parent, params); 83 | } 84 | return this; 85 | }; 86 | 87 | 88 | 89 | }; 90 | 91 | -------------------------------------------------------------------------------- /examples/img/space-planet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 37 | 41 | 42 | 45 | 49 | 50 | 53 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/scss/_icon.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icon'; 3 | src: url('../font/icon.eot?34240226'); 4 | src: url('../font/icon.eot?34240226#iefix') format('embedded-opentype'), 5 | url('../font/icon.woff?34240226') format('woff'), 6 | url('../font/icon.ttf?34240226') format('truetype'), 7 | url('../font/icon.svg?34240226#icon') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | @media screen and (-webkit-min-device-pixel-ratio:0) { 13 | @font-face { 14 | font-family: 'icon'; 15 | src: url('../font/icon.svg?34240226#icon') format('svg'); 16 | } 17 | } 18 | 19 | 20 | [class^="icon-"]:before, [class*=" icon-"]:before { 21 | font-family: "icon"; 22 | font-style: normal; 23 | font-weight: normal; 24 | speak: none; 25 | 26 | display: inline-block; 27 | text-decoration: inherit; 28 | width: 1em; 29 | margin-right: .2em; 30 | text-align: center; 31 | /* opacity: .8; */ 32 | 33 | /* For safety - reset parent styles, that can break glyph codes*/ 34 | font-variant: normal; 35 | text-transform: none; 36 | 37 | /* fix buttons height, for twitter bootstrap */ 38 | line-height: 1em; 39 | 40 | /* Animation center compensation - margins should be symmetric */ 41 | /* remove if not needed */ 42 | margin-left: .2em; 43 | 44 | /* you can be more comfortable with increased icons size */ 45 | /* font-size: 120%; */ 46 | 47 | /* Uncomment for 3D effect */ 48 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 49 | } 50 | 51 | .icon-right:before { content: '\e800'; } /* '' */ 52 | .icon-left:before { content: '\e801'; } /* '' */ 53 | .icon-up:before { content: '\e802'; } /* '' */ 54 | .icon-down:before { content: '\e803'; } /* '' */ 55 | .icon-download:before { content: '\e804'; } /* '' */ 56 | .icon-play:before { content: '\e805'; } /* '' */ 57 | .icon-stop:before { content: '\e806'; } /* '' */ 58 | .icon-pause:before { content: '\e807'; } /* '' */ 59 | .icon-to-end:before { content: '\e808'; } /* '' */ 60 | .icon-ok:before { content: '\e809'; } /* '' */ 61 | .icon-cancel:before { content: '\e80a'; } /* '' */ 62 | .icon-to-start:before { content: '\e80b'; } /* '' */ 63 | .icon-archive:before { content: '\e80c'; } /* '' */ 64 | .icon-github:before { content: '\e80d'; } /* '' */ 65 | .icon-fast-forward:before { content: '\e80e'; } /* '' */ 66 | .icon-fast-backward:before { content: '\e80f'; } /* '' */ 67 | .icon-ccw:before { content: '\e810'; } /* '' */ 68 | .icon-gauge:before { content: '\e811'; } /* '' */ 69 | .icon-menu:before { content: '\e812'; } /* '' */ -------------------------------------------------------------------------------- /examples/space-invaders.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Space Invaders :: Tweene :: JavaScript Animation Proxy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 |
26 | 27 |
28 |

Select the animation library:

29 |
    30 |
  • GSAP
  • 31 |
  • Transit (CSS Transitions)
  • 32 |
  • Velocity.js
  • 33 |
34 | 35 |

Use Left and Right arrow keys for move and Space for shot.

36 |
37 |
38 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(config) { 3 | config.set({ 4 | 5 | // base path that will be used to resolve all patterns (eg. files, exclude) 6 | basePath: '', 7 | 8 | 9 | // frameworks to use 10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 11 | frameworks: ['jasmine'], 12 | 13 | 14 | // list of files / patterns to load in the browser 15 | files: [ 16 | 'vendor/vendor.js', 17 | 'test/vendor.js', 18 | 'src/tweene.js', 19 | 'src/common.js', 20 | 'src/label.js', 21 | 'src/callback.js', 22 | 'src/ticker.js', 23 | 'src/tween-common.js', 24 | 'src/timeline-common.js', 25 | 'src/controls-pro.js', 26 | 'src/tween-pro.js', 27 | 'src/timeline-pro.js', 28 | 'src/tweene-dummy.js', 29 | 'src/tweene-gsap.js', 30 | 'src/tweene-velocity.js', 31 | 'src/tweene-transit.js', 32 | 'src/tweene-jquery.js', 33 | 'test/spec.js' 34 | ], 35 | 36 | 37 | // list of files to exclude 38 | exclude: [ 39 | ], 40 | 41 | 42 | // preprocess matching files before serving them to the browser 43 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 44 | preprocessors: { 45 | 'src/*.js': ['coverage'] 46 | }, 47 | 48 | // test results reporter to use 49 | // possible values: 'dots', 'progress' 50 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 51 | reporters: [ 52 | 'progress', 53 | 'coverage' 54 | ], 55 | 56 | coverageReporter: { 57 | reporters: [ 58 | { 59 | type: 'text' 60 | } 61 | ] 62 | }, 63 | 64 | // web server port 65 | port: 9876, 66 | 67 | 68 | // enable / disable colors in the output (reporters and logs) 69 | colors: true, 70 | 71 | 72 | // level of logging 73 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 74 | logLevel: config.LOG_INFO, 75 | 76 | 77 | // enable / disable watching file and executing tests whenever any file changes 78 | autoWatch: true, 79 | 80 | 81 | // start these browsers 82 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 83 | browsers: [ 84 | 'Chrome', 85 | 'Firefox', 86 | 'IE' 87 | ], 88 | 89 | 90 | // Continuous Integration mode 91 | // if true, Karma captures browsers, runs the tests and exits 92 | singleRun: true 93 | }); 94 | }; 95 | -------------------------------------------------------------------------------- /examples/img/alien.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 18 | 19 | 21 | 22 | 24 | image/svg+xml 25 | 27 | 28 | 29 | 30 | 31 | 34 | 37 | 40 | 44 | 45 | 48 | 52 | 53 | 56 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/img/rocket-03.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 37 | 41 | 42 | 45 | 49 | 50 | 53 | 57 | 58 | 61 | 65 | 66 | 69 | 73 | 74 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /examples/css-transitions-control.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CSS Transitions Control :: Tweene :: JavaScript Animation Proxy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 |
    26 |
  • 27 |
  • 28 |
  • 29 |
  • 30 |
31 |
32 | 33 |
34 |
35 | GSAP 36 | Transit (CSS Transitions) 37 | 38 | 39 |

This example shows the same tween applied to two different images: the first one 40 | uses Tweene with GSAP library, that second one uses Tweene with Transit, a library that animates DOM elements 41 | with CSS Transitions technology. 42 | As you can see, pause, resume and reverse actions honor the original easing function.
43 | You can try by yourself changing the easing function with one of those supported by Tweene. 44 |

45 |
46 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/tweene-dummy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | /** 15 | * Dummy tween driver. Dummies are used to emulate delay and to fill time gaps between real tweens inside timelines 16 | * @class 17 | * @mixes Common, TweenCommon, ControlsPro, TweenPro 18 | * 19 | * suitable OST https://www.youtube.com/watch?v=GaUqpnHvua8 20 | * 21 | */ 22 | Tw.registerDriver('Dummy', 'tween', function() { 23 | Common.call(this); 24 | TweenCommon.call(this); 25 | ControlsPro.call(this); 26 | TweenPro.call(this); 27 | 28 | 29 | this._driverTimeUnit = 'ms'; 30 | this._emulatedPlayhead = true; 31 | this._emulatedProgress = true; 32 | 33 | this 34 | .setCoreHandler('end', 'resetPos', this._resetPosition, this) 35 | .setCoreHandler('reverse', 'resetPos', this._resetPosition, this) 36 | .setCoreHandler('end', '_progress', this._stopProgress, this) 37 | .setCoreHandler('reverse', '_progress', this._stopProgress, this); 38 | 39 | 40 | /** 41 | * nothing to invalidate here 42 | * 43 | * @returns {this} 44 | */ 45 | this.invalidate = function() 46 | { 47 | return this; 48 | }; 49 | 50 | 51 | /** 52 | * Override ControlsPro.pause() 53 | * 54 | * @returns {this} 55 | */ 56 | this.pause = function() 57 | { 58 | if(!this._paused) 59 | { 60 | this._stopProgress(); 61 | // remove callback from ticker queue 62 | Tw.ticker.removeCallback(this._id); 63 | this._paused = true; 64 | this._pauseTime = Tw.ticker.now(); 65 | this._position += (this._pauseTime - this._startTime) * this.getRealSpeed() * (this._localFwd? 1 : -1); 66 | } 67 | return this; 68 | }; 69 | 70 | 71 | /** 72 | * Set internal position of dummy 73 | * 74 | * @param {number} value 75 | * @returns {this} 76 | */ 77 | this.position = function(value) 78 | { 79 | this._position = value; 80 | if(value === 0) 81 | { 82 | this._playAllowed = true; 83 | } 84 | else if(value == this._duration) 85 | { 86 | this._reverseAllowed = true; 87 | } 88 | 89 | return this; 90 | }; 91 | 92 | 93 | /** 94 | * Set dummy duration 95 | * 96 | * @param {number} value 97 | * @returns {this} 98 | */ 99 | this.duration = function(value) 100 | { 101 | this._duration = value; 102 | return this; 103 | }; 104 | 105 | 106 | /** 107 | * Override ControlsPro.resume() 108 | * 109 | * @returns {this} 110 | */ 111 | this.resume = function() 112 | { 113 | if(this._paused) 114 | { 115 | this._running = true; 116 | this._paused = false; 117 | var handler = this._localFwd? 'end' : 'reverse'; 118 | 119 | if(this._localFwd && this._position === 0) 120 | { 121 | this._runHandlers('begin'); 122 | } 123 | var duration = (this._localFwd? this._duration - this._position : this._position) / this.getRealSpeed(); 124 | if(!duration) 125 | { 126 | this._runHandlers(handler); 127 | } 128 | else 129 | { 130 | var params = [duration, this._id, this._runHandlers, this, [handler]]; 131 | this._startTime = Tw.ticker.now(); 132 | // add callback in ticker queue 133 | Tw.ticker.addCallback.apply(Tw.ticker, params); 134 | this._startProgress(); 135 | } 136 | } 137 | return this; 138 | }; 139 | 140 | 141 | this._backTween = function() {}; 142 | 143 | }); 144 | 145 | -------------------------------------------------------------------------------- /src/ticker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | /** 15 | * Ticker object used to emulate delay, progress callbacks, async calls. 16 | * It uses RequestAnimationFrame when available, with fallback to setTimeout. 17 | * We instantiate a single Ticker in Tweene.ticker 18 | * @class 19 | * 20 | */ 21 | var Ticker = function() 22 | { 23 | var _lastTime = 0; 24 | var _callbacks = []; 25 | 26 | this.now = Date.now || function() 27 | { 28 | return new Date().getTime(); 29 | }; 30 | 31 | var _now = this.now; 32 | 33 | /* 34 | * Polyfill taken here: https://gist.github.com/paulirish/1579671 35 | * 36 | * requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel 37 | * MIT license 38 | * 39 | */ 40 | var _requestAnimationFrame = 41 | window.requestAnimationFrame || 42 | window.webkitRequestAnimationFrame || 43 | window.mozRequestAnimationFrame || 44 | window.msRequestAnimationFrame || 45 | window.oRequestAnimationFrame || 46 | 47 | function(callback) 48 | { 49 | var currTime = _now(); 50 | var timeToCall = Math.max(0, 16 - (currTime - _lastTime)); 51 | var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); 52 | _lastTime = currTime + timeToCall; 53 | return id; 54 | }; 55 | 56 | 57 | 58 | var _tick = function() 59 | { 60 | var now = _now(); 61 | var i = 0; 62 | while(i < _callbacks.length) 63 | { 64 | var entry = _callbacks[i]; 65 | var call = (!entry.time || now - entry.now - entry.time >= 0); 66 | if(call && entry.time) 67 | { 68 | // remove expired callback 69 | _callbacks.splice(i, 1); 70 | } 71 | else 72 | { 73 | i++; 74 | } 75 | if(call) 76 | { 77 | entry.callback.apply(entry.scope, entry.params); 78 | } 79 | } 80 | // ticker is turned off when the callbacks list is empty 81 | if(_callbacks.length) 82 | { 83 | _requestAnimationFrame(_tick); 84 | } 85 | }; 86 | 87 | 88 | /** 89 | * Register a callback 90 | * 91 | * @param {number} time - Timeout in ms. When = 0, the function will be called on each tick and need to be unregistered manually 92 | * @param {string} id - unique identifier of the registered callback 93 | * @param {function} callback 94 | * @param {object} [scope] scope used as 'this' inside the callback 95 | * @param {array} [params] params to be passed to the callback on execution 96 | * @returns {this} 97 | */ 98 | this.addCallback = function(time, id, callback, scope, params) 99 | { 100 | this.removeCallback(id); 101 | _callbacks.push({now: _now(), time: time, id: id, callback: callback, scope: scope || this, params: params || []}); 102 | 103 | // turn on ticker if it is the first callback in queue 104 | if(_callbacks.length == 1) 105 | { 106 | _requestAnimationFrame(_tick); 107 | } 108 | return this; 109 | }; 110 | 111 | 112 | /** 113 | * Unregister a callback. Callbacks registered with a positive timeout, commonly do not need to be unregistered manually, 114 | * the ticker unregister them after automatically after their execution. 115 | * 116 | * @param {string} id - unique identifier of the registered callback 117 | * @returns {this} 118 | */ 119 | this.removeCallback = function(id) 120 | { 121 | for(var i = 0, end = _callbacks.length; i < end; i++) 122 | { 123 | if(_callbacks[i].id == id) 124 | { 125 | _callbacks.splice(i, 1); 126 | break; 127 | } 128 | } 129 | return this; 130 | }; 131 | 132 | }; 133 | 134 | 135 | Tw.ticker = new Ticker(); 136 | -------------------------------------------------------------------------------- /examples/speed-control.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speed Control :: Tweene :: JavaScript Animation Proxy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 |
26 | 27 |
28 | 29 | 30 |
31 |

Select the animation library:

32 |
    33 |
  • GSAP
  • 34 |
  • Transit (CSS Transitions)
  • 35 |
  • Velocity.js
  • 36 |
37 | 38 |

Select the animation speed:

39 |
    40 |
  • 0.25X
  • 41 |
  • 0.5X (half speed)
  • 42 |
  • 1 (normal speed)
  • 43 |
  • 2X (double speed)
  • 44 |
  • 3X
  • 45 |
46 |
47 |
48 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /examples/font/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2014 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/css/speed-control.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}html,button,input,select,textarea{font-family:sans-serif;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}h2{font-size:1.5em;margin:0.83em 0;}h3{font-size:1.17em;margin:1em 0;}h4{font-size:1em;margin:1.33em 0;}h5{font-size:0.83em;margin:1.67em 0;}h6{font-size:0.67em;margin:2.33em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}blockquote{margin:1em 40px;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}p,pre{margin:1em 0;}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em;}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word;}q{quotes:none;}q:before,q:after{content:'';content:none;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}dl,menu,ol,ul{margin:1em 0;}dd{margin:0 0 0 40px;}menu,ol,ul{padding:0 0 0 40px;}nav ul,nav ol{list-style:none;list-style-image:none;}img{border:0;-ms-interpolation-mode:bicubic;}svg:not(:root){overflow:hidden;}figure{margin:0;}form{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;white-space:normal;*margin-left:-7px;}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{-moz-box-sizing:border-box;box-sizing:border-box;padding:0;*height:13px;*width:13px;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}html,button,input,select,textarea{color:#222;}body{font-size:1em;line-height:1.4;}::-moz-selection{background:#b3d4fc;text-shadow:none;}::selection{background:#b3d4fc;text-shadow:none;}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0;}img{vertical-align:middle;}fieldset{border:0;margin:0;padding:0;}textarea{resize:vertical;}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0;}.ir{background-color:transparent;border:0;overflow:hidden;*text-indent:-9999px;}.ir:before{content:"";display:block;width:0;height:150%;}.hidden{display:none !important;visibility:hidden;}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto;}.invisible{visibility:hidden;}.clearfix:before,.clearfix:after{content:" ";display:table;}.clearfix:after{clear:both;}.clearfix{*zoom:1;}@media only screen and (min-width: 35em){}@media print,(-o-min-device-pixel-ratio: 5/4),(-webkit-min-device-pixel-ratio: 1.25),(min-resolution: 120dpi){}@media print{@page{margin:0.5cm;}*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important;}a,a:visited{text-decoration:underline;}a[href]:after{content:" (" attr(href) ")";}abbr[title]:after{content:" (" attr(title) ")";}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:"";}pre,blockquote{border:1px solid #999;page-break-inside:avoid;}thead{display:table-header-group;}tr,img{page-break-inside:avoid;}img{max-width:100% !important;}p,h2,h3{orphans:3;widows:3;}h2,h3{page-break-after:avoid;}}*{-moz-box-sizing:border-box;box-sizing:border-box;}*:after,*:before{-moz-box-sizing:border-box;box-sizing:border-box;}html,body,.all{height:100%;margin:0;padding:0;}body{font-family:'Roboto',arial,sans-serif;font-weight:300;font-size:16px;color:#454545;background-color:#62ABBE;line-height:1.9;}p{margin:0 0 0.7em 0;padding:0;text-indent:0;}a{color:#DDDDDD;text-decoration:none;outline:none !important;}a:hover{color:#FFFFFF;text-decoration:none;outline:none !important;}.all{position:relative;width:100%;max-width:960px;height:100%;margin:auto;padding:20px;}.object{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform:translate3d(0, 0, 1px);transform:translate3d(0, 0, 1px);background-size:100% 100%;}.rocket{width:30px;height:50px;left:10px;top:175px;background-image:url('../img/rocket-03.svg');}.planet{width:160px;height:90px;left:160px;top:200px;margin-left:-80px;margin-top:-45px;background-image:url('../img/space-planet.svg');}.controls{position:absolute;right:20px;top:20px;width:300px;}.controls .intro{font-family:'Roboto Condensed',arial,sans-serif;font-weight:300;font-size:20px;text-transform:uppercase;}.controls ul{list-style:circle;}.controls li{cursor:pointer;}.controls li:hover{color:#FFFFFF;}.controls .current{color:#DDDDDD;font-weight:400;}@media screen and (max-width: 767px){.all{padding-left:0;padding-right:0;}.controls{right:auto;top:500px;left:10px;}} -------------------------------------------------------------------------------- /examples/scss/space-invaders.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import "_example.scss"; 4 | 5 | 6 | .alien 7 | { 8 | width: 60px; 9 | height: 45px; 10 | background-image: url('../img/alien.svg'); 11 | } 12 | 13 | 14 | .invader 15 | { 16 | width: 180px; 17 | height: 135px; 18 | left: 50%; 19 | margin-left: -90px; 20 | //top: 10%; 21 | bottom: 100%; 22 | background-image: url('../img/alien.svg'); 23 | } 24 | 25 | .defender 26 | { 27 | width: 50px; 28 | height: 28px; 29 | background-image: url('../img/defender.svg'); 30 | } 31 | 32 | .spaceball 33 | { 34 | width: 9px; 35 | height: 9px; 36 | background-image: url('../img/space-ball.svg'); 37 | } 38 | 39 | 40 | 41 | .aliens 42 | { 43 | left: 0px; 44 | top: 0px; 45 | display: block; 46 | margin: 0; 47 | padding: 0; 48 | list-style: none; 49 | 50 | 51 | ul 52 | { 53 | display: block; 54 | margin: 0; 55 | padding: 0; 56 | list-style: none; 57 | } 58 | 59 | .row 60 | { 61 | display: block; 62 | padding: 0; 63 | 64 | height: 30px; 65 | white-space: nowrap; 66 | } 67 | 68 | .row:last-child 69 | { 70 | margin-bottom: 0; 71 | } 72 | 73 | .col 74 | { 75 | position: relative; 76 | display: inline-block; 77 | padding: 0; 78 | width: 40px; 79 | height: 30px; 80 | } 81 | 82 | .col:last-child 83 | { 84 | margin-right: 0; 85 | } 86 | 87 | } 88 | 89 | 90 | .alien 91 | { 92 | left: 0; 93 | top: 0; 94 | width: 40px; 95 | height: 30px; 96 | } 97 | 98 | 99 | .defender 100 | { 101 | left: 0px; 102 | bottom: 60px; 103 | z-index: 3; 104 | 105 | width: 40px; 106 | height: 22.4px; 107 | } 108 | 109 | .spaceball 110 | { 111 | left: 17px; 112 | bottom: 84px; 113 | z-index: 22; 114 | width: 6px; 115 | height: 6px; 116 | } 117 | 118 | .controls 119 | { 120 | position: absolute; 121 | left: 0; 122 | bottom: 0; 123 | width: 100%; 124 | height: 58px; 125 | z-index: 20; 126 | background-color: #62ABBE; 127 | 128 | 129 | > span 130 | { 131 | display: inline-block; 132 | height: 34px; 133 | border: 1px solid #CCCCCC; 134 | border-radius: 6px; 135 | margin: 20px 0 0 6px; 136 | text-align: center; 137 | font-size: 18px; 138 | line-height: 30px; 139 | text-transform: uppercase; 140 | cursor: pointer; 141 | } 142 | 143 | .left 144 | { 145 | width: 55px; 146 | } 147 | 148 | .right 149 | { 150 | width: 55px; 151 | } 152 | 153 | .space 154 | { 155 | width: 186px; 156 | } 157 | 158 | } 159 | 160 | 161 | .win 162 | { 163 | left: 0; 164 | top: 40%; 165 | width: 100%; 166 | font-size: 48px; 167 | text-align: center; 168 | text-transform: uppercase; 169 | font-family: $headingFontFamily; 170 | font-weight: 300; 171 | line-height: 1; 172 | display: none; 173 | } 174 | 175 | 176 | .lose 177 | { 178 | left: 0; 179 | top: 50%; 180 | width: 100%; 181 | font-size: 48px; 182 | text-align: center; 183 | text-transform: uppercase; 184 | font-family: $headingFontFamily; 185 | font-weight: 300; 186 | line-height: 1; 187 | display: none; 188 | 189 | } 190 | 191 | .restart 192 | { 193 | left: 0; 194 | top: 70%; 195 | width: 100%; 196 | font-size: 28px; 197 | text-align: center; 198 | text-transform: uppercase; 199 | font-family: $headingFontFamily; 200 | font-weight: 300; 201 | line-height: 1; 202 | color: #DDDDDD; 203 | cursor: pointer; 204 | display: none; 205 | 206 | &:hover 207 | { 208 | color: #FFFFFF; 209 | } 210 | } 211 | 212 | 213 | 214 | .game 215 | { 216 | position: relative; 217 | width: 320px; 218 | height: 400px; 219 | overflow: hidden; 220 | background-color: #62ABBE; 221 | 222 | 223 | &.auto 224 | { 225 | .restart, .controls, .start 226 | { 227 | display: none !important; 228 | } 229 | 230 | 231 | .defender 232 | { 233 | bottom: 0; 234 | } 235 | 236 | .spaceball 237 | { 238 | bottom: 24px; 239 | } 240 | } 241 | 242 | } 243 | 244 | .driverControls 245 | { 246 | position: absolute; 247 | right: 20px; 248 | top: 20px; 249 | width: 300px; 250 | 251 | 252 | .intro 253 | { 254 | font-family: $headingFontFamily; 255 | font-weight: 300; 256 | font-size: 20px; 257 | text-transform: uppercase; 258 | } 259 | 260 | ul 261 | { 262 | list-style: circle; 263 | } 264 | 265 | li 266 | { 267 | cursor: pointer; 268 | } 269 | 270 | li:hover 271 | { 272 | color: #FFFFFF; 273 | } 274 | 275 | .current 276 | { 277 | color: #DDDDDD; 278 | font-weight: 400; 279 | } 280 | } 281 | 282 | 283 | @media screen and (max-width: 767px) 284 | { 285 | .all 286 | { 287 | padding-left: 0; 288 | padding-right: 0; 289 | } 290 | 291 | .driverControls 292 | { 293 | right: auto; 294 | top: 500px; 295 | left: 10px; 296 | } 297 | } 298 | 299 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tweene - JS Animation Proxy - v0.5.10 2 | 3 | __Tweene__ is a JS library that helps to improve your favourite animation engine, allowing you to do more and better. 4 | 5 | There are already a lot of good JavaScript animation libraries on the market, each one with specific features, strengths and weaknesses. 6 | Each programmer and each project have their specific requirements, so sometimes one library may be suitable while other times it could not. 7 | 8 | __Tweene__ is something different. It is an animation proxy: used as a wrapper of your chosen library, it may allow you to 9 | 10 | - write animations easily, thanks to its versatile interface that adapts itself to your programming style 11 | - gain extra features (play, pause, reverse, resume, restart and speed control, Timelines) 12 | - switch easily from one library to another any time you want. 13 | 14 | Currently it can work together with [GSAP](http://www.greensock.com/gsap-js/), [Velocity.js](http://julian.com/research/velocity/), 15 | [Transit (CSS Transitions)](http://ricostacruz.com/jquery.transit/) or [jQuery](http://jquery.com). 16 | 17 | ## Resources 18 | - [Features](http://tweene.com/#features) 19 | - [Documentation](http://tweene.com/docs) 20 | - [Examples](http://tweene.com/#examples) 21 | 22 | ## Getting started 23 | To start using Tweene just include the script after your animation library of choice. 24 | 25 | [jsDelivr CDN](http://www.jsdelivr.com/#!tweene) provides free hosting for Tweene. 26 | You can simply replace the script URL with one of the minified files on jsDelivr like this: 27 | ```html 28 | 29 | ``` 30 | For more details, like version aliasing, please visit the [README](https://github.com/jsdelivr/jsdelivr#usage). 31 | 32 | Alternatively, you can download the repository and host the files locally. 33 | 34 | ```html 35 | // use Tweene with GSAP 36 | 37 | 38 | 39 | // use Tweene with jQuery 40 | ; 41 | 42 | // or fetch all with a single HTTP request 43 | 44 | 45 | // use Tweene with Transit 46 | ; 47 | 48 | 49 | // or fetch all with a single HTTP request 50 | 51 | 52 | // use Tweene with Velocity.js 53 | ; 54 | 55 | 56 | // or fetch all with a single HTTP request 57 | 58 | 59 | // use Tweene with more then one library 60 | 61 | 62 | 63 | 64 | 65 | 66 | 73 | ``` 74 | 75 | ### Package managers 76 | 77 | `bower install tweene`
78 | or
79 | `npm install tweene` 80 | 81 | ```js 82 | // use Tweene with more then one library 83 | require('tweene'); 84 | 85 | // use Tweene with GSAP 86 | require('tweene/gsap'); 87 | 88 | // use Tweene with jQuery 89 | require('tweene/jquery'); 90 | 91 | // use Tweene with Transit 92 | require('tweene/transit'); 93 | 94 | // use Tweene with Velocity.js 95 | require('tweene/velocity'); 96 | ``` 97 | 98 | ## Time unit 99 | Tweene tries to accommodate your current programming habits, not to force you to change them. For this reason, you can configure the default time unit used to indicate durations and delays of your tweens, by changing the value of Tweene.defaultTimeUnit (accepted value: 's' or 'ms'). 100 | Since the GSAP library uses natively seconds as time unit, when you will use only that specific driver through tweene-gsap.min.js or require('tweene/gsap') please note that the predefined value of Tweene.defaultTimeUnit will be 's'. In all other cases, it defaults to 'ms'. 101 | However, you can change it any time you want and also on a single call basis. 102 | Check http://tweene.com/docs/#duration for more details. 103 | 104 | ## Changelog 105 | - __0.5.11__ Fixed a label related issue. 106 | - __0.5.10__ Fixed bug in jQuery driver. 107 | - __0.5.9__ Fixed restart() procedure when timelines are paused. 108 | - __0.5.8__ Added addPause() method. Fixed events call order. 109 | - __0.5.7__ Fixed wrong link to Velocity.js homepage 110 | - __0.5.6__ Fixed require() return value. Roadmap added. 111 | - __0.5.5__ Added references for CDN hosting support. 112 | - __0.5.4__ Renamed some internal vars. Added more details in README and comments. 113 | - __0.5.3__ Renamed all files in lowercase. Fixed jQuery minimum version in package.json dependencies. 114 | - __0.5.2__ Added support for npm and bower. 115 | - __0.5.1__ Predefined transforms order: now transformations are applied always in the same order. Fixed some minor glitches with CSS transitions. 116 | - __0.5.0__ First public release 117 | 118 | ## License 119 | 120 | Tweene is available under Artistic License 2.0, check the LICENSE.txt inside the archive for details. 121 | 122 | Animation libraries of your choice are not included and have their own license agreement. 123 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 2 | var gulp = require('gulp'), 3 | jshint = require('gulp-jshint'), 4 | karma = require('karma').server, 5 | concat = require('gulp-concat'), 6 | map = require('vinyl-map'), 7 | size = require('gulp-size'), 8 | uglify = require('gulp-uglifyjs'), 9 | wrapper = require('gulp-wrapper'), 10 | merge = require('merge-stream'), 11 | browserify = require('gulp-browserify-thin'), 12 | header = require('gulp-header'), 13 | sourcemaps = require('gulp-sourcemaps'), 14 | sass = require('gulp-sass'), 15 | autoprefixer = require('gulp-autoprefixer'), 16 | pkg = require('./package.json'); 17 | 18 | 19 | 20 | gulp.task('lint', function() { 21 | return gulp.src('./src/*.js') 22 | .pipe(jshint()) 23 | .pipe(jshint.reporter('jshint-stylish')); 24 | }); 25 | 26 | 27 | gulp.task('test', function (done) { 28 | karma.start({ 29 | configFile: __dirname + '/karma.conf.js', 30 | singleRun: true 31 | }, function(){ 32 | done(); 33 | }); 34 | }); 35 | 36 | 37 | gulp.task('testing', function (done) { 38 | karma.start({ 39 | configFile: __dirname + '/karma.conf.js', 40 | singleRun: false 41 | }, done); 42 | }); 43 | 44 | var modulesPath = 'src/'; 45 | 46 | var commons = [ 47 | modulesPath + 'tweene.js', 48 | modulesPath + 'common.js', 49 | modulesPath + 'label.js', 50 | modulesPath + 'callback.js', 51 | modulesPath + 'tween-common.js', 52 | modulesPath + 'timeline-common.js' 53 | ]; 54 | 55 | var pro = [ 56 | modulesPath + 'ticker.js', 57 | modulesPath + 'controls-pro.js', 58 | modulesPath + 'tween-pro.js', 59 | modulesPath + 'timeline-pro.js', 60 | modulesPath + 'tweene-dummy.js' 61 | ]; 62 | 63 | var drivers = { 64 | tweene: { 65 | files: commons.concat(pro, [ 66 | modulesPath + 'tweene-gsap.js', 67 | modulesPath + 'tweene-velocity.js', 68 | modulesPath + 'tweene-transit.js', 69 | modulesPath + 'tweene-jquery.js' 70 | ]), 71 | deps: ['jquery', 'jquery.transit', 'velocity-animate', 'gsap'] 72 | }, 73 | 74 | jquery: { 75 | files: commons.concat(pro, [ 76 | modulesPath + 'tweene-jquery.js' 77 | ]), 78 | deps: ['jquery'] 79 | }, 80 | 81 | transit: { 82 | files: commons.concat(pro, [ 83 | modulesPath + 'tweene-transit.js' 84 | ]), 85 | deps: ['jquery', 'jquery.transit'] 86 | }, 87 | 88 | velocity: { 89 | files: commons.concat(pro, [ 90 | modulesPath + 'tweene-velocity.js' 91 | ]), 92 | deps: ['jquery', 'velocity-animate'] 93 | }, 94 | 95 | gsap: { 96 | files: commons.concat([ 97 | modulesPath + 'tweene-gsap.js' 98 | ]), 99 | deps: ['gsap'] 100 | } 101 | 102 | }; 103 | 104 | 105 | var banner = [ 106 | '/**', 107 | ' * <%= pkg.title %> - <%= pkg.description %>', 108 | ' * @version <%= pkg.version %>', 109 | ' * @link <%= pkg.homepage %>', 110 | " * Copyright (c) 2014, Federico Orru' ", 111 | ' * ', 112 | ' * @license <%= pkg.license %>', 113 | ' * See LICENSE.txt for details', 114 | ' * ', 115 | ' */', 116 | '' 117 | ].join('\n'); 118 | 119 | 120 | gulp.task('src', function(){ 121 | 122 | var header = 123 | ";(function (window) {\n" + 124 | "'use strict'; \n" + 125 | "var func = function(window, undef) {\n" + 126 | "'use strict'; \n\n", 127 | footer = '', 128 | driver, srcs, deps, streams = []; 129 | 130 | for(driver in drivers) 131 | { 132 | srcs = drivers[driver].files; 133 | deps = drivers[driver].deps; 134 | footer = 135 | "return Tw;\n" + 136 | "};\n\n" + 137 | "if(typeof(define) === 'function' && define.amd) {\n" + 138 | " define(['" + deps.join("', '") + "'], func.bind(this, window));\n" + 139 | "} else if(typeof(module) !== 'undefined' && module.exports) {\n" + 140 | " var mod;\n"; 141 | for(var i = 0, end = deps.length; i < end; i++) 142 | { 143 | footer += " mod = require('" + deps[i] + "');\n"; 144 | if(deps[i] == 'jquery') 145 | { 146 | footer += " if(window) window.jQuery = window.$ = mod;\n"; 147 | } 148 | } 149 | footer += 150 | "module.exports = func(window);\n" + 151 | "} else {\n" + 152 | " func(window);\n" + 153 | "}\n" + 154 | "}(typeof(global) !== 'undefined'? global : window));\n"; 155 | 156 | streams.push( 157 | gulp.src(srcs) 158 | .pipe(sourcemaps.init()) 159 | .pipe(concat(driver + '.js')) 160 | .pipe(wrapper({ 161 | header: header, 162 | footer: footer 163 | })) 164 | .pipe(sourcemaps.write('.')) 165 | .pipe(gulp.dest('./')) 166 | ); 167 | } 168 | return merge.apply(null, streams); 169 | }); 170 | 171 | gulp.task('vendor', function() 172 | { 173 | return browserify() 174 | .require(['jquery', 'velocity-animate', 'jquery.transit', 'gsap']) 175 | .bundle('vendor.js') 176 | .on('error', function(err) 177 | { 178 | console.error(err.toString()); 179 | process.exit(1); 180 | }) 181 | .pipe(gulp.dest('./vendor')) 182 | .pipe(gulp.dest('./examples/js')); 183 | }); 184 | 185 | 186 | gulp.task('css', function () { 187 | gulp.src('examples/scss/*.scss') 188 | .pipe(sass({ 189 | errLogToConsole: true, 190 | outputStyle: 'compressed' 191 | })) 192 | .pipe(autoprefixer({ 193 | cascade: false 194 | })) 195 | .pipe(gulp.dest('examples/css')); 196 | }); 197 | 198 | 199 | 200 | gulp.task('default', ['src'], function(){ 201 | 202 | var driver, streams = [], suffix; 203 | 204 | for(driver in drivers) 205 | { 206 | suffix = driver == 'tweene'? 'all' : driver; 207 | streams.push( 208 | gulp.src(driver + '.js') 209 | .pipe(uglify('tweene-' + suffix + '.min.js')) 210 | .pipe(header(banner, {pkg: pkg})) 211 | .pipe(size({gzip: true, title: driver + ': '})) 212 | .pipe(gulp.dest('minified/')) 213 | ); 214 | } 215 | return merge.apply(null, streams); 216 | 217 | }); 218 | 219 | 220 | var srcWatcher = gulp.watch('src/modules/*.js', ['src']); 221 | srcWatcher.on('change', function(event) { 222 | console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); 223 | }); 224 | 225 | 226 | var cssWatcher = gulp.watch('examples/scss/*.scss', ['css']); 227 | cssWatcher.on('change', function(event) { 228 | console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); 229 | }); 230 | -------------------------------------------------------------------------------- /examples/scss/_main.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * HTML5 Boilerplate 3 | * 4 | * What follows is the result of much research on cross-browser styling. 5 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 6 | * Kroc Camen, and the H5BP dev community and team. 7 | */ 8 | 9 | /* ========================================================================== 10 | Base styles: opinionated defaults 11 | ========================================================================== */ 12 | 13 | 14 | html, 15 | button, 16 | input, 17 | select, 18 | textarea { 19 | color: #222; 20 | } 21 | 22 | body { 23 | font-size: 1em; 24 | line-height: 1.4; 25 | } 26 | 27 | /* 28 | * Remove text-shadow in selection highlight: h5bp.com/i 29 | * These selection declarations have to be separate. 30 | * Customize the background color to match your design. 31 | */ 32 | 33 | ::-moz-selection { 34 | background: #b3d4fc; 35 | text-shadow: none; 36 | } 37 | 38 | ::selection { 39 | background: #b3d4fc; 40 | text-shadow: none; 41 | } 42 | 43 | /* 44 | * A better looking default horizontal rule 45 | */ 46 | 47 | hr { 48 | display: block; 49 | height: 1px; 50 | border: 0; 51 | border-top: 1px solid #ccc; 52 | margin: 1em 0; 53 | padding: 0; 54 | } 55 | 56 | /* 57 | * Remove the gap between images and the bottom of their containers: h5bp.com/i/440 58 | */ 59 | 60 | img { 61 | vertical-align: middle; 62 | } 63 | 64 | /* 65 | * Remove default fieldset styles. 66 | */ 67 | 68 | fieldset { 69 | border: 0; 70 | margin: 0; 71 | padding: 0; 72 | } 73 | 74 | /* 75 | * Allow only vertical resizing of textareas. 76 | */ 77 | 78 | textarea { 79 | resize: vertical; 80 | } 81 | 82 | /* ========================================================================== 83 | Chrome Frame prompt 84 | ========================================================================== */ 85 | 86 | .chromeframe { 87 | margin: 0.2em 0; 88 | background: #ccc; 89 | color: #000; 90 | padding: 0.2em 0; 91 | } 92 | 93 | /* ========================================================================== 94 | Author's custom styles 95 | ========================================================================== */ 96 | 97 | 98 | /* ========================================================================== 99 | Helper classes 100 | ========================================================================== */ 101 | 102 | /* 103 | * Image replacement 104 | */ 105 | 106 | .ir { 107 | background-color: transparent; 108 | border: 0; 109 | overflow: hidden; 110 | /* IE 6/7 fallback */ 111 | *text-indent: -9999px; 112 | } 113 | 114 | .ir:before { 115 | content: ""; 116 | display: block; 117 | width: 0; 118 | height: 150%; 119 | } 120 | 121 | /* 122 | * Hide from both screenreaders and browsers: h5bp.com/u 123 | */ 124 | 125 | .hidden { 126 | display: none !important; 127 | visibility: hidden; 128 | } 129 | 130 | /* 131 | * Hide only visually, but have it available for screenreaders: h5bp.com/v 132 | */ 133 | 134 | .visuallyhidden { 135 | border: 0; 136 | clip: rect(0 0 0 0); 137 | height: 1px; 138 | margin: -1px; 139 | overflow: hidden; 140 | padding: 0; 141 | position: absolute; 142 | width: 1px; 143 | } 144 | 145 | /* 146 | * Extends the .visuallyhidden class to allow the element to be focusable 147 | * when navigated to via the keyboard: h5bp.com/p 148 | */ 149 | 150 | .visuallyhidden.focusable:active, 151 | .visuallyhidden.focusable:focus { 152 | clip: auto; 153 | height: auto; 154 | margin: 0; 155 | overflow: visible; 156 | position: static; 157 | width: auto; 158 | } 159 | 160 | /* 161 | * Hide visually and from screenreaders, but maintain layout 162 | */ 163 | 164 | .invisible { 165 | visibility: hidden; 166 | } 167 | 168 | /* 169 | * Clearfix: contain floats 170 | * 171 | * For modern browsers 172 | * 1. The space content is one way to avoid an Opera bug when the 173 | * `contenteditable` attribute is included anywhere else in the document. 174 | * Otherwise it causes space to appear at the top and bottom of elements 175 | * that receive the `clearfix` class. 176 | * 2. The use of `table` rather than `block` is only necessary if using 177 | * `:before` to contain the top-margins of child elements. 178 | */ 179 | 180 | .clearfix:before, 181 | .clearfix:after { 182 | content: " "; /* 1 */ 183 | display: table; /* 2 */ 184 | } 185 | 186 | .clearfix:after { 187 | clear: both; 188 | } 189 | 190 | /* 191 | * For IE 6/7 only 192 | * Include this rule to trigger hasLayout and contain floats. 193 | */ 194 | 195 | .clearfix { 196 | *zoom: 1; 197 | } 198 | 199 | /* ========================================================================== 200 | EXAMPLE Media Queries for Responsive Design. 201 | Theses examples override the primary ('mobile first') styles. 202 | Modify as content requires. 203 | ========================================================================== */ 204 | 205 | @media only screen and (min-width: 35em) { 206 | /* Style adjustments for viewports that meet the condition */ 207 | } 208 | 209 | @media print, 210 | (-o-min-device-pixel-ratio: 5/4), 211 | (-webkit-min-device-pixel-ratio: 1.25), 212 | (min-resolution: 120dpi) { 213 | /* Style adjustments for high resolution devices */ 214 | } 215 | 216 | /* ========================================================================== 217 | Print styles. 218 | Inlined to avoid required HTTP connection: h5bp.com/r 219 | ========================================================================== */ 220 | 221 | @media print { 222 | * { 223 | background: transparent !important; 224 | color: #000 !important; /* Black prints faster: h5bp.com/s */ 225 | box-shadow: none !important; 226 | text-shadow: none !important; 227 | } 228 | 229 | a, 230 | a:visited { 231 | text-decoration: underline; 232 | } 233 | 234 | a[href]:after { 235 | content: " (" attr(href) ")"; 236 | } 237 | 238 | abbr[title]:after { 239 | content: " (" attr(title) ")"; 240 | } 241 | 242 | /* 243 | * Don't show links for images, or javascript/internal links 244 | */ 245 | 246 | .ir a:after, 247 | a[href^="javascript:"]:after, 248 | a[href^="#"]:after { 249 | content: ""; 250 | } 251 | 252 | pre, 253 | blockquote { 254 | border: 1px solid #999; 255 | page-break-inside: avoid; 256 | } 257 | 258 | thead { 259 | display: table-header-group; /* h5bp.com/t */ 260 | } 261 | 262 | tr, 263 | img { 264 | page-break-inside: avoid; 265 | } 266 | 267 | img { 268 | max-width: 100% !important; 269 | } 270 | 271 | @page { 272 | margin: 0.5cm; 273 | } 274 | 275 | p, 276 | h2, 277 | h3 { 278 | orphans: 3; 279 | widows: 3; 280 | } 281 | 282 | h2, 283 | h3 { 284 | page-break-after: avoid; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /examples/css/css-transitions-control.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}html,button,input,select,textarea{font-family:sans-serif;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}h2{font-size:1.5em;margin:0.83em 0;}h3{font-size:1.17em;margin:1em 0;}h4{font-size:1em;margin:1.33em 0;}h5{font-size:0.83em;margin:1.67em 0;}h6{font-size:0.67em;margin:2.33em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}blockquote{margin:1em 40px;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}p,pre{margin:1em 0;}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em;}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word;}q{quotes:none;}q:before,q:after{content:'';content:none;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}dl,menu,ol,ul{margin:1em 0;}dd{margin:0 0 0 40px;}menu,ol,ul{padding:0 0 0 40px;}nav ul,nav ol{list-style:none;list-style-image:none;}img{border:0;-ms-interpolation-mode:bicubic;}svg:not(:root){overflow:hidden;}figure{margin:0;}form{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;white-space:normal;*margin-left:-7px;}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{-moz-box-sizing:border-box;box-sizing:border-box;padding:0;*height:13px;*width:13px;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}html,button,input,select,textarea{color:#222;}body{font-size:1em;line-height:1.4;}::-moz-selection{background:#b3d4fc;text-shadow:none;}::selection{background:#b3d4fc;text-shadow:none;}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0;}img{vertical-align:middle;}fieldset{border:0;margin:0;padding:0;}textarea{resize:vertical;}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0;}.ir{background-color:transparent;border:0;overflow:hidden;*text-indent:-9999px;}.ir:before{content:"";display:block;width:0;height:150%;}.hidden{display:none !important;visibility:hidden;}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto;}.invisible{visibility:hidden;}.clearfix:before,.clearfix:after{content:" ";display:table;}.clearfix:after{clear:both;}.clearfix{*zoom:1;}@media only screen and (min-width: 35em){}@media print,(-o-min-device-pixel-ratio: 5/4),(-webkit-min-device-pixel-ratio: 1.25),(min-resolution: 120dpi){}@media print{@page{margin:0.5cm;}*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important;}a,a:visited{text-decoration:underline;}a[href]:after{content:" (" attr(href) ")";}abbr[title]:after{content:" (" attr(title) ")";}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:"";}pre,blockquote{border:1px solid #999;page-break-inside:avoid;}thead{display:table-header-group;}tr,img{page-break-inside:avoid;}img{max-width:100% !important;}p,h2,h3{orphans:3;widows:3;}h2,h3{page-break-after:avoid;}}*{-moz-box-sizing:border-box;box-sizing:border-box;}*:after,*:before{-moz-box-sizing:border-box;box-sizing:border-box;}html,body,.all{height:100%;margin:0;padding:0;}body{font-family:'Roboto',arial,sans-serif;font-weight:300;font-size:16px;color:#454545;background-color:#62ABBE;line-height:1.9;}p{margin:0 0 0.7em 0;padding:0;text-indent:0;}a{color:#DDDDDD;text-decoration:none;outline:none !important;}a:hover{color:#FFFFFF;text-decoration:none;outline:none !important;}.all{position:relative;width:100%;max-width:960px;height:100%;margin:auto;padding:20px;}.object{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform:translate3d(0, 0, 1px);transform:translate3d(0, 0, 1px);background-size:100% 100%;}@font-face{font-family:'icon';src:url('../font/icon.eot?34240226');src:url('../font/icon.eot?34240226#iefix') format('embedded-opentype'),url('../font/icon.woff?34240226') format('woff'),url('../font/icon.ttf?34240226') format('truetype'),url('../font/icon.svg?34240226#icon') format('svg');font-weight:normal;font-style:normal;}@media screen and (-webkit-min-device-pixel-ratio: 0){@font-face{font-family:'icon';src:url('../font/icon.svg?34240226#icon') format('svg');}}[class^="icon-"]:before,[class*=" icon-"]:before{font-family:"icon";font-style:normal;font-weight:normal;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:0.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:0.2em;}.icon-right:before{content:'\e800';}.icon-left:before{content:'\e801';}.icon-up:before{content:'\e802';}.icon-down:before{content:'\e803';}.icon-download:before{content:'\e804';}.icon-play:before{content:'\e805';}.icon-stop:before{content:'\e806';}.icon-pause:before{content:'\e807';}.icon-to-end:before{content:'\e808';}.icon-ok:before{content:'\e809';}.icon-cancel:before{content:'\e80a';}.icon-to-start:before{content:'\e80b';}.icon-archive:before{content:'\e80c';}.icon-github:before{content:'\e80d';}.icon-fast-forward:before{content:'\e80e';}.icon-fast-backward:before{content:'\e80f';}.icon-ccw:before{content:'\e810';}.icon-gauge:before{content:'\e811';}.icon-menu:before{content:'\e812';}.controls{position:relative;width:320px;text-align:center;}.controls ul,.controls li{list-style:none;margin:0;padding:0;}.controls li{position:relative;display:inline-block;font-size:36px;margin-right:10px;}.controls li:last-child{margin-right:0;}.controls span{display:inline-block;cursor:pointer;}.controls span:hover{color:#FFFFFF;}.controls .reverse{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg);}.rocket{width:32px;height:41px;top:380px;}.rocket-01{background-image:url('../img/rocket-01.svg');left:70px;}.rocket-02{background-image:url('../img/rocket-02.svg');left:230px;}.label{position:absolute;top:440px;}.label-01{left:65px;}.label-02{left:160px;}.description{position:absolute;right:20px;top:40px;width:300px;}@media screen and (max-width: 767px){.all{padding-left:0;padding-right:0;}.description{right:auto;top:500px;left:10px;}} -------------------------------------------------------------------------------- /examples/css/space-invaders.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}html,button,input,select,textarea{font-family:sans-serif;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}h2{font-size:1.5em;margin:0.83em 0;}h3{font-size:1.17em;margin:1em 0;}h4{font-size:1em;margin:1.33em 0;}h5{font-size:0.83em;margin:1.67em 0;}h6{font-size:0.67em;margin:2.33em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}blockquote{margin:1em 40px;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}p,pre{margin:1em 0;}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em;}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word;}q{quotes:none;}q:before,q:after{content:'';content:none;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}dl,menu,ol,ul{margin:1em 0;}dd{margin:0 0 0 40px;}menu,ol,ul{padding:0 0 0 40px;}nav ul,nav ol{list-style:none;list-style-image:none;}img{border:0;-ms-interpolation-mode:bicubic;}svg:not(:root){overflow:hidden;}figure{margin:0;}form{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;white-space:normal;*margin-left:-7px;}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{-moz-box-sizing:border-box;box-sizing:border-box;padding:0;*height:13px;*width:13px;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}html,button,input,select,textarea{color:#222;}body{font-size:1em;line-height:1.4;}::-moz-selection{background:#b3d4fc;text-shadow:none;}::selection{background:#b3d4fc;text-shadow:none;}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0;}img{vertical-align:middle;}fieldset{border:0;margin:0;padding:0;}textarea{resize:vertical;}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0;}.ir{background-color:transparent;border:0;overflow:hidden;*text-indent:-9999px;}.ir:before{content:"";display:block;width:0;height:150%;}.hidden{display:none !important;visibility:hidden;}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto;}.invisible{visibility:hidden;}.clearfix:before,.clearfix:after{content:" ";display:table;}.clearfix:after{clear:both;}.clearfix{*zoom:1;}@media only screen and (min-width: 35em){}@media print,(-o-min-device-pixel-ratio: 5/4),(-webkit-min-device-pixel-ratio: 1.25),(min-resolution: 120dpi){}@media print{@page{margin:0.5cm;}*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important;}a,a:visited{text-decoration:underline;}a[href]:after{content:" (" attr(href) ")";}abbr[title]:after{content:" (" attr(title) ")";}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:"";}pre,blockquote{border:1px solid #999;page-break-inside:avoid;}thead{display:table-header-group;}tr,img{page-break-inside:avoid;}img{max-width:100% !important;}p,h2,h3{orphans:3;widows:3;}h2,h3{page-break-after:avoid;}}*{-moz-box-sizing:border-box;box-sizing:border-box;}*:after,*:before{-moz-box-sizing:border-box;box-sizing:border-box;}html,body,.all{height:100%;margin:0;padding:0;}body{font-family:'Roboto',arial,sans-serif;font-weight:300;font-size:16px;color:#454545;background-color:#62ABBE;line-height:1.9;}p{margin:0 0 0.7em 0;padding:0;text-indent:0;}a{color:#DDDDDD;text-decoration:none;outline:none !important;}a:hover{color:#FFFFFF;text-decoration:none;outline:none !important;}.all{position:relative;width:100%;max-width:960px;height:100%;margin:auto;padding:20px;}.object{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform:translate3d(0, 0, 1px);transform:translate3d(0, 0, 1px);background-size:100% 100%;}.alien{width:60px;height:45px;background-image:url('../img/alien.svg');}.invader{width:180px;height:135px;left:50%;margin-left:-90px;bottom:100%;background-image:url('../img/alien.svg');}.defender{width:50px;height:28px;background-image:url('../img/defender.svg');}.spaceball{width:9px;height:9px;background-image:url('../img/space-ball.svg');}.aliens{left:0px;top:0px;display:block;margin:0;padding:0;list-style:none;}.aliens ul{display:block;margin:0;padding:0;list-style:none;}.aliens .row{display:block;padding:0;height:30px;white-space:nowrap;}.aliens .row:last-child{margin-bottom:0;}.aliens .col{position:relative;display:inline-block;padding:0;width:40px;height:30px;}.aliens .col:last-child{margin-right:0;}.alien{left:0;top:0;width:40px;height:30px;}.defender{left:0px;bottom:60px;z-index:3;width:40px;height:22.4px;}.spaceball{left:17px;bottom:84px;z-index:22;width:6px;height:6px;}.controls{position:absolute;left:0;bottom:0;width:100%;height:58px;z-index:20;background-color:#62ABBE;}.controls>span{display:inline-block;height:34px;border:1px solid #CCCCCC;border-radius:6px;margin:20px 0 0 6px;text-align:center;font-size:18px;line-height:30px;text-transform:uppercase;cursor:pointer;}.controls .left{width:55px;}.controls .right{width:55px;}.controls .space{width:186px;}.win{left:0;top:40%;width:100%;font-size:48px;text-align:center;text-transform:uppercase;font-family:'Roboto Condensed',arial,sans-serif;font-weight:300;line-height:1;display:none;}.lose{left:0;top:50%;width:100%;font-size:48px;text-align:center;text-transform:uppercase;font-family:'Roboto Condensed',arial,sans-serif;font-weight:300;line-height:1;display:none;}.restart{left:0;top:70%;width:100%;font-size:28px;text-align:center;text-transform:uppercase;font-family:'Roboto Condensed',arial,sans-serif;font-weight:300;line-height:1;color:#DDDDDD;cursor:pointer;display:none;}.restart:hover{color:#FFFFFF;}.game{position:relative;width:320px;height:400px;overflow:hidden;background-color:#62ABBE;}.game.auto .restart,.game.auto .controls,.game.auto .start{display:none !important;}.game.auto .defender{bottom:0;}.game.auto .spaceball{bottom:24px;}.driverControls{position:absolute;right:20px;top:20px;width:300px;}.driverControls .intro{font-family:'Roboto Condensed',arial,sans-serif;font-weight:300;font-size:20px;text-transform:uppercase;}.driverControls ul{list-style:circle;}.driverControls li{cursor:pointer;}.driverControls li:hover{color:#FFFFFF;}.driverControls .current{color:#DDDDDD;font-weight:400;}@media screen and (max-width: 767px){.all{padding-left:0;padding-right:0;}.driverControls{right:auto;top:500px;left:10px;}} -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2000-2006, The Perl Foundation. 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | -------------------------------------------------------------------------------- /src/tweene-jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | 15 | /** 16 | * // jQuery plugin that allow to use cubic bezier curves for easing 17 | * Based on Bez http://github.com/rdallasgray/bez 18 | * 19 | * Copyright Robert Dallas Gray. All rights reserved. 20 | * Provided under the FreeBSD license: https://github.com/rdallasgray/bez/blob/master/LICENSE.txt 21 | */ 22 | if(jQuery) 23 | { 24 | jQuery.extend({ tweeneBezier: function(coOrdArray) { 25 | var encodedFuncName = "tweenebez_" + jQuery.makeArray(arguments).join("_").replace(/\./g, "p"); 26 | if (typeof jQuery.easing[encodedFuncName] !== "function") 27 | { 28 | jQuery.easing[encodedFuncName] = function(x, time, b, c, d) 29 | { 30 | return c * bezier(coOrdArray[0], coOrdArray[1], coOrdArray[2], coOrdArray[3])(time / d) + b; 31 | }; 32 | } 33 | return encodedFuncName; 34 | }}); 35 | } 36 | 37 | 38 | /** 39 | * jQuery Tween Driver 40 | * 41 | * @mixes Common, TweenCommon, ControlsPro, TweenPro 42 | * 43 | */ 44 | Tw.registerDriver('jquery', 'tween', function() { 45 | Common.call(this); 46 | TweenCommon.call(this); 47 | ControlsPro.call(this); 48 | TweenPro.call(this); 49 | 50 | this._driverTimeUnit = 'ms'; 51 | 52 | this._emulatedPlayhead = true; 53 | this._emulatedFrom = true; 54 | this._emulatedLoop = true; 55 | this._emulatedDelay = true; 56 | this._emulatedLoop = true; 57 | this._emulatedBegin = false; 58 | this._emulatedProgress = false; 59 | this._allowMultipleEasing = true; 60 | this._allowTransform = false; 61 | 62 | 63 | this._propertyMap = { 64 | x: 'left', 65 | y: 'top' 66 | }; 67 | 68 | 69 | // handle position properties in a custom way 70 | this._getProperty = function(target, style, name) 71 | { 72 | if(style[name] !== void 0) 73 | { 74 | var value = style[name]; 75 | if(inArray(['left', 'top', 'right', 'bottom'], name) !== -1 && (value === '' || value === 'auto')) 76 | { 77 | value = target.position()[name]; 78 | } 79 | 80 | return [name, value]; 81 | } 82 | 83 | return getProperty(style, name); 84 | }; 85 | 86 | 87 | /** 88 | * Fetch current values before starting 89 | * 90 | * @param {object} target 91 | * @param {object} tween 92 | */ 93 | this._getCurrentValues = function(target, tween) 94 | { 95 | var item = target.get(0), name, value, property, prop, style = window.getComputedStyle(item); 96 | 97 | for(name in tween) 98 | { 99 | property = this._getProperty(target, style, name); 100 | if(property[0] != name) 101 | { 102 | tween[property[0]] = tween[name]; 103 | delete tween[name]; 104 | name = property[0]; 105 | } 106 | value = property[1]; 107 | 108 | if(value !== void 0) 109 | { 110 | prop = tween[property[0]]; 111 | prop.pre = value; 112 | if(!this._hasEnd) 113 | { 114 | prop.end = value; 115 | if(this._hasThen && prop.then === null) 116 | { 117 | prop.then = value; 118 | } 119 | } 120 | this._hasPre = true; 121 | } 122 | } 123 | }; 124 | 125 | 126 | /** 127 | * return a callback used as jquery step event handler. It saves style values during _setTween() 128 | * 129 | * @param {object} tween 130 | * @param {boolean} fetchBegin 131 | * @param {boolean} fetchEnd 132 | * @returns {function} 133 | */ 134 | this._setFetch = function(tween, fetchBegin, fetchEnd) 135 | { 136 | var self = this; 137 | return function(now, fx) { 138 | if(fx.prop in tween) 139 | { 140 | var prop = tween[fx.prop]; 141 | var unit = isNumber(fx.end)? fx.unit : ''; 142 | if(fetchBegin) 143 | { 144 | prop.begin = fx.end + unit; 145 | if(fetchEnd) 146 | { 147 | prop.end = prop.pre = fx.start + unit; 148 | if(self._hasThen && prop.then === null) 149 | { 150 | prop.then = prop.end; 151 | } 152 | self._endReady = true; 153 | } 154 | else 155 | { 156 | prop.pre = fx.start + unit; 157 | if(prop.end === null) 158 | { 159 | prop.end = fx.end + unit; 160 | } 161 | } 162 | self._hasPre = true; 163 | } 164 | else 165 | { 166 | prop.then = fx.end + unit; 167 | if(prop.end === null) 168 | { 169 | prop.end = fx.start + unit; 170 | } 171 | if(prop.begin === null) 172 | { 173 | prop.begin = prop.end; 174 | } 175 | if(self._hasPre && prop.pre === null) 176 | { 177 | prop.pre = self._hasEnd? prop.begin : prop.end; 178 | } 179 | } 180 | } 181 | }; 182 | 183 | }; 184 | 185 | 186 | /** 187 | * return a callback used as jquery step event handler. It saves style values during _playTween() 188 | * 189 | * @param {object} tween 190 | * @returns {function} 191 | */ 192 | this._playFetch = function(tween) 193 | { 194 | var self = this; 195 | return function(now, fx) 196 | { 197 | if(fx.prop in tween) 198 | { 199 | var prop = tween[fx.prop]; 200 | var unit = isNumber(fx.end)? fx.unit : ''; 201 | prop.end = fx.end + unit; 202 | 203 | if(self._hasThen && prop.then === null) 204 | { 205 | prop.then = prop.end; 206 | } 207 | if(!self._beginReady || prop.begin === null) 208 | { 209 | prop.begin = fx.start + unit; 210 | } 211 | if(self._duration) 212 | { 213 | fx.end = fx.start; 214 | fx.now = fx.start; 215 | } 216 | 217 | } 218 | }; 219 | }; 220 | 221 | 222 | /** 223 | * Set css values instantly 224 | * 225 | * @param {string} field - 'begin' | 'end' | 'pre' | 'then' 226 | */ 227 | this._setTween = function(field) 228 | { 229 | var tween, target, i, end, values, 230 | fetchBegin = (field == 'begin' && this._hasBegin && !this._beginReady), 231 | fetchThen = !fetchBegin && (field == 'then' && this._hasThen && !this._thenReady), 232 | fetchEnd = fetchBegin && !this._hasEnd, 233 | fetch = fetchBegin || fetchThen; 234 | 235 | for(i = 0, end = this._target.length; i < end; i++) 236 | { 237 | tween = this._data.tween[i]; 238 | target = this._target.eq(i); 239 | 240 | if(fetchBegin) 241 | { 242 | this._getCurrentValues(target, tween); 243 | } 244 | 245 | values = this._getTweenValues(tween, field, true); 246 | if(fetch) 247 | { 248 | target.animate(values, { 249 | duration: 0, 250 | step: this._setFetch(tween, fetchBegin, fetchEnd) 251 | }); 252 | } 253 | else 254 | { 255 | target.css(values); 256 | } 257 | } 258 | 259 | if(field == 'begin') 260 | { 261 | this._beginReady = true; 262 | } 263 | else if(field == 'then') 264 | { 265 | this._thenReady = true; 266 | } 267 | 268 | return this; 269 | }; 270 | 271 | 272 | /** 273 | * Execute the effective tween 274 | * 275 | */ 276 | this._playTween = function() 277 | { 278 | var data = this._data, 279 | field = this._localFwd? 'end' : 'begin', 280 | self = this, tween, target, values, prePlay = false, options, 281 | onStart = function() { self._runHandlers('_begin'); }, 282 | onComplete = function() { self._runHandlers('_end'); }, 283 | onProgress = function() { self._runHandlers('progress'); }; 284 | 285 | 286 | if(!this._endReady) 287 | { 288 | prePlay = true; 289 | for(i = 0, end = this._target.length; i < end; i++) 290 | { 291 | tween = this._data.tween[i]; 292 | target = this._target.eq(i); 293 | values = this._getTweenValues(tween, field, true); 294 | options = { 295 | duration: 0, 296 | step: this._playFetch(tween) 297 | }; 298 | 299 | if(i == (end - 1) && !this._duration) 300 | { 301 | options.start = onStart; 302 | options.complete = onComplete; 303 | } 304 | 305 | target.animate(values, options); 306 | } 307 | this._beginReady = true; 308 | this._endReady = true; 309 | } 310 | 311 | 312 | if(this._duration || !prePlay) 313 | { 314 | for(var i = 0, end = this._target.length; i < end; i++) 315 | { 316 | values = this._getTweenValues(data.tween[i], field, false); 317 | options = { 318 | duration: data.realDuration, 319 | queue: 'queue_' + this._id 320 | }; 321 | if(data.duration) 322 | { 323 | options.easing = this._getRealEasing(data.easing); 324 | } 325 | 326 | if(i == end - 1) 327 | { 328 | options.start = onStart; 329 | options.complete = onComplete; 330 | if(this._hasHandlers('progress')) 331 | { 332 | options.progress = onProgress; 333 | } 334 | } 335 | this._target.eq(i).animate(values, options); 336 | } 337 | this._target.dequeue('queue_' + this._id); 338 | } 339 | }; 340 | 341 | 342 | 343 | /** 344 | * Pause a running tween 345 | * 346 | */ 347 | this._pauseTween = function() 348 | { 349 | this._target.stop('queue_' + this._id, true); 350 | }; 351 | 352 | 353 | 354 | this._resumeTween = function() 355 | { 356 | return this._playTween(); 357 | }; 358 | 359 | 360 | 361 | this._getBezierEasing = function(value) 362 | { 363 | return $.tweeneBezier(value); 364 | }; 365 | 366 | }); 367 | 368 | 369 | 370 | /** 371 | * jQuery Timeline Driver 372 | * 373 | * @mixes Common, TimelineCommon, ControlsPro, TimelinePro 374 | * 375 | */ 376 | Tw.registerDriver('jquery', 'timeline', function() { 377 | Common.call(this); 378 | TimelineCommon.call(this); 379 | ControlsPro.call(this); 380 | TimelinePro.call(this); 381 | 382 | this._driverTimeUnit = 'ms'; 383 | 384 | }); 385 | 386 | Tw.defaultTimeUnit = 'ms'; 387 | Tw.defaultDriver = 'jquery'; 388 | -------------------------------------------------------------------------------- /examples/scss/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | *display: inline; 35 | *zoom: 1; 36 | } 37 | 38 | /** 39 | * Prevent modern browsers from displaying `audio` without controls. 40 | * Remove excess height in iOS 5 devices. 41 | */ 42 | 43 | audio:not([controls]) { 44 | display: none; 45 | height: 0; 46 | } 47 | 48 | /** 49 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 50 | * Known issue: no IE 6 support. 51 | */ 52 | 53 | [hidden] { 54 | display: none; 55 | } 56 | 57 | /* ========================================================================== 58 | Base 59 | ========================================================================== */ 60 | 61 | /** 62 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 63 | * `em` units. 64 | * 2. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | font-size: 100%; /* 1 */ 70 | -webkit-text-size-adjust: 100%; /* 2 */ 71 | -ms-text-size-adjust: 100%; /* 2 */ 72 | } 73 | 74 | /** 75 | * Address `font-family` inconsistency between `textarea` and other form 76 | * elements. 77 | */ 78 | 79 | html, 80 | button, 81 | input, 82 | select, 83 | textarea { 84 | font-family: sans-serif; 85 | } 86 | 87 | /** 88 | * Address margins handled incorrectly in IE 6/7. 89 | */ 90 | 91 | body { 92 | margin: 0; 93 | } 94 | 95 | /* ========================================================================== 96 | Links 97 | ========================================================================== */ 98 | 99 | /** 100 | * Address `outline` inconsistency between Chrome and other browsers. 101 | */ 102 | 103 | a:focus { 104 | outline: thin dotted; 105 | } 106 | 107 | /** 108 | * Improve readability when focused and also mouse hovered in all browsers. 109 | */ 110 | 111 | a:active, 112 | a:hover { 113 | outline: 0; 114 | } 115 | 116 | /* ========================================================================== 117 | Typography 118 | ========================================================================== */ 119 | 120 | /** 121 | * Address font sizes and margins set differently in IE 6/7. 122 | * Address font sizes within `section` and `article` in Firefox 4+, Safari 5, 123 | * and Chrome. 124 | */ 125 | 126 | h1 { 127 | font-size: 2em; 128 | margin: 0.67em 0; 129 | } 130 | 131 | h2 { 132 | font-size: 1.5em; 133 | margin: 0.83em 0; 134 | } 135 | 136 | h3 { 137 | font-size: 1.17em; 138 | margin: 1em 0; 139 | } 140 | 141 | h4 { 142 | font-size: 1em; 143 | margin: 1.33em 0; 144 | } 145 | 146 | h5 { 147 | font-size: 0.83em; 148 | margin: 1.67em 0; 149 | } 150 | 151 | h6 { 152 | font-size: 0.67em; 153 | margin: 2.33em 0; 154 | } 155 | 156 | /** 157 | * Address styling not present in IE 7/8/9, Safari 5, and Chrome. 158 | */ 159 | 160 | abbr[title] { 161 | border-bottom: 1px dotted; 162 | } 163 | 164 | /** 165 | * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. 166 | */ 167 | 168 | b, 169 | strong { 170 | font-weight: bold; 171 | } 172 | 173 | blockquote { 174 | margin: 1em 40px; 175 | } 176 | 177 | /** 178 | * Address styling not present in Safari 5 and Chrome. 179 | */ 180 | 181 | dfn { 182 | font-style: italic; 183 | } 184 | 185 | /** 186 | * Address differences between Firefox and other browsers. 187 | * Known issue: no IE 6/7 normalization. 188 | */ 189 | 190 | hr { 191 | -moz-box-sizing: content-box; 192 | box-sizing: content-box; 193 | height: 0; 194 | } 195 | 196 | /** 197 | * Address styling not present in IE 6/7/8/9. 198 | */ 199 | 200 | mark { 201 | background: #ff0; 202 | color: #000; 203 | } 204 | 205 | /** 206 | * Address margins set differently in IE 6/7. 207 | */ 208 | 209 | p, 210 | pre { 211 | margin: 1em 0; 212 | } 213 | 214 | /** 215 | * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. 216 | */ 217 | 218 | code, 219 | kbd, 220 | pre, 221 | samp { 222 | font-family: monospace, serif; 223 | _font-family: 'courier new', monospace; 224 | font-size: 1em; 225 | } 226 | 227 | /** 228 | * Improve readability of pre-formatted text in all browsers. 229 | */ 230 | 231 | pre { 232 | white-space: pre; 233 | white-space: pre-wrap; 234 | word-wrap: break-word; 235 | } 236 | 237 | /** 238 | * Address CSS quotes not supported in IE 6/7. 239 | */ 240 | 241 | q { 242 | quotes: none; 243 | } 244 | 245 | /** 246 | * Address `quotes` property not supported in Safari 4. 247 | */ 248 | 249 | q:before, 250 | q:after { 251 | content: ''; 252 | content: none; 253 | } 254 | 255 | /** 256 | * Address inconsistent and variable font size in all browsers. 257 | */ 258 | 259 | small { 260 | font-size: 80%; 261 | } 262 | 263 | /** 264 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 265 | */ 266 | 267 | sub, 268 | sup { 269 | font-size: 75%; 270 | line-height: 0; 271 | position: relative; 272 | vertical-align: baseline; 273 | } 274 | 275 | sup { 276 | top: -0.5em; 277 | } 278 | 279 | sub { 280 | bottom: -0.25em; 281 | } 282 | 283 | /* ========================================================================== 284 | Lists 285 | ========================================================================== */ 286 | 287 | /** 288 | * Address margins set differently in IE 6/7. 289 | */ 290 | 291 | dl, 292 | menu, 293 | ol, 294 | ul { 295 | margin: 1em 0; 296 | } 297 | 298 | dd { 299 | margin: 0 0 0 40px; 300 | } 301 | 302 | /** 303 | * Address paddings set differently in IE 6/7. 304 | */ 305 | 306 | menu, 307 | ol, 308 | ul { 309 | padding: 0 0 0 40px; 310 | } 311 | 312 | /** 313 | * Correct list images handled incorrectly in IE 7. 314 | */ 315 | 316 | nav ul, 317 | nav ol { 318 | list-style: none; 319 | list-style-image: none; 320 | } 321 | 322 | /* ========================================================================== 323 | Embedded content 324 | ========================================================================== */ 325 | 326 | /** 327 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 328 | * 2. Improve image quality when scaled in IE 7. 329 | */ 330 | 331 | img { 332 | border: 0; /* 1 */ 333 | -ms-interpolation-mode: bicubic; /* 2 */ 334 | } 335 | 336 | /** 337 | * Correct overflow displayed oddly in IE 9. 338 | */ 339 | 340 | svg:not(:root) { 341 | overflow: hidden; 342 | } 343 | 344 | /* ========================================================================== 345 | Figures 346 | ========================================================================== */ 347 | 348 | /** 349 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 350 | */ 351 | 352 | figure { 353 | margin: 0; 354 | } 355 | 356 | /* ========================================================================== 357 | Forms 358 | ========================================================================== */ 359 | 360 | /** 361 | * Correct margin displayed oddly in IE 6/7. 362 | */ 363 | 364 | form { 365 | margin: 0; 366 | } 367 | 368 | /** 369 | * Define consistent border, margin, and padding. 370 | */ 371 | 372 | fieldset { 373 | border: 1px solid #c0c0c0; 374 | margin: 0 2px; 375 | padding: 0.35em 0.625em 0.75em; 376 | } 377 | 378 | /** 379 | * 1. Correct color not being inherited in IE 6/7/8/9. 380 | * 2. Correct text not wrapping in Firefox 3. 381 | * 3. Correct alignment displayed oddly in IE 6/7. 382 | */ 383 | 384 | legend { 385 | border: 0; /* 1 */ 386 | padding: 0; 387 | white-space: normal; /* 2 */ 388 | *margin-left: -7px; /* 3 */ 389 | } 390 | 391 | /** 392 | * 1. Correct font size not being inherited in all browsers. 393 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 394 | * and Chrome. 395 | * 3. Improve appearance and consistency in all browsers. 396 | */ 397 | 398 | button, 399 | input, 400 | select, 401 | textarea { 402 | font-size: 100%; /* 1 */ 403 | margin: 0; /* 2 */ 404 | vertical-align: baseline; /* 3 */ 405 | *vertical-align: middle; /* 3 */ 406 | } 407 | 408 | /** 409 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 410 | * the UA stylesheet. 411 | */ 412 | 413 | button, 414 | input { 415 | line-height: normal; 416 | } 417 | 418 | /** 419 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 420 | * All other form control elements do not inherit `text-transform` values. 421 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 422 | * Correct `select` style inheritance in Firefox 4+ and Opera. 423 | */ 424 | 425 | button, 426 | select { 427 | text-transform: none; 428 | } 429 | 430 | /** 431 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 432 | * and `video` controls. 433 | * 2. Correct inability to style clickable `input` types in iOS. 434 | * 3. Improve usability and consistency of cursor style between image-type 435 | * `input` and others. 436 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 437 | * Known issue: inner spacing remains in IE 6. 438 | */ 439 | 440 | button, 441 | html input[type="button"], /* 1 */ 442 | input[type="reset"], 443 | input[type="submit"] { 444 | -webkit-appearance: button; /* 2 */ 445 | cursor: pointer; /* 3 */ 446 | *overflow: visible; /* 4 */ 447 | } 448 | 449 | /** 450 | * Re-set default cursor for disabled elements. 451 | */ 452 | 453 | button[disabled], 454 | html input[disabled] { 455 | cursor: default; 456 | } 457 | 458 | /** 459 | * 1. Address box sizing set to content-box in IE 8/9. 460 | * 2. Remove excess padding in IE 8/9. 461 | * 3. Remove excess padding in IE 7. 462 | * Known issue: excess padding remains in IE 6. 463 | */ 464 | 465 | input[type="checkbox"], 466 | input[type="radio"] { 467 | box-sizing: border-box; /* 1 */ 468 | padding: 0; /* 2 */ 469 | *height: 13px; /* 3 */ 470 | *width: 13px; /* 3 */ 471 | } 472 | 473 | /** 474 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 475 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 476 | * (include `-moz` to future-proof). 477 | */ 478 | 479 | input[type="search"] { 480 | -webkit-appearance: textfield; /* 1 */ 481 | -moz-box-sizing: content-box; 482 | -webkit-box-sizing: content-box; /* 2 */ 483 | box-sizing: content-box; 484 | } 485 | 486 | /** 487 | * Remove inner padding and search cancel button in Safari 5 and Chrome 488 | * on OS X. 489 | */ 490 | 491 | input[type="search"]::-webkit-search-cancel-button, 492 | input[type="search"]::-webkit-search-decoration { 493 | -webkit-appearance: none; 494 | } 495 | 496 | /** 497 | * Remove inner padding and border in Firefox 3+. 498 | */ 499 | 500 | button::-moz-focus-inner, 501 | input::-moz-focus-inner { 502 | border: 0; 503 | padding: 0; 504 | } 505 | 506 | /** 507 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 508 | * 2. Improve readability and alignment in all browsers. 509 | */ 510 | 511 | textarea { 512 | overflow: auto; /* 1 */ 513 | vertical-align: top; /* 2 */ 514 | } 515 | 516 | /* ========================================================================== 517 | Tables 518 | ========================================================================== */ 519 | 520 | /** 521 | * Remove most spacing between table cells. 522 | */ 523 | 524 | table { 525 | border-collapse: collapse; 526 | border-spacing: 0; 527 | } 528 | -------------------------------------------------------------------------------- /src/tweene-velocity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | 15 | // needed for scheduling sequences of Tweene.set() 16 | var velocitySetPendings = []; 17 | 18 | 19 | /** 20 | * Velocity Tween Driver 21 | * 22 | * @link http://julian.com/research/velocity/ 23 | * @mixes Common, TweenCommon, ControlsPro, TweenPro 24 | * 25 | */ 26 | Tw.registerDriver('velocity', 'tween', function() { 27 | Common.call(this); 28 | TweenCommon.call(this); 29 | ControlsPro.call(this); 30 | TweenPro.call(this); 31 | 32 | this._driverTimeUnit = 'ms'; 33 | 34 | this._emulatedPlayhead = true; 35 | this._emulatedFrom = true; 36 | this._emulatedLoop = true; 37 | this._emulatedDelay = true; 38 | this._emulatedBegin = false; 39 | this._emulatedProgress = false; 40 | this._allowMultipleEasing = true; 41 | this._allowTransform = true; 42 | 43 | 44 | this._propertyMap = { 45 | x: 'translateX', 46 | y: 'translateY', 47 | z: 'translateZ', 48 | rotation: 'rotateZ', 49 | rotate: 'rotateZ', 50 | rotationX: 'rotateX', 51 | rotationY: 'rotateY', 52 | rotationZ: 'rotateZ' 53 | }; 54 | 55 | var _css = $.fn.velocity.CSS; 56 | this._tweenReady = true; 57 | this._pendings = []; 58 | this._setPending = false; 59 | 60 | 61 | /** 62 | * Velocity executes also instant tweens async, so we need to handle an internal schedule 63 | * 64 | * @param {function} callback 65 | * @param {array} [params] 66 | */ 67 | this._addPendingCall = function(callback, params) 68 | { 69 | this._pendings.push([callback, params || []]); 70 | }; 71 | 72 | 73 | /** 74 | * Run pending callbacks 75 | * 76 | */ 77 | this._processPendings = function() 78 | { 79 | for(var i = 0, end = this._pendings.length; i < end; i++) 80 | { 81 | this._pendings[i][0].apply(this, this._pendings[i][1]); 82 | } 83 | // pending processed could push other calls, so we cannot just empty the whole array 84 | this._pendings.splice(0, end); 85 | }; 86 | 87 | 88 | /** 89 | * Fetch actual style values from Velocity calls queue, then pass them to a callback 90 | * 91 | * @param {object} tween 92 | * @param {function} callback 93 | */ 94 | this._getVelocityValues = function(tween, callback) 95 | { 96 | var calls = $.fn.velocity.State.calls; 97 | var name, root, beginValue, endValue, entry = calls[calls.length - 1][0][0]; 98 | for(name in entry) 99 | { 100 | if(isObject(entry[name]) && 'startValue' in entry[name]) 101 | { 102 | // Velocity splits some properties in sub-properties 103 | root = _css.Hooks.getRoot(name); 104 | if(!(name in tween)) 105 | { 106 | tween[name] = {begin: null, end: null, then: null, easing: root in tween? tween[root].easing : null}; 107 | } 108 | beginValue = entry[name].startValue + entry[name].unitType; 109 | endValue = entry[name].endValue + entry[name].unitType; 110 | callback.call(this, tween, name, root, beginValue, endValue); 111 | } 112 | } 113 | }; 114 | 115 | 116 | /** 117 | * Velocity implements bezier internally, we can send directly the array param 118 | * 119 | * @param {array} value 120 | * @returns {array} 121 | */ 122 | this._getBezierEasing = function(value) 123 | { 124 | return value; 125 | }; 126 | 127 | 128 | /** 129 | * Save pre-start values in tween structure 130 | * 131 | * @param {object} tween 132 | * @param {string} name 133 | * @param {string} root 134 | * @param {string|number} begin 135 | * @param {string|number} end 136 | */ 137 | this._fetchBegin = function(tween, name, root, begin, end) 138 | { 139 | tween[name].begin = end; 140 | if(this._hasBegin && !this._hasEnd) 141 | { 142 | tween[name].end = tween[name].pre = begin; 143 | if(this._hasThen && tween[name].then === null) 144 | { 145 | tween[name].then = begin; 146 | } 147 | if(root != name) 148 | { 149 | tween[root].begin = tween[root].end = tween[root].then = null; 150 | } 151 | this._endReady = true; 152 | } 153 | else 154 | { 155 | tween[name].pre = begin; 156 | if(tween[name].end === null) 157 | { 158 | tween[name].end = end; 159 | } 160 | if(root != name) 161 | { 162 | tween[root].begin = null; 163 | } 164 | } 165 | 166 | }; 167 | 168 | 169 | /** 170 | * Save then values in tween structure 171 | * 172 | * @param {object} tween 173 | * @param {string} name 174 | * @param {string} root 175 | * @param {string|number} begin 176 | * @param {string|number} end 177 | */ 178 | this._fetchThen = function(tween, name, root, begin, end) 179 | { 180 | tween[name].then = end; 181 | if(tween[name].end === null) 182 | { 183 | tween[name].end = begin; 184 | } 185 | if(tween[name].begin === null) 186 | { 187 | tween[name].begin = tween[name].end; 188 | } 189 | if(this._hasPre && tween[name].pre === null) 190 | { 191 | tween[name].pre = this._hasEnd? tween[name].begin : tween[name].end; 192 | } 193 | if(root != name) 194 | { 195 | tween[root].begin = tween[root].end = tween[root].then = tween[root].pre = null; 196 | } 197 | }; 198 | 199 | 200 | /** 201 | * Save post-tween values in tween structure 202 | * 203 | * @param {object} tween 204 | * @param {string} name 205 | * @param {string} root 206 | * @param {string|number} begin 207 | * @param {string|number} end 208 | */ 209 | this._fetchEnd = function(tween, name, root, begin, end) 210 | { 211 | tween[name].begin = begin; 212 | tween[name].end = end; 213 | if(this._hasPre && tween[name].pre === null) 214 | { 215 | tween[name].pre = begin; 216 | } 217 | if(this._hasThen && tween[name].then === null) 218 | { 219 | tween[name].then = end; 220 | } 221 | if(root != name) 222 | { 223 | tween[root].begin = tween[root].end = tween[root].pre = tween[root].then = null; 224 | } 225 | }; 226 | 227 | 228 | /** 229 | * Set css values instantly 230 | * 231 | * @param {string} field - 'begin' | 'end' | 'pre' | 'then' 232 | */ 233 | this._setTween = function(field) 234 | { 235 | if(!this._tweenReady) 236 | { 237 | this._addPendingCall(this._setTween, [field]); 238 | return; 239 | } 240 | 241 | var options, self = this, i, end, tween, values, 242 | onComplete = function() { 243 | self._tweenReady = true; 244 | self._processPendings(); 245 | }; 246 | 247 | this._tweenReady = false; 248 | for(i = 0, end = this._target.length; i < end; i++) 249 | { 250 | tween = this._data.tween[i]; 251 | values = this._getTweenValues(this._data.tween[i], field, true); 252 | options = {duration: 0, queue: false}; 253 | if(i == end - 1) 254 | { 255 | options.complete = onComplete; 256 | } 257 | this._target.eq(i).velocity(values, options); 258 | if(field == 'begin' && this._hasBegin && !this._beginReady) 259 | { 260 | this._getVelocityValues(tween, this._fetchBegin); 261 | this._hasPre = true; 262 | } 263 | else if(field == 'then' && this._hasThen && !this._thenReady) 264 | { 265 | this._getVelocityValues(tween, this._fetchThen); 266 | } 267 | } 268 | 269 | if(field == 'begin') 270 | { 271 | this._beginReady = true; 272 | } 273 | else if(field == 'then') 274 | { 275 | this._thenReady = true; 276 | } 277 | }; 278 | 279 | 280 | /** 281 | * Execute the effective tween 282 | * 283 | */ 284 | this._playTween = function() 285 | { 286 | if(!this._tweenReady) 287 | { 288 | this._addPendingCall(this._playTween); 289 | return; 290 | } 291 | // in Velocity also tweens with duration = 0 are async, so we need to handle a queue in order to allow multiple Tweene.set() to run in the right order 292 | if(!this._duration) 293 | { 294 | if(!this._setPending) 295 | { 296 | this._setPending = true; 297 | velocitySetPendings.push(this); 298 | 299 | this.setCoreHandler('_end', 'setEnd', function() { 300 | velocitySetPendings.shift(); 301 | if(velocitySetPendings.length > 0) 302 | { 303 | velocitySetPendings[0]._playTween(); 304 | } 305 | }, this); 306 | 307 | if(velocitySetPendings.length > 1) 308 | { 309 | return; 310 | } 311 | } 312 | } 313 | 314 | 315 | var self = this, 316 | data = this._data, 317 | field = this._localFwd? 'end' : 'begin', 318 | i, end, tween, values, options, target, 319 | onBegin = function() { self._runHandlers('_begin'); }, 320 | onComplete = function() { self._runHandlers('_end'); }, 321 | onProgress = function() { self._runHandlers('progress'); }; 322 | for(i = 0, end = this._target.length; i < end; i++) 323 | { 324 | tween = data.tween[i]; 325 | target = this._target.eq(i); 326 | values = this._getTweenValues(tween, field, (data.duration !== 0)); 327 | options = { 328 | duration: data.realDuration, 329 | queue: 'tweene_' + this._id 330 | }; 331 | 332 | if(data.duration) 333 | { 334 | options.easing = this._getRealEasing(data.easing); 335 | } 336 | 337 | if(i === end - 1) 338 | { 339 | options.begin = onBegin; 340 | options.complete = onComplete; 341 | 342 | if(this._hasHandlers('progress')) 343 | { 344 | options.progress = onProgress; 345 | } 346 | } 347 | target.velocity(values, options).dequeue('tweene_' + this._id); 348 | if(!this._endReady) 349 | { 350 | this._getVelocityValues(tween, this._fetchEnd); 351 | } 352 | } 353 | this._endReady = true; 354 | return this; 355 | }; 356 | 357 | 358 | 359 | /** 360 | * Pause a running tween 361 | * 362 | */ 363 | this._pauseTween = function() 364 | { 365 | // console.log('pausing velocity tween'); 366 | this._target.velocity('stop', 'tweene_' + this._id); 367 | // this._pendings = []; 368 | return this; 369 | }; 370 | 371 | 372 | 373 | this._resumeTween = function() 374 | { 375 | // console.log('resuming velocity tween'); 376 | return this._playTween(); 377 | }; 378 | 379 | 380 | // need to handle also this with queue 381 | this._oldStaticProps = this._setStaticProps; 382 | 383 | this._setStaticProps = function(first, second) 384 | { 385 | if(!this._tweenReady) 386 | { 387 | this._addPendingCall(this._setStaticProps, [first, second]); 388 | return; 389 | } 390 | 391 | this._oldStaticProps(first, second); 392 | }; 393 | 394 | 395 | }); 396 | 397 | 398 | /** 399 | * Velocity Timeline Driver 400 | * 401 | * @mixes Common, TimelineCommon, ControlsPro, TimelinePro 402 | * 403 | */ 404 | Tw.registerDriver('velocity', 'timeline', function() { 405 | Common.call(this); 406 | TimelineCommon.call(this); 407 | ControlsPro.call(this); 408 | TimelinePro.call(this); 409 | 410 | this._driverTimeUnit = 'ms'; 411 | }); 412 | 413 | Tw.defaultTimeUnit = 'ms'; 414 | Tw.defaultDriver = 'velocity'; 415 | -------------------------------------------------------------------------------- /src/tweene-gsap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | /** 15 | * Vars and methods common to Gsap tween and timeline drivers 16 | * @mixin 17 | */ 18 | var GsapCommon = function() 19 | { 20 | 21 | this._driverTimeUnit = 's'; 22 | 23 | this._native = null; 24 | 25 | this._eventsTarget = null; 26 | 27 | this._eventsSet = false; 28 | 29 | 30 | /** 31 | * Return the native TimelineMax object used internally by tween and timeline 32 | * 33 | * @returns {object} - TimelineMax object 34 | */ 35 | this.getNative = function() 36 | { 37 | this.prepare(); 38 | return this._native; 39 | }; 40 | 41 | 42 | /** 43 | * Get current time position, needed only by info methods like time() and progress() 44 | * 45 | * @returns {number} 46 | */ 47 | this._getPosition = function() 48 | { 49 | if(!this._ready) 50 | { 51 | return 0; 52 | } 53 | return convertTime(this._native.time(), this._driverTimeUnit, this._coreTimeUnit); 54 | }; 55 | 56 | 57 | /** 58 | * Get the current percent progress, as a value between 0 and 1 59 | * 60 | * @returns {number} 61 | */ 62 | this._getProgress = function() 63 | { 64 | if(!this._ready) 65 | { 66 | return 0; 67 | } 68 | return this._native.progress(); 69 | }; 70 | 71 | 72 | /** 73 | * Get the current running status 74 | * 75 | * @returns {bolean} 76 | */ 77 | this._getPaused = function() 78 | { 79 | return (!this._ready || this._native.paused()); 80 | }; 81 | 82 | 83 | /** 84 | * Map Tweene event handlers to native Gsap events 85 | * 86 | */ 87 | this._setupEvents = function() 88 | { 89 | if(this._eventsSet) 90 | { 91 | return; 92 | } 93 | var self = this, target, name; 94 | this.setCoreHandler('begin', '_running', function() { self._running = true; }); 95 | 96 | this._eventsSet = true; 97 | 98 | var eventMaps = { 99 | begin: 'onStart', 100 | end: 'onComplete', 101 | loop: 'onRepeat', 102 | reverse: 'onReverseComplete', 103 | progress: 'onUpdate' 104 | }; 105 | 106 | for(name in eventMaps) 107 | { 108 | if(this._hasHandlers(name)) 109 | { 110 | target = name == 'end'? this._native : this._eventsTarget; 111 | target.eventCallback(eventMaps[name], this._runHandlers, [name], this); 112 | } 113 | } 114 | }; 115 | 116 | 117 | /** 118 | * Propagate a call to the internal native object 119 | * 120 | * @param {string} name - method name 121 | */ 122 | this._callNative = function(name) 123 | { 124 | this.prepare(); 125 | this._setupEvents(); 126 | this._native[name](); 127 | }; 128 | 129 | 130 | /** 131 | * Propagate play() to native object 132 | * 133 | */ 134 | this._playTween = function() 135 | { 136 | this._callNative('play'); 137 | }; 138 | 139 | 140 | /** 141 | * Propagate pause() to native object 142 | * 143 | */ 144 | this._pauseTween = function() 145 | { 146 | this._callNative('pause'); 147 | }; 148 | 149 | 150 | /** 151 | * Propagate resume() to native object 152 | * 153 | */ 154 | this._resumeTween = function() 155 | { 156 | this._callNative('resume'); 157 | }; 158 | 159 | 160 | /** 161 | * Propagate reverse() to native object 162 | * 163 | */ 164 | this._reverseTween = function() 165 | { 166 | this._callNative('reverse'); 167 | }; 168 | 169 | 170 | /** 171 | * Propagate restart() to native object 172 | * 173 | */ 174 | this._restartTween = function() 175 | { 176 | this._callNative('restart'); 177 | }; 178 | 179 | 180 | /** 181 | * Map speed() calls to native timeScale() method 182 | * 183 | */ 184 | this._speedTween = function() 185 | { 186 | if(this._native && !this._parent) 187 | { 188 | this._native.timeScale(this._speed); 189 | } 190 | }; 191 | 192 | }; 193 | 194 | 195 | /** 196 | * Gsap Tween Driver 197 | * @link http://greensock.com/gsap 198 | * 199 | * @mixes Common, TweenCommon, GsapCommon 200 | * 201 | */ 202 | Tw.registerDriver('Gsap', 'tween', function(){ 203 | Common.call(this); 204 | TweenCommon.call(this); 205 | GsapCommon.call(this); 206 | 207 | this._allowMultipleEasing = true; 208 | this._allowTransform = true; 209 | 210 | this._propertyMap = { 211 | translateX: 'x', 212 | translateY: 'y', 213 | translateZ: 'z', 214 | rotate: 'rotation', 215 | rotateX: 'rotationX', 216 | rotateY: 'rotationY', 217 | rotateZ: 'rotationZ' 218 | }; 219 | 220 | 221 | 222 | /** 223 | * Override TweenCommon._reset() 224 | * 225 | */ 226 | this._reset = function() 227 | { 228 | if(this._native) 229 | { 230 | this._native.clear(); 231 | this._native = null; 232 | } 233 | this._eventsTarget = null; 234 | this._eventsSet = false; 235 | }; 236 | 237 | 238 | /** 239 | * Get a Gsap generic Ease object constructed with a cubic bezier easing function 240 | * 241 | * @param {array} value 242 | * @returns {object} 243 | */ 244 | this._getBezierEasing = function(value) 245 | { 246 | return new Ease(bezier.apply(null, value)); 247 | }; 248 | 249 | 250 | /** 251 | * Override TweenCommon.prepare() 252 | * 253 | * @returns {number} 254 | */ 255 | this.prepare = function() 256 | { 257 | this._prepare(); 258 | 259 | if(this._native !== null) 260 | { 261 | return this; 262 | } 263 | 264 | var 265 | data = this._data, 266 | from, to, 267 | then = {}, 268 | i, end, tween, name, elem, fromCount, toCount, thenCount = 0; 269 | 270 | this._native = new TimelineMax({ 271 | delay: data.delay 272 | }) 273 | .pause() 274 | .timeScale(this._speed); 275 | 276 | // with Gsap we do per-property easing with overlapping tweens of the same targets 277 | data.tween = this._hasMultipleEasing? this._splitEasing(data.tween) : [{tween: data.tween, easing: data.easing}]; 278 | 279 | for(i = 0, end = data.tween.length; i < end; i++) 280 | { 281 | tween = data.tween[i].tween; 282 | 283 | fromCount = 0; 284 | toCount = 0; 285 | from = {}; 286 | to = {}; 287 | 288 | for(name in tween) 289 | { 290 | if(tween[name].begin !== null) 291 | { 292 | fromCount ++; 293 | from[name] = tween[name].begin; 294 | } 295 | 296 | if(tween[name].end !== null) 297 | { 298 | toCount ++; 299 | to[name] = tween[name].end; 300 | } 301 | 302 | if(tween[name].then !== null) 303 | { 304 | thenCount ++; 305 | then[name] = tween[name].then; 306 | } 307 | } 308 | 309 | var values = (this._hasEnd)? to : from; 310 | if(data.duration) 311 | { 312 | values.ease = this._getRealEasing(data.tween[i].easing); 313 | } 314 | 315 | // in order to achieve almost the same behavior in all the drivers, it runs always with immediateRender = false 316 | values.immediateRender = false; 317 | values.paused = true; 318 | if(this._loops !== 0) 319 | { 320 | values.repeat = this._loops; 321 | values.repeatDelay = data.loopsDelay; 322 | } 323 | if(this._yoyo) 324 | { 325 | values.yoyo = true; 326 | } 327 | 328 | // add display and visibility properties 329 | 330 | if(toCount) 331 | { 332 | if(this._display.end) 333 | { 334 | to.display = this._display.end; 335 | } 336 | if(this._visibility.end) 337 | { 338 | if(this._visibility.end == 'hidden') 339 | { 340 | thenCount++; 341 | then.visibility = this._visibility.end; 342 | } 343 | else 344 | { 345 | to.visibility = this._visibility.end; 346 | } 347 | } 348 | } 349 | 350 | var duration = Math.max(0, data.duration - 0.000001); 351 | if(fromCount) 352 | { 353 | if(this._display.begin) 354 | { 355 | from.display = this._display.begin; 356 | } 357 | if(this._visibility.begin) 358 | { 359 | from.visibility = this._visibility.begin; 360 | } 361 | 362 | if(toCount) 363 | { 364 | elem = TweenMax.fromTo(this._target, duration, from, to); 365 | } 366 | else 367 | { 368 | elem = TweenMax.from(this._target, duration, from); 369 | } 370 | } 371 | else if(toCount) 372 | { 373 | elem = TweenMax.to(this._target, duration, to); 374 | } 375 | else 376 | { 377 | elem = TweenMax.to(this._target, duration, {opacity: '+=0'}); 378 | } 379 | 380 | 381 | // we add events only to the first tween 382 | if(i === 0) 383 | { 384 | this._eventsTarget = elem; 385 | } 386 | 387 | // avoid from bug when nested inside timelines 388 | // @link http://greensock.com/forums/topic/10418-fromto-seems-to-ignore-immediaterender-false-when-nested/ 389 | this._native.add(elem, 0.000001); 390 | elem.paused(false); 391 | } 392 | 393 | if(this._display.then) 394 | { 395 | thenCount++; 396 | then.display = this._display.then; 397 | } 398 | if(this._visibility.then) 399 | { 400 | thenCount++; 401 | then.visibility = this._visibility.then; 402 | } 403 | 404 | if(thenCount) 405 | { 406 | this._native.to(this._target, 0, then, data.duration); 407 | } 408 | 409 | this._setupEvents(); 410 | 411 | return this._getTotalDuration(); 412 | }; 413 | 414 | 415 | }); 416 | 417 | 418 | 419 | /** 420 | * Gsap Timeline Driver 421 | * 422 | * @mixes Common, TimelineCommon, GsapCommon 423 | * 424 | */ 425 | Tw.registerDriver('Gsap', 'timeline', function(){ 426 | Common.call(this); 427 | TimelineCommon.call(this); 428 | GsapCommon.call(this); 429 | 430 | this._innerNative = null; 431 | 432 | 433 | /** 434 | * Override TimelineCommon.prepare() 435 | * 436 | * @returns {number} 437 | */ 438 | this.prepare = function() 439 | { 440 | if(this._ready) 441 | { 442 | return this; 443 | } 444 | 445 | var values = {paused: true}; 446 | if(this._loops) 447 | { 448 | values.repeat = this._loops; 449 | values.repeatDelay = convertTime(this._loopsDelay, this._coreTimeUnit, this._driverTimeUnit); 450 | } 451 | 452 | if(this._yoyo) 453 | { 454 | values.yoyo = true; 455 | } 456 | 457 | var _native = new TimelineMax(values); 458 | 459 | if(this._parent) 460 | { 461 | this._native = _native; 462 | } 463 | else 464 | { 465 | this._native = new TimelineMax({paused: true, delay: convertTime(this._delay, this._coreTimeUnit, this._driverTimeUnit)}) 466 | .add(_native); 467 | _native.paused(false); 468 | } 469 | 470 | this._innerNative = _native; 471 | this._native.timeScale(this._speed); 472 | this._eventsTarget = _native; 473 | this._setupEvents(); 474 | 475 | this._mergeChildren(); 476 | this._ready = true; 477 | return this._getTotalDuration(); 478 | }; 479 | 480 | 481 | /** 482 | * Override TimelineCommon._reset() 483 | * 484 | */ 485 | this._reset = function() 486 | { 487 | this._offset = 0; 488 | this._cursor = null; 489 | if(this._native) 490 | { 491 | this._native.clear(); 492 | this._innerNative.clear(); 493 | this._native = null; 494 | this._innerNative = null; 495 | } 496 | this._eventsTarget = null; 497 | this._eventsSet = false; 498 | }; 499 | 500 | 501 | /** 502 | * Override TimelineCommon._mergeLabel() 503 | * 504 | */ 505 | this._mergeLabel = function(child, begin) 506 | { 507 | // nop 508 | }; 509 | 510 | 511 | /** 512 | * Override TimelineCommon._mergeTweenable() 513 | * 514 | */ 515 | this._mergeTweenable = function(child, begin, end) 516 | { 517 | if(begin != Infinity) 518 | { 519 | var childNative = child.getNative(); 520 | this._innerNative.add(childNative, convertTime(begin, this._coreTimeUnit, this._driverTimeUnit)); 521 | childNative.paused(false); 522 | } 523 | }; 524 | 525 | 526 | /** 527 | * Override TimelineCommon._mergeCallback() 528 | * 529 | */ 530 | this._mergeCallback = function(child, begin, end) 531 | { 532 | if(begin != Infinity) 533 | { 534 | if(child.isPause) 535 | { 536 | this._native.addPause(convertTime(begin + this._delay, this._coreTimeUnit, this._driverTimeUnit), child.resume, [], child); 537 | } 538 | else 539 | { 540 | this._innerNative.call(child.resume, [], child, convertTime(begin, this._coreTimeUnit, this._driverTimeUnit)); 541 | } 542 | } 543 | }; 544 | 545 | 546 | 547 | }); 548 | 549 | 550 | Tw.defaultTimeUnit = 's'; 551 | Tw.defaultDriver = 'gsap'; 552 | -------------------------------------------------------------------------------- /src/tween-pro.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | 15 | // used as cache for vendor-prefixed names 16 | var propertyNames = {}; 17 | 18 | /** 19 | * Get style real name and value, checking for browser prefixes if needed 20 | * 21 | * @param {object} style 22 | * @param {string} name 23 | * @returns {array} - return [realName, value] 24 | */ 25 | function getProperty(style, name) 26 | { 27 | if(style[name] !== void 0) 28 | { 29 | return [name, style[name]]; 30 | } 31 | if(name in propertyNames) 32 | { 33 | return [propertyNames[name], style[propertyNames[name]]]; 34 | } 35 | name = name.substr(0, 1).toUpperCase() + name.substr(1); 36 | var prefixes = ['webkit', 'moz', 'ms', 'o'], fullName; 37 | for(var i = 0, end = prefixes.length; i < end; i++) 38 | { 39 | fullName = prefixes[i] + name; 40 | if(style[fullName] !== void 0) 41 | { 42 | propertyNames[name] = fullName; 43 | return [fullName, style[fullName]]; 44 | } 45 | } 46 | return [name, void 0]; 47 | } 48 | 49 | 50 | /** 51 | * Reverse a cubic bezier, needed for playing tweens backwards 52 | * 53 | * @param {array} value - 4-length cubic bezier array 54 | * @returns {array} 55 | */ 56 | function reverseBezier(value) 57 | { 58 | return [ 59 | 1 - value[2], 60 | 1 - value[3], 61 | 1 - value[0], 62 | 1 - value[1] 63 | ]; 64 | } 65 | 66 | 67 | // needed by next func 68 | var bezierEasingCache = {}; 69 | 70 | /** 71 | * Widely based on the great work by Vincent Tan 72 | * http://polymathprogrammer.com/2007/06/27/reverse-engineering-bezier-curves/ 73 | * 74 | * given a bezier curve and current time progress, it returns a new cubic bezier array equal to the remaining part of the curve 75 | * 76 | * @param {Array} oldBezier - 4-length cubic bezier array 77 | * @param {Number} time - current progress value, between 0 and 1 78 | * @returns {Array} 79 | */ 80 | function getNewBezier(oldBezier, time) 81 | { 82 | if(time === 0 || time === 1) 83 | { 84 | return oldBezier; 85 | } 86 | 87 | var cacheName = oldBezier.join('_').replace(/\./g, 'p') + '_' + time.toFixed(3); 88 | if(cacheName in bezierEasingCache) 89 | { 90 | return bezierEasingCache[cacheName]; 91 | } 92 | 93 | var oldBezierFunc = bezier.apply(null, oldBezier); 94 | var xInterval = 1 - time; 95 | var startY = oldBezierFunc(time); 96 | var sign = startY > 1? - 1 : 1; 97 | var yInterval = (1 - startY) * sign; 98 | 99 | var u = 0.33, v = 0.67; 100 | var uu = u * xInterval + time; 101 | var vv = v * xInterval + time; 102 | 103 | var 104 | p0x = 0, p0y = 0, 105 | p1x = u, p1y = (oldBezierFunc(uu) - startY) * sign / yInterval, 106 | p2x = v, p2y = (oldBezierFunc(vv) - startY) * sign / yInterval, 107 | p3x = 1, p3y = 1, 108 | compU = 1 - u, compV = 1 -v, 109 | u2 = u * u, u3 = u * u * u, v2 = v * v, v3 = v * v * v, 110 | 111 | a = 3 * compU * compU * u, b = 3 * compU * u2, 112 | c = 3 * compV * compV * v, d = 3 * compV * v2; 113 | 114 | var det = a*d - b*c; 115 | 116 | /* it would not be needed, it's just to honor Murphy's Law */ 117 | if(det === 0) 118 | { 119 | console.log('New Bezier FAIL: Det == 0'); 120 | return oldBezier; 121 | } 122 | 123 | var compU3 = compU * compU * compU, compV3 = compV * compV * compV; 124 | 125 | var 126 | q1x = p1x - (compU3 * p0x + u3 * p3x), 127 | q1y = p1y - (compU3 * p0y + u3 * p3y), 128 | q2x = p2x - (compV3 * p0x + v3 * p3x), 129 | q2y = p2y - (compV3 * p0y + v3 * p3y); 130 | 131 | var res = [ 132 | (d * q1x - b * q2x) / det, 133 | (d * q1y - b * q2y) / det, 134 | 135 | ((-c) * q1x + a * q2x) / det, 136 | ((-c) * q1y + a * q2y) / det 137 | ]; 138 | 139 | bezierEasingCache[cacheName] = res; 140 | return res; 141 | } 142 | 143 | 144 | 145 | 146 | /** 147 | * Vars and methods used in tween object, for animation library that does not have native support 148 | * for playhead control (play / pause / reverse and so on) 149 | * @mixin 150 | * 151 | */ 152 | var TweenPro = function() 153 | { 154 | this._beginReady = this._endReady = this._thenReady = false; 155 | 156 | 157 | 158 | /** 159 | * Called on first tween start 160 | * 161 | * @returns {TweenPro} 162 | */ 163 | this._run = function() 164 | { 165 | if(this._duration) 166 | { 167 | this._startProgress(); 168 | } 169 | // get current display and/or visibility values before starting, if needed 170 | if(this._hasStaticProps) 171 | { 172 | this._fetchStaticProps(); 173 | this._setStaticProps('tween'); 174 | } 175 | 176 | this._running = true; 177 | this._delayDummy = null; 178 | 179 | if(this._emulatedBegin && this._hasHandlers('_begin')) 180 | { 181 | this._runHandlers('_begin'); 182 | } 183 | 184 | // if from() or fromTo() tween, need to jump to begin position before starting the animation 185 | if(this._emulatedFrom && this._from) 186 | { 187 | this._setTween('begin'); 188 | } 189 | 190 | this._startTime = Tw.ticker.now(); 191 | this._playTween(); 192 | 193 | return this; 194 | }; 195 | 196 | 197 | /** 198 | * Used to restore begin or end style values accordingly to current direction 199 | * 200 | * @param {string} field 201 | */ 202 | this._backTween = function(field) 203 | { 204 | this._resetTween(field); 205 | }; 206 | 207 | 208 | /** 209 | * Set style values accordinglty to the param, and perform postTween actions 210 | * 211 | * @param {string} field 212 | */ 213 | this._resetTween = function(field) 214 | { 215 | this._setTween(field); 216 | this._postTween(field); 217 | }; 218 | 219 | 220 | /** 221 | * Perform actions before starting the tween. Apply to both directions 222 | * 223 | * @param {boolean} direction - true = forward, false = backward 224 | */ 225 | this._preTween = function(direction) 226 | { 227 | var field = direction? 'begin' : 'end'; 228 | if(this._hasStaticProps && this._duration) 229 | { 230 | this._setStaticProps(field, 'tween'); 231 | } 232 | this._setTween(field); 233 | }; 234 | 235 | 236 | /** 237 | * Perform actions after ending the tween. Apply to both directions 238 | * 239 | * @param {string} field 240 | */ 241 | this._postTween = function(field) 242 | { 243 | if(field == 'end') 244 | { 245 | if(this._hasThen) 246 | { 247 | this._setTween('then'); 248 | } 249 | if(this._hasStaticProps) 250 | { 251 | this._setStaticProps(field, 'then'); 252 | } 253 | } 254 | else 255 | { 256 | // if the tween is reversed, restore previous style values 257 | // this is needed in timelines, when a reversed tween is preceded by others that refer common targets, with a time gap between them 258 | // otherwise, during the time gap in reverse direction the targets will have wrong style values 259 | if(this._hasPre)// && this._offset !== 0) 260 | { 261 | this._setTween('pre'); 262 | } 263 | this._setStaticProps(field); 264 | } 265 | 266 | }; 267 | 268 | 269 | /** 270 | * Get a simple name: value map of style property, ready to be passed to the chosen animation library. 271 | * If supported, set also the per-property easing specified by the user 272 | * 273 | * @param {object} tween - tween data structure 274 | * @param {string} field - 'begin' | 'end' | 'pre' | 'then' 275 | * @param {boolean} isSet - true when the values are needed for changing the style instantly. Easing info are omitted in this case 276 | * @returns {object} 277 | */ 278 | this._getTweenValues = function(tween, field, isSet) 279 | { 280 | var values = {}, entry, value, name, easing, i = 0; 281 | for(name in tween) 282 | { 283 | entry = tween[name]; 284 | value = null; 285 | easing = entry.easing; 286 | 287 | if(entry[field] !== null) 288 | { 289 | i++; 290 | // cast pure numeric string to number. This avoid bugs in Transit and other libraries that potentially does not support numeric 291 | // values passed as string 292 | value = isNumeric(entry[field])? Number(entry[field]) : entry[field]; 293 | value = isSet || !this._allowMultipleEasing || !easing? value : [value, this._getRealEasing(easing)]; 294 | values[name] = value; 295 | } 296 | } 297 | 298 | // instead of handling different errors from any library involved, if there are no values to set, we force a fake tween 299 | if(!i) 300 | { 301 | values.opacity = '+=0'; 302 | } 303 | 304 | return values; 305 | }; 306 | 307 | 308 | /** 309 | * Fetch a style value for a dom element 310 | * 311 | * @param {object} item - dom element 312 | * @param {string} name - property name 313 | * @param {boolean} useStyle - if true, use element.style instead of computedStyle value 314 | * @returns {string|number} 315 | */ 316 | this._getCurrentValue = function(item, name, useStyle) 317 | { 318 | var style = useStyle? item.style : window.getComputedStyle(item); 319 | return style[name]; 320 | }; 321 | 322 | 323 | /** 324 | * Fetch current values for display and / or visibility properties 325 | * 326 | */ 327 | this._fetchStaticProps = function() 328 | { 329 | this._staticProps = []; 330 | 331 | var item, i, end, names, name, value, fieldName, field, hidden, tweenValue, values; 332 | for(i = 0, end = this._getTargetLength(); i < end; i++) 333 | { 334 | item = this._target.get(i); 335 | names = {display: false, visibility: false}; 336 | 337 | this._staticProps[i] = { 338 | begin: {}, 339 | end: {}, 340 | then: {}, 341 | tween: {} 342 | }; 343 | 344 | for(name in names) 345 | { 346 | fieldName = '_' + name; 347 | field = cloneObject(this[fieldName]); 348 | if(field.mask > 0) 349 | { 350 | if(field.mask < 7) 351 | { 352 | value = this._getCurrentValue(item, name, false); 353 | if(field.begin === null) 354 | { 355 | field.begin = value; 356 | } 357 | 358 | if(field.end === null) 359 | { 360 | field.end = value; 361 | if(field.then === null) 362 | { 363 | field.then = value; 364 | } 365 | } 366 | } 367 | 368 | values = this._staticProps[i]; 369 | 370 | values.begin[name] = field.begin; 371 | values.end[name] = field.end; 372 | values.then[name] = field.then; 373 | // values that show the element need to be set before the tween 374 | hidden = (name == 'display')? 'none' : 'hidden'; 375 | tweenValue = field.begin != hidden? field.begin : (field.end != hidden? field.end : false); 376 | if(tweenValue !== false) 377 | { 378 | values.tween[name] = tweenValue; 379 | } 380 | } 381 | } 382 | } 383 | }; 384 | 385 | 386 | /** 387 | * Set display and visibility properties, that are handled separately 388 | * @link http://tweene.com/docs/#displayVisibility 389 | * 390 | * @param {string} first 391 | * @param {string} [second] two set of values could be applied toghether in some cases, for example end + then or begin + tween 392 | */ 393 | this._setStaticProps = function(first, second) 394 | { 395 | if(this._staticProps.length) 396 | { 397 | var i, end, values; 398 | for(i = 0, end = this._getTargetLength(); i < end; i++) 399 | { 400 | values = this._staticProps[i][first]; 401 | if(second) 402 | { 403 | values = extendObject(values, this._staticProps[i][second]); 404 | } 405 | if(!isEmpty(values)) 406 | { 407 | this._target.eq(i).css(values); 408 | } 409 | } 410 | } 411 | }; 412 | 413 | 414 | /** 415 | * Override TweenCommon._getRealEasing() 416 | * It generates also custom bezier curves needed for resuming a paused tween honoring the original easing effect 417 | * 418 | * @param {string|array} value 419 | * @returns {string|array|function} 420 | */ 421 | this._getRealEasing = function(value) 422 | { 423 | if(isString(value) && value in easings) 424 | { 425 | value = easings[value]; 426 | } 427 | 428 | if(isArray(value)) 429 | { 430 | var position = this._position; 431 | // in backward direction, reverse the bezier curve too 432 | if(!this._localFwd) 433 | { 434 | value = reverseBezier(value); 435 | position = this._duration - position; 436 | } 437 | 438 | var timeRatio = position / this._duration; 439 | value = this._getBezierEasing(getNewBezier(value, timeRatio)); 440 | } 441 | 442 | return value; 443 | }; 444 | 445 | 446 | }; -------------------------------------------------------------------------------- /src/timeline-common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | 15 | /** 16 | * Vars and methods common to every timeline object, whatever is the driver used 17 | * @mixin 18 | * 19 | */ 20 | var TimelineCommon = function() 21 | { 22 | this.type = 'timeline'; 23 | 24 | this._offset = 0; 25 | 26 | this._children = []; 27 | 28 | this._cursor = null; 29 | 30 | this._labels = {}; 31 | 32 | 33 | /** 34 | * Add tweens, timelines, callbacks and labels to current timeline object 35 | * @link http://tweene.com/docs/#timelineAdd 36 | * @link http://tweene.com/docs/#nestedTimelines 37 | * @link http://tweene.com/docs/#directionalCallbacks 38 | * @link http://tweene.com/docs/#labels 39 | * 40 | * @param {string|object|number} child - number is accepted only when adding directional callback 41 | * @param {string|number} [startPosition] 42 | * @returns {this} 43 | */ 44 | this.add = function(child, startPosition) 45 | { 46 | // adding a label 47 | if(isString(child)) 48 | { 49 | // label needs to start with an alphabetic character and cannot contains arithmetic symbols specified in the regexp here 50 | if(child.search(/^[a-z][^\+\-=]*$/) != -1) 51 | { 52 | child = new Label(child); 53 | this._labels[child.id()] = child; 54 | } 55 | else 56 | { 57 | throw 'The label "' + child +'" contains invalid symbols'; 58 | } 59 | } 60 | else 61 | { 62 | // adding a callback or directional callback 63 | if(isFunction(child) || (isNumber(child) && isFunction(startPosition))) 64 | { 65 | var dir = 0, i = 0; 66 | // when a number is passed as first arg, it is a directional callback and we need to shift left the other params 67 | if(isNumber(child)) 68 | { 69 | dir = child; 70 | child = startPosition; 71 | startPosition = arguments[2] || null; 72 | i = 1; 73 | } 74 | i = i + 2; 75 | // params expected after the callback 76 | var params = arguments.length > i? (isArray(arguments[i])? arguments[i] : [arguments[i]]) : []; 77 | i ++; 78 | // callback scope object expected after callback params 79 | var scope = arguments.length > i? arguments[i] : null; 80 | child = new Callback(child, scope, params, dir, false); 81 | } 82 | 83 | child.parent(this); 84 | } 85 | 86 | if(startPosition === void 0) 87 | { 88 | startPosition = null; 89 | } 90 | 91 | this._children.push({id: child.id(), child: child, start: startPosition}); 92 | this.invalidate(); 93 | return this; 94 | }; 95 | 96 | 97 | /** 98 | * Add pause, with an optional callback 99 | * @link http://tweene.com/docs/#addPause 100 | * 101 | * @param {string|number} [startPosition] 102 | * @param {string|number} [callbackDirection] 103 | * @param {function} [callback] - callback 104 | * @param {array} [params] - callback params 105 | * @param {object} [scope] - callback scope 106 | * @returns {this} 107 | */ 108 | this.addPause = function() 109 | { 110 | var args = toArray(arguments), 111 | startPosition = null, 112 | dir = 0, 113 | callback = null, 114 | params = [], 115 | scope = null, 116 | arg, 117 | child; 118 | 119 | if(args.length) 120 | { 121 | arg = args.shift(); 122 | if(isFunction(arg)) 123 | { 124 | callback = arg; 125 | } 126 | else 127 | { 128 | startPosition = arg; 129 | } 130 | 131 | if(args.length) 132 | { 133 | arg = args.shift(); 134 | if(!callback) 135 | { 136 | if(isNumber(arg)) 137 | { 138 | dir = arg; 139 | if(args.length) 140 | { 141 | callback = args.shift(); 142 | } 143 | } 144 | else 145 | { 146 | callback = arg; 147 | } 148 | } 149 | 150 | if(callback && args.length) 151 | { 152 | params = args.shift(); 153 | if(!isArray(params)) 154 | { 155 | params = [params]; 156 | } 157 | 158 | if(args.length) 159 | { 160 | scope = args.shift(); 161 | } 162 | } 163 | } 164 | } 165 | 166 | child = new Callback(callback, scope, params, dir, true); 167 | child.parent(this); 168 | this._children.push({id: child.id(), child: child, start: startPosition}); 169 | this.invalidate(); 170 | return this; 171 | }; 172 | 173 | 174 | 175 | /** 176 | * Create a tween and execute a previously registered macro on it 177 | * If the timeline has not a target specified, it expects a target as first param. 178 | * It expects a position as second (or first) param, all other params are passed to the tween exec() method 179 | * 180 | * @returns {this} 181 | */ 182 | this.exec = function() 183 | { 184 | var args = toArray(arguments); 185 | if(args.length) 186 | { 187 | var target = this._target? this._target : args.shift(); 188 | var tween = Tw.get(target, this.driverName); 189 | var pos = args.length > 1? args.splice(1, 1)[0] : null; 190 | this.add(tween, pos); 191 | tween.exec.apply(tween, args); 192 | } 193 | return this; 194 | }; 195 | 196 | /** 197 | * Schedule a tween with duration = 0 198 | * @link http://tweene.com/docs/#timelineSet 199 | * 200 | * @returns {this} 201 | */ 202 | this.set = function() 203 | { 204 | var args = toArray(arguments); 205 | if(args.length) 206 | { 207 | var target = this._target? this._target : args.shift(); 208 | var tween = Tw.get(target, this.driverName); 209 | if(args.length) 210 | { 211 | var values = args.shift(); 212 | var pos = args.length? args.shift() : null; 213 | tween._to = values; 214 | tween.duration(0); 215 | this.add(tween, pos); 216 | } 217 | } 218 | return this; 219 | }; 220 | 221 | 222 | /** 223 | * Shortcut for .add(Tweene.get().to()) 224 | * @link http://tweene.com/docs/#timelineTo 225 | * 226 | * @returns {this} 227 | */ 228 | this.to = function() 229 | { 230 | return this._tweenMethod(arguments, false, true); 231 | }; 232 | 233 | 234 | /** 235 | * Shortcut for .add(Tweene.get().fromTo()) 236 | * @link http://tweene.com/docs/#timelineFromTo 237 | * 238 | * @returns {this} 239 | */ 240 | this.fromTo = function() 241 | { 242 | return this._tweenMethod(arguments, true, true); 243 | }; 244 | 245 | 246 | /** 247 | * Shortcut for .add(Tweene.get().from()) 248 | * @link http://tweene.com/docs/#timelineFrom 249 | * 250 | * @returns {this} 251 | */ 252 | this.from = function() 253 | { 254 | return this._tweenMethod(arguments, true, false); 255 | }; 256 | 257 | 258 | /** 259 | * used internally for setting child timeline time position inside the parent 260 | * 261 | * @param {number} value 262 | * @returns {this} 263 | */ 264 | this.offset = function(value) 265 | { 266 | this._offset = value; 267 | return this; 268 | }; 269 | 270 | 271 | /** 272 | * Timeline need to process its children just before starting or when you ask for duration. See implementation in TimelinePro or 273 | * in specific drivers 274 | * 275 | * @returns {this} 276 | */ 277 | this.prepare = function() 278 | { 279 | if(this._ready) 280 | { 281 | return this; 282 | } 283 | 284 | this._reset(); 285 | this._mergeChildren(); 286 | this.ready = true; 287 | return this; 288 | }; 289 | 290 | 291 | /** 292 | * Perform all the common actions needed by .to(), .from() and .fromTo() 293 | * 294 | * @param {arguments} args 295 | * @param {boolean} from 296 | * @param {boolean} to 297 | * @returns {this} 298 | */ 299 | this._tweenMethod = function(args, from, to) 300 | { 301 | args = toArray(args); 302 | if(args.length) 303 | { 304 | // use first argument as target if the timeline does not have a global target set 305 | var target = this._target? this._target : args.shift(); 306 | var tween = Tw.get(target, this.driverName); 307 | var pos = tween.parseArguments(args, from, to, true); 308 | this.add(tween, pos); 309 | } 310 | return this; 311 | }; 312 | 313 | 314 | /** 315 | * Process all the children added evaluating their actual time position inside the timeline 316 | * 317 | * @returns {this} 318 | */ 319 | this._mergeChildren = function() 320 | { 321 | if(this._ready) 322 | { 323 | return this; 324 | } 325 | 326 | // cursor will contains the end of the last processed child, while duration holds the overall end of the timeline 327 | this._cursor = this._duration = 0; 328 | 329 | var child, begin, end, start, childDelay, tweenable; 330 | for(var i = 0, len = this._children.length; i < len; i++) 331 | { 332 | child = this._children[i].child; 333 | start = this._children[i].start; 334 | 335 | tweenable = child.type == 'timeline' || child.type == 'tween'; 336 | 337 | if(tweenable) 338 | { 339 | // if the child has a delay, remove it from the child and use it as a start offset inside the timeline 340 | childDelay = this._parseTime(child.delay()); 341 | if(childDelay) 342 | { 343 | this._cursor += childDelay; 344 | this._duration += childDelay; 345 | child.delay(0); 346 | } 347 | } 348 | 349 | // evaluate actual start position 350 | begin = this._getStartPosition(this._duration, this._cursor, start); 351 | 352 | if(child.type == 'label') 353 | { 354 | child.position(begin); 355 | this._mergeLabel(child, begin); 356 | continue; 357 | } 358 | 359 | if(tweenable) 360 | { 361 | if(child.type == 'timeline') 362 | { 363 | child.offset(this._offset + begin); 364 | } 365 | // prepare() returns totalDuration 366 | end = begin + child.prepare(); 367 | this._mergeTweenable(child, begin, end); 368 | } 369 | else 370 | { 371 | // callbacks have duration = 0 372 | end = begin; 373 | this._mergeCallback(child, begin, end); 374 | } 375 | 376 | // an infinite loop in a child tween or timeline results in its duration = Infinity 377 | if(end != Infinity) 378 | { 379 | this._cursor = end; 380 | if(this._cursor > this._duration) 381 | { 382 | this._duration = this._cursor; 383 | } 384 | } 385 | else 386 | { 387 | this._cursor = this._duration = Infinity; 388 | } 389 | } 390 | return this; 391 | }; 392 | 393 | 394 | /** 395 | * Evaluate actual time position of a child inside a timeline 396 | * 397 | * @param {number} currentDuration 398 | * @param {number} currentCursor - end of the previously processed child 399 | * @param {string|number} startPosition 400 | * 401 | * @returns {number} 402 | */ 403 | this._getStartPosition = function(currentDuration, currentCursor, startPosition) 404 | { 405 | // by default, add to the end of the timeline, obtaining a queue of not-overlapping tweens 406 | if(startPosition === null) 407 | { 408 | return currentDuration; 409 | } 410 | var start = currentDuration, pos, sign = 0, toCursor = false; 411 | if(isString(startPosition)) 412 | { 413 | // parts: 414 | // 1 - label 415 | // 2 - relative operator, +=, ++=, -=, --= 416 | // 3 - time value, number or string with 's' or 'ms' suffix 417 | var parts = startPosition.match(/^([a-z][^\+\-=]*)?(?:(\+{1,2}|\-{1,2})=)?([^\+\-=]+)?$/i); 418 | if(parts === null) 419 | { 420 | return currentDuration; 421 | } 422 | 423 | pos = parts[3] !== void 0? this._parseTime(parts[3]) : 0; 424 | 425 | if(parts[2] !== void 0) 426 | { 427 | toCursor = parts[2].length == 2; 428 | sign = parts[2].substr(0, 1) == '-'? -1 : 1; 429 | } 430 | 431 | if(parts[1] !== void 0 && parts[1] in this._labels) 432 | { 433 | start = this._labels[parts[1]].position(); 434 | if(!sign) 435 | { 436 | pos = 0; 437 | sign = 1; 438 | } 439 | } 440 | else 441 | { 442 | if(sign) 443 | { 444 | start = toCursor? currentCursor: currentDuration; 445 | } 446 | else 447 | { 448 | start = 0; 449 | sign = 1; 450 | } 451 | } 452 | } 453 | else 454 | { 455 | start = 0; 456 | sign = 1; 457 | pos = this._parseTime(startPosition); 458 | } 459 | 460 | if(start == Infinity) 461 | { 462 | return Infinity; 463 | } 464 | 465 | // cannot add child in negative positions, fallback to 0 466 | return Math.max(0, start + (pos * sign)); 467 | }; 468 | 469 | }; 470 | -------------------------------------------------------------------------------- /src/tweene-transit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweene - JavaScript Animation Proxy 3 | * 4 | * @link http://tweene.com 5 | * 6 | * Copyright (c) 2014, Federico Orru' 7 | * 8 | * @license Artistic License 2.0 9 | * See LICENSE.txt for details 10 | * 11 | */ 12 | 13 | 14 | /** 15 | * Transit Tween Driver 16 | * 17 | * @link http://ricostacruz.com/jquery.transit/ * 18 | * @mixes Common, TweenCommon, ControlsPro, TweenPro 19 | * 20 | */ 21 | Tw.registerDriver('transit', 'tween', function() { 22 | Common.call(this); 23 | TweenCommon.call(this); 24 | ControlsPro.call(this); 25 | TweenPro.call(this); 26 | 27 | this._driverTimeUnit = 'ms'; 28 | 29 | this._emulatedPlayhead = true; 30 | this._emulatedFrom = true; 31 | this._emulatedDelay = true; 32 | this._emulatedLoop = true; 33 | this._emulatedBegin = true; 34 | this._emulatedProgress = true; 35 | this._allowTransform = true; 36 | this._allowMultipleEasing = false; 37 | 38 | this._propertyMap = { 39 | translateX: 'x', 40 | translateY: 'y', 41 | translateZ: 'z', 42 | rotateZ: 'rotate', 43 | rotation: 'rotate', 44 | rotationX: 'rotateX', 45 | rotationY: 'rotateY', 46 | rotationZ: 'rotate', 47 | scaleZ: 'scale' 48 | }; 49 | 50 | // force transitionEnd events 51 | $.transit.useTransitionEnd = true; 52 | 53 | this._emulatingComplete = false; 54 | this._currentEasing = null; 55 | 56 | this._styles = []; 57 | this._firstRun = true; 58 | this._rotationFixed = false; 59 | 60 | 61 | /** 62 | * Get style objects for a dom target, storing the reference inside an internal cache 63 | * 64 | */ 65 | this._getTargetStyle = function(i, useStyle) 66 | { 67 | // style are checked in asc order 68 | if(i >= this._styles.length) 69 | { 70 | var target = this._target.get(i); 71 | this._styles[i] = [window.getComputedStyle(target), target.style]; 72 | } 73 | 74 | return this._styles[i][useStyle? 1 : 0]; 75 | }; 76 | 77 | 78 | /** 79 | * CSS Transitions supports natively cubic bezier curves 80 | * 81 | * @param {array} value 82 | * @returns {string} 83 | */ 84 | this._getBezierEasing = function(value) 85 | { 86 | if(this._currentEasing === null) 87 | { 88 | this._currentEasing = value; 89 | } 90 | return 'cubic-bezier(' + value.join(', ') + ')'; 91 | }; 92 | 93 | 94 | /** 95 | * Fetch transform property value directly from Transit 96 | * 97 | */ 98 | this._getTransformValue = function(target, name, raw) 99 | { 100 | var transform = target.data('transform'); 101 | if(!transform || !(name in transform)) 102 | { 103 | if(raw) 104 | { 105 | return null; 106 | } 107 | else 108 | { 109 | if(name.indexOf('scale') === 0) 110 | { 111 | name = 'scale'; 112 | } 113 | else if(name.indexOf('rotate') === 0) 114 | { 115 | name = 'rotate'; 116 | } 117 | } 118 | } 119 | return target.css(name); 120 | }; 121 | 122 | 123 | /** 124 | * Fetch current style properties values and pass them to the given callback 125 | * 126 | * @param {object} target 127 | * @param {object} tween 128 | * @param {boolean} useStyle 129 | * @param {function} callback 130 | * 131 | */ 132 | this._getCurrentValues = function(target, tween, useStyle, callback) 133 | { 134 | var item = target.get(0), name, value, property; 135 | var style = useStyle? item.style : window.getComputedStyle(item); 136 | 137 | for(name in tween) 138 | { 139 | if(tween[name].isTransform) 140 | { 141 | value = this._getTransformValue(target, name); 142 | } 143 | else 144 | { 145 | property = getProperty(style, name); 146 | // update data in case of browser-prefixed name 147 | if(property[0] != name) 148 | { 149 | tween[property[0]] = tween[name]; 150 | delete tween[name]; 151 | name = property[0]; 152 | } 153 | value = property[1]; 154 | } 155 | 156 | if(value !== void 0) 157 | { 158 | callback.call(this, tween, name, value); 159 | } 160 | } 161 | }; 162 | 163 | 164 | /** 165 | * Save pre-start values in tween structure 166 | * 167 | * @param {object} tween 168 | * @param {string} name 169 | * @param {string|number} value 170 | */ 171 | this._fetchBeginPre = function(tween, name, value) 172 | { 173 | var prop = tween[name]; 174 | prop.pre = value; 175 | if(!this._hasEnd) 176 | { 177 | prop.end = value; 178 | if(this._hasThen && prop.then === null) 179 | { 180 | prop.then = value; 181 | } 182 | } 183 | this._hasPre = true; 184 | }; 185 | 186 | 187 | /** 188 | * Save post-start values in tween structure 189 | * 190 | * @param {object} tween 191 | * @param {string} name 192 | * @param {string|number} value 193 | */ 194 | this._fetchBeginPost = function(tween, name, value) 195 | { 196 | var prop = tween[name]; 197 | prop.begin = value; 198 | if(prop.end === null) 199 | { 200 | prop.end = value; 201 | if(this._hasThen && prop.then === null) 202 | { 203 | prop.then = value; 204 | } 205 | } 206 | }; 207 | 208 | 209 | /** 210 | * Save values after applying 'then' style 211 | * 212 | * @param {object} tween 213 | * @param {string} name 214 | * @param {string|number} value 215 | */ 216 | this._fetchThen = function(tween, name, value) 217 | { 218 | tween[name].then = value; 219 | }; 220 | 221 | 222 | /** 223 | * Save pre-tween values in tween structure 224 | * 225 | * @param {object} tween 226 | * @param {string} name 227 | * @param {string|number} value 228 | */ 229 | this._fetchPlayPre = function(tween, name, value) 230 | { 231 | var prop = tween[name]; 232 | prop.begin = value; 233 | 234 | if(prop.end === null) 235 | { 236 | prop.end = value; 237 | } 238 | 239 | if(this._hasThen && prop.then === null) 240 | { 241 | prop.then = value; 242 | } 243 | }; 244 | 245 | 246 | /** 247 | * Save post-tween values in tween structure 248 | * 249 | * @param {object} tween 250 | * @param {string} name 251 | * @param {string|number} value 252 | */ 253 | this._fetchPlayPost = function(tween, name, value) 254 | { 255 | var prop = tween[name]; 256 | prop.end = value; 257 | 258 | if(this._hasThen && prop.then === null) 259 | { 260 | prop.then = value; 261 | } 262 | 263 | }; 264 | 265 | 266 | /** 267 | * Set css values instantly 268 | * 269 | * @param {string} field - 'begin' | 'end' | 'pre' | 'then' 270 | */ 271 | this._setTween = function(field) 272 | { 273 | var tween, target, i, end; 274 | for(i = 0, end = this._target.length; i < end; i++) 275 | { 276 | tween = this._data.tween[i]; 277 | target = this._target.eq(i); 278 | 279 | if(field == 'begin' && this._hasBegin && !this._beginReady) 280 | { 281 | this._getCurrentValues(target, tween, false, this._fetchBeginPre); 282 | } 283 | 284 | var values = this._getTweenValues(tween, field, true); 285 | this._target.eq(i).css(values); 286 | 287 | if(field == 'begin' && this._hasBegin && !this._beginReady) 288 | { 289 | this._getCurrentValues(target, tween, false, this._fetchBeginPost); 290 | } 291 | else if(field == 'then' && this._hasThen && !this._thenReady) 292 | { 293 | this._getCurrentValues(target, tween, false, this._fetchThen); 294 | } 295 | } 296 | 297 | if(field == 'begin') 298 | { 299 | this._beginReady = true; 300 | } 301 | else if(field == 'then') 302 | { 303 | this._thenReady = true; 304 | } 305 | 306 | return this; 307 | }; 308 | 309 | 310 | /** 311 | * Execute the effective tween 312 | * 313 | */ 314 | this._playTween = function() 315 | { 316 | var data = this._data, 317 | self = this, useTrans = data.duration > 0, 318 | method = useTrans? 'transition' : 'css', 319 | field = this._localFwd? 'end' : 'begin', 320 | name, needRepaint, item, targetStyle, targetComputedStyle, posValue, 321 | posNames = ['left', 'top', 'right', 'bottom'], pos, 322 | i, end, j, endj, values, nop, 323 | onComplete = function(event) { 324 | //event.stopPropagation(); 325 | //event.preventDefault(); 326 | // if(this == event.target) 327 | // { 328 | self._target.css('transition', 'none'); 329 | self._runHandlers('_end'); 330 | // Tw.ticker.addCallback(-1, '-' + self._id, self._runHandlers, self, ['_end']); 331 | // } 332 | // else 333 | // { 334 | // console.log('parent!'); 335 | // } 336 | return false; 337 | }; 338 | 339 | for(i = 0, end = this._target.length; i < end; i++) 340 | { 341 | var tween = data.tween[i]; 342 | var target = this._target.eq(i); 343 | if(!this._beginReady) 344 | { 345 | this._getCurrentValues(target, tween, false, this._fetchPlayPre); 346 | } 347 | 348 | values = this._getTweenValues(tween, field, false); 349 | 350 | // use transitions only if duration > 0 351 | if(useTrans) 352 | { 353 | values.duration = data.realDuration; 354 | values.queue = false; 355 | if(data.duration) 356 | { 357 | values.easing = this._getRealEasing(data.easing); 358 | } 359 | 360 | // on first run there could be a bug when animating left, top, right or bottom that are 'auto', we need to force a repaint 361 | if(this._firstRun) 362 | { 363 | needRepaint = false; 364 | targetStyle = null; 365 | targetComputedStyle = null; 366 | item = target.get(0); 367 | 368 | for(j = 0, endj = posNames.length; j < endj; j++) 369 | { 370 | pos = posNames[j]; 371 | if(pos in values) 372 | { 373 | // target style cached while checking all the properties 374 | if(!targetStyle) 375 | { 376 | targetStyle = this._getTargetStyle(i, true); 377 | targetComputedStyle = this._getTargetStyle(i, false); 378 | } 379 | posValue = targetStyle[pos]; 380 | // if 'auto', use value from jquery position(), else use computed value, then force a repaint 381 | if(posValue === '' || posValue === 'auto') 382 | { 383 | targetStyle[pos] = targetComputedStyle[pos] == 'auto'? target.position()[pos] : targetComputedStyle[pos]; 384 | needRepaint = true; 385 | } 386 | } 387 | } 388 | if(needRepaint) 389 | { 390 | nop = item.offsetWidth; 391 | } 392 | 393 | } 394 | } 395 | if(useTrans && i == end - 1) 396 | { 397 | // transit complete callback is not passing the event object needed to stop propagation in nested contexts 398 | // also when display = none, css transitions does not raise complete event 399 | // we need to emulate the event 400 | this._emulatingComplete = true; 401 | Tw.ticker.addCallback(data.realDuration, '-emulate' + this._id, onComplete); 402 | target.transition(values); 403 | target.unbind('transitionend'); 404 | } 405 | else 406 | { 407 | target[method](values); 408 | } 409 | 410 | if(!this._endReady) 411 | { 412 | this._getCurrentValues(target, tween, true, this._fetchPlayPost); 413 | } 414 | } 415 | 416 | this._firstRun = false; 417 | this._beginReady = true; 418 | this._endReady = true; 419 | 420 | // if instant tween, emulate complete event 421 | if(!useTrans) 422 | { 423 | this._runHandlers('_end'); 424 | } 425 | 426 | return this; 427 | }; 428 | 429 | 430 | /** 431 | * Pause a running tween 432 | * 433 | */ 434 | this._pauseTween = function() 435 | { 436 | var easingFunc = null, transform, transformValues = null, offset, currentOffset, 437 | timeProgress, valueProgress, beginValue, endValue, current, 438 | i, end, style, targetStyle, tween, target, name, prop; 439 | 440 | // cancel complete callback 441 | if(this._emulatingComplete) 442 | { 443 | Tw.ticker.removeCallback('-emulate' + this._id); 444 | } 445 | this._target.unbind($.support.transitionEnd); 446 | 447 | for(i = 0, end = this._target.length; i < end; i++) 448 | { 449 | style = {}; 450 | targetStyle = this._getTargetStyle(i, false); 451 | tween = this._data.tween[i]; 452 | target = this._target.eq(i); 453 | for(name in tween) 454 | { 455 | prop = tween[name]; 456 | if(prop.isTransform) 457 | { 458 | // fetch current transition values directly from transform matrix, no need for calculation 459 | if(name == 'x' || name == 'y' || name == 'z') 460 | { 461 | if(!transformValues) 462 | { 463 | transform = getProperty(targetStyle, 'transform')[1]; 464 | transformValues = transform.substring(transform.indexOf('(') + 1, transform.length - 1).split(/\s*,\s*/); 465 | offset = transform.indexOf('matrix3d') === 0? 12 : 4; 466 | } 467 | currentOffset = offset + (name == 'z'? 2 : (name == 'y'? 1 : 0)); 468 | style[name] = transformValues[currentOffset]; 469 | } 470 | else 471 | { 472 | // calculate the current value at current progress, taking easing in consideration 473 | if(!easingFunc) 474 | { 475 | easingFunc = bezier.apply(null, this._currentEasing); 476 | timeProgress = this.progress(); 477 | valueProgress = easingFunc(timeProgress); 478 | } 479 | beginValue = parseFloat(prop.begin); 480 | endValue = parseFloat(prop.end); 481 | current = ((endValue - beginValue) * valueProgress) + beginValue; 482 | style[name] = current; 483 | } 484 | } 485 | else 486 | { 487 | // read non-transform properties directly from computed style 488 | style[name] = targetStyle[name]; 489 | } 490 | } 491 | style[getProperty(targetStyle, 'transition')[0]] = 'none'; 492 | target.css(style); 493 | } 494 | return this; 495 | }; 496 | 497 | 498 | 499 | this._resumeTween = function() 500 | { 501 | return this._playTween(); 502 | }; 503 | 504 | 505 | }); 506 | 507 | 508 | 509 | /** 510 | * Transit Timeline Driver 511 | * 512 | * @mixes Common, TimelineCommon, ControlsPro, TimelinePro 513 | * 514 | */ 515 | Tw.registerDriver('transit', 'timeline', function() { 516 | Common.call(this); 517 | TimelineCommon.call(this); 518 | ControlsPro.call(this); 519 | TimelinePro.call(this); 520 | 521 | this._driverTimeUnit = 'ms'; 522 | 523 | }); 524 | 525 | Tw.defaultTimeUnit = 'ms'; 526 | Tw.defaultDriver = 'transit'; 527 | --------------------------------------------------------------------------------