├── .editorconfig ├── .gitattributes ├── .gitignore ├── .npmrc ├── .travis.yml ├── dist └── gsap-then.browser.js ├── index.js ├── license ├── logo.svg ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | Desktop.ini 4 | ._* 5 | Thumbs.db 6 | *.tmp 7 | *.bak 8 | *.log 9 | logs 10 | 11 | *.common-js.js 12 | *.es-modules.js 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'node' 4 | -------------------------------------------------------------------------------- /dist/gsap-then.browser.js: -------------------------------------------------------------------------------- 1 | /*! npm.im/gsap-then */ 2 | !function(){"use strict";("undefined"==typeof window?global:window).com.greensock.core.Animation.prototype.then=function(n){var e=this;return new Promise(function(o){var t=e.eventCallback("onComplete");e.eventCallback("onComplete",function(){t&&t.apply(this,arguments),n(),o()})})}}(); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (typeof window === 'undefined' ? global : window) 3 | .com 4 | .greensock 5 | .core 6 | .Animation 7 | .prototype 8 | .then = function (onFullfilled) { 9 | return new Promise(resolve => { 10 | const existing = this.eventCallback('onComplete'); 11 | this.eventCallback('onComplete', function () { 12 | if (existing) { 13 | existing.apply(this, arguments); 14 | } 15 | onFullfilled(); 16 | resolve(); 17 | }); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Federico Brigante (bfred.it) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 11 | 14 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gsap-then", 3 | "version": "3.0.0", 4 | "description": "Make every GSAP Tween a promise. tl.then(doSomething)", 5 | "keywords": [ 6 | "greensock", 7 | "gsap", 8 | "promise", 9 | "then", 10 | "thenable", 11 | "timelinelite", 12 | "timelinemax", 13 | "tweenlite", 14 | "tweenmax" 15 | ], 16 | "repository": "fregante/gsap-then", 17 | "license": "MIT", 18 | "author": "Federico Brigante (bfred.it)", 19 | "files": [ 20 | "dist/gsap-then.common-js.js", 21 | "dist/gsap-then.es-modules.js" 22 | ], 23 | "main": "dist/gsap-then.common-js.js", 24 | "module": "dist/gsap-then.es-modules.js", 25 | "jsnext:main": "dist/gsap-then.es-modules.js", 26 | "scripts": { 27 | "build": "npm-run-all --silent jsfix build:*", 28 | "build:js": "bfred-npm-bundler gsap-then makeGsapThenable", 29 | "jsfix": "xo --fix", 30 | "prepublish": "npm run build", 31 | "test": "xo && ava --serial && npm run build", 32 | "watch": "npm-run-all --parallel --silent watch:*", 33 | "watch:build": "onchange 'index.js' --initial -- npm run build -- --continue-on-error" 34 | }, 35 | "xo": { 36 | "env": [ 37 | "browser" 38 | ], 39 | "esnext": false 40 | }, 41 | "dependencies": {}, 42 | "devDependencies": { 43 | "ava": "^1.3.1", 44 | "bfred-npm-bundler": "^7.1.2", 45 | "gsap": "^2.1.2", 46 | "npm-run-all": "^2.3.0", 47 | "onchange": "^2.5.0", 48 | "xo": "^0.16.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # gsap-then 2 | 3 | > Make every GSAP Tween a promise 4 | 5 | [![gzipped size](https://badges.herokuapp.com/size/github/fregante/gsap-then/master/dist/gsap-then.browser.js?gzip=true&label=gzipped%20size)](#readme) 6 | [![Travis build](https://api.travis-ci.org/fregante/gsap-then.svg?branch=master)](https://travis-ci.org/fregante/gsap-then) 7 | [![npm link](https://img.shields.io/npm/v/gsap-then.svg)](https://www.npmjs.com/package/gsap-then) 8 | 9 | **Good news! GSAP 3 [supports promises](https://greensock.com/docs/v3/GSAP/Tween/then()) natively!** This module is only necessary for GSAP v1 and v2. 10 | 11 | Once loaded, every GSAP tween (TweenLite, TimelineLite, TweenMax, TimelineMax) will automatically be a promise. See the [usage examples](#usage) to see what this enables. 12 | 13 | ## Install 14 | 15 | ```sh 16 | npm install --save gsap-then 17 | ``` 18 | 19 | ```js 20 | import 'gsap'; 21 | import 'gsap-then'; 22 | ``` 23 | 24 | Or include the file `dist/gsap-then.browser.js` after loading GreenSock. 25 | 26 | ## Usage 27 | 28 | ```js 29 | TweenLite.to('.title', 1, {opacity: 0}).then(function () { 30 | console.log('Done animating title'); 31 | }) 32 | ``` 33 | 34 | ```js 35 | Promise.all([ 36 | TweenLite.to('.title', 1, {opacity: 0}), 37 | loadImage('img.jpg') // https://npm.im/image-promise 38 | ]).then(function () { 39 | console.log('Animation done and image loaded'); 40 | }); 41 | ``` 42 | 43 | ```js 44 | var tl = new TimelineLite(); 45 | tl.then(function () { 46 | console.log('Timeline completed:', tl); 47 | }) 48 | tl.to('.title', 1, {opacity: 0}); 49 | ``` 50 | 51 | ```js 52 | await TweenLite.to('.title', 1, {opacity: 0}); 53 | 54 | console.log('Done animating title'); 55 | ``` 56 | 57 | ## Notes 58 | 59 | * Calling `.then()` generates a new Promise. 60 | * The generated Promise is resolved the next time GSAP calls `onComplete` 61 | * The Promise is only resolved once, so if you restart the animation, nothing new will happen—unless you generate a new Promise. 62 | * If the tween already has an `onComplete` callback, it will be replaced by the Promise, but it will still work. 63 | * Don't remove or set a new `onComplete` callback **after** calling `.then()` because this will override the Promise (i.e. it will never be resolved) 64 | 65 | ## Dependencies 66 | 67 | * Load `gsap` or simply `TweenLite` before `gsap-then`. 68 | * `window.Promise` is available in Edge 12+ and all the [other browsers.](https://caniuse.com/#feat=promises) 69 | 70 | ## Related 71 | 72 | * [GSAP](https://github.com/greensock/GreenSock-JS): GreenSock Animation Platform, duh! 73 | 74 | ## License 75 | 76 | MIT © [Federico Brigante](https://bfred.it) 77 | 78 | gsap-then is NOT affiliated with, endorsed, or sponsored by GreenSock, Inc. 79 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { 3 | TweenLite, 4 | TweenMax, 5 | TimelineLite, 6 | TimelineMax 7 | } from 'gsap'; 8 | 9 | global.window = { 10 | com: global.com 11 | }; 12 | 13 | const help = { 14 | TweenLite: class { 15 | constructor() { 16 | return TweenLite.set({}, {}); 17 | } 18 | }, 19 | TweenMax: class { 20 | constructor() { 21 | return TweenMax.set({}, {}); 22 | } 23 | } 24 | }; 25 | 26 | require('./index.js'); 27 | 28 | [ 29 | help.TweenLite, 30 | help.TweenMax, 31 | TimelineLite, 32 | TimelineMax 33 | ].forEach(Tween => { 34 | test(Tween.name + ': base setting', t => { 35 | const tl = new Tween(); 36 | t.is(typeof tl.eventCallback('onComplete'), 'undefined'); 37 | tl.then(() => {}); 38 | t.is(typeof tl.eventCallback('onComplete'), 'function'); 39 | }); 40 | 41 | test.cb(Tween.name + ': calling the callback (gsap native)', t => { 42 | const tl = new Tween(); 43 | tl.eventCallback('onComplete', () => t.end()); 44 | tl.progress(1, false); // Mark tween as "done" 45 | }); 46 | 47 | test.cb(Tween.name + ': calling the callback (then)', t => { 48 | const tl = new Tween(); 49 | tl.then(() => t.end()); 50 | tl.progress(1, false); // Mark tween as "done" 51 | }); 52 | 53 | test(Tween.name + ': calling the callback (await)', async t => { 54 | const tl = new Tween(); 55 | setTimeout(() => tl.progress(1, false)); // Mark tween as "done" 56 | 57 | t.is(await tl, undefined); 58 | }); 59 | 60 | test.cb(Tween.name + ': calling the callback (gsap native + then)', t => { 61 | t.plan(2); 62 | const tl = new Tween(); 63 | tl.eventCallback('onComplete', () => t.pass()); 64 | tl.then(() => { 65 | t.pass(); 66 | t.end(); 67 | }); 68 | 69 | tl.progress(1, false); // Mark tween as "done" 70 | }); 71 | 72 | test(Tween.name + ': calling the callback (gsap native + await)', async t => { 73 | t.plan(2); 74 | const tl = new Tween(); 75 | setTimeout(() => tl.progress(1, false)); // Mark tween as "done" 76 | 77 | let callCount = 0; 78 | tl.eventCallback('onComplete', () => t.is(++callCount, 1)); 79 | await tl; 80 | t.is(++callCount, 2); 81 | }); 82 | }); 83 | --------------------------------------------------------------------------------