├── LICENSE ├── README.md ├── ScrubGSAPTimeline.js ├── ScrubGSAPTimeline.min.js ├── ScrubGSAPTimeline_V3.min.js └── ScrubGSAPTimeline_v3.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Chris Gannon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScrubGSAPTimeline 2 | Allows granular scrubbing through a GreenSock timeline with the mouse to help with accurate animation timing and positioning. 3 | 4 | This script allows you to scrub through a GreenSock timeline simply by moving your mouse left to right. It also displays your timeline's current time. It's particularly useful if you create long timelines and need to quickly and accurately preview certain points in your animation. 5 | 6 | To use it: 7 | 8 | - Add the line `ScrubGSAPTimeline()`; and pass in the TimelineLite/Max instance. So if your instance is called umbrellaTimeline you would write: 9 | - `ScrubGSAPTimeline(umbrellaTimeline)`; 10 | - To let the timeline play automatically, move your mouse outside the document. 11 | - Moving your mouse off the document at, say, halfway along the X axis, will play the timeline from that percentage of the timeline (i.e. halfway through the timeline's duration). 12 | - Double click to pause scrubbing. Double click again to unpause. 13 | - Pass in multiple timeline instances in an array like this: 14 | - `ScrubGSAPTimeline([umbrellaTimeline, fadeUmbrellaTimeline])` 15 | 16 | **CodePen demo 17 | [here](http://codepen.io/chrisgannon/pen/zGmdBN)** 18 | 19 | *********************************** 20 | 21 | **Support for GSAP 3.0** 22 | 23 | Greensock 3.0 has some fundamental changes to the syntax — if you are working with the beta version of GSAP 3.0 please use the _v3_ file. 24 | 25 | https://github.com/chrisgannon/ScrubGSAPTimeline/blob/master/ScrubGSAPTimeline_v3.js 26 | -------------------------------------------------------------------------------- /ScrubGSAPTimeline.js: -------------------------------------------------------------------------------- 1 | window.ScrubGSAPTimeline = (tl) => { 2 | let mainTl = new TimelineLite(); 3 | if (Object.prototype.toString.call(tl) === '[object Array]') { 4 | let i = tl.length; 5 | while (--i > -1) { 6 | mainTl.add(tl[i], 0); 7 | } 8 | 9 | } else { 10 | mainTl = tl; 11 | } 12 | const Scrubber = (_tl) => { 13 | 14 | let mouseMove = (e) => { 15 | box.innerHTML = parseFloat(_tl.time()).toFixed(2); 16 | _tl.pause(); 17 | _tl.progress(e.clientX / (window.innerWidth)); // * tl.duration(); 18 | mouseX = e.clientX, mouseY = e.clientY; 19 | 20 | TweenLite.set(box, { 21 | x: (mouseX >= (window.innerWidth - 48)) ? mouseX - 48 : mouseX, 22 | y: (mouseY <= 20) ? mouseY + 20 : mouseY - 20 23 | }) 24 | }, 25 | mouseOver = (e) => { 26 | _tl.pause(); 27 | _tl.play(0); 28 | TweenLite.set(box, { 29 | autoAlpha: 1 30 | }) 31 | }, 32 | mouseOut = (e) => { 33 | _tl.play(e.clientX / (window.innerWidth) * _tl.duration()); 34 | TweenLite.set(box, { 35 | autoAlpha: 0 36 | }) 37 | }, 38 | box = document.createElement('div'), 39 | mouseX, mouseY, gGreen = '#53A018', 40 | cssText = "position:fixed;border-radius:4px;font-size:14px;padding:5px;user-select:none;pointer-events:none;text-align:center;color:#53A018;top:0;left:0;font-family:Helvetica, Arial, sans-serif;background-color:#262626;line-height:1 !important;z-index:99999999" 41 | box.style.cssText = cssText; 42 | 43 | document.body.appendChild(box); 44 | document.body.onmousemove = mouseMove; 45 | document.body.onmouseover = mouseOver; 46 | document.body.onmouseout = mouseOut; 47 | document.body.ondblclick = (e) => { 48 | if (!document.body.onmousemove) { 49 | TweenLite.to(box, 0.2, { 50 | color: gGreen 51 | }) 52 | document.body.onmousemove = mouseMove; 53 | document.body.onmouseover = mouseOver; 54 | document.body.onmouseout = mouseOut; 55 | return; 56 | } 57 | 58 | TweenLite.to(box, 0.2, { 59 | color: '#A31632' 60 | }) 61 | document.body.onmousemove = null; 62 | document.body.onmouseover = null; 63 | document.body.onmouseout = null; 64 | _tl.pause(); 65 | 66 | } 67 | 68 | 69 | } 70 | 71 | Scrubber(mainTl); 72 | 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /ScrubGSAPTimeline.min.js: -------------------------------------------------------------------------------- 1 | window.ScrubGSAPTimeline=function(a){var d=new TimelineLite;if("[object Array]"===Object.prototype.toString.call(a))for(var e=a.length;-1<--e;)d.add(a[e],0);else d=a;(function(b){var a=function(a){c.innerHTML=parseFloat(b.time()).toFixed(2);b.pause();b.progress(a.clientX/window.innerWidth);f=a.clientX;g=a.clientY;TweenLite.set(c,{x:f>=window.innerWidth-48?f-48:f,y:20>=g?g+20:g-20})},d=function(a){b.pause();b.play(0);TweenLite.set(c,{autoAlpha:1})},e=function(a){b.play(a.clientX/window.innerWidth* 2 | b.duration());TweenLite.set(c,{autoAlpha:0})},c=document.createElement("div"),f,g;c.style.cssText="position:fixed;border-radius:4px;font-size:14px;padding:5px;user-select:none;pointer-events:none;text-align:center;color:#53A018;top:0;left:0;font-family:Helvetica, Arial, sans-serif;background-color:#262626;line-height:1 !important;z-index:99999999";document.body.appendChild(c);document.body.onmousemove=a;document.body.onmouseover=d;document.body.onmouseout=e;document.body.ondblclick=function(f){document.body.onmousemove? 3 | (TweenLite.to(c,.2,{color:"#A31632"}),document.body.onmousemove=null,document.body.onmouseover=null,document.body.onmouseout=null,b.pause()):(TweenLite.to(c,.2,{color:"#53A018"}),document.body.onmousemove=a,document.body.onmouseover=d,document.body.onmouseout=e)}})(d)}; 4 | -------------------------------------------------------------------------------- /ScrubGSAPTimeline_V3.min.js: -------------------------------------------------------------------------------- 1 | window.ScrubGSAPTimeline=function(d){document.body.childNodes.forEach(function(a){gsap.set(a,{pointerEvents:"none"})});var g=gsap.timeline();if("[object Array]"===Object.prototype.toString.call(d))for(var h=d.length;-1<--h;)g.add(d[h],0);else g=d;(function(a){var k=function(c){b.innerHTML=parseFloat(a.time()).toFixed(2);a.pause();a.progress(c.clientX/window.innerWidth);e=c.clientX;f=c.clientY;gsap.set(b,{x:e>=window.innerWidth-48?e-48:e,y:20>=f?f+20:f-20})},l=function(c){a.pause();a.play(0);gsap.set(b, 2 | {autoAlpha:1})},m=function(c){a.play(c.clientX/window.innerWidth*a.duration());gsap.set(b,{autoAlpha:0})},b=document.createElement("div"),e,f;b.style.cssText="position:fixed;border-radius:4px;font-size:14px;padding:5px;user-select:none;pointer-events:none;text-align:center;color:#53A018;top:0;left:0;font-family:Helvetica, Arial, sans-serif;background-color:#262626;line-height:1 !important;z-index:99999999";document.body.appendChild(b);document.body.onmousemove=k;document.body.onmouseover=l;document.body.onmouseout= 3 | m;document.body.ondblclick=function(c){document.body.onmousemove?(gsap.to(b,{duration:.2,color:"#A31632"}),document.body.onmousemove=null,document.body.onmouseover=null,document.body.onmouseout=null,a.pause()):(gsap.to(b,{duration:.2,color:"#53A018"}),document.body.onmousemove=k,document.body.onmouseover=l,document.body.onmouseout=m)}})(g)}; 4 | -------------------------------------------------------------------------------- /ScrubGSAPTimeline_v3.js: -------------------------------------------------------------------------------- 1 | window.ScrubGSAPTimeline = (tl) => { 2 | 3 | document.body.childNodes.forEach(target => { 4 | if(target.style){ 5 | target.style.pointerEvents = 'none'; 6 | } 7 | }) 8 | let mainTl = gsap.timeline(); 9 | if (Object.prototype.toString.call(tl) === '[object Array]') { 10 | let i = tl.length; 11 | while (--i > -1) { 12 | mainTl.add(tl[i], 0); 13 | } 14 | 15 | } else { 16 | mainTl = tl; 17 | } 18 | const Scrubber = (_tl) => { 19 | 20 | let mouseMove = (e) => { 21 | 22 | box.innerHTML = parseFloat(_tl.time()).toFixed(2); 23 | _tl.pause(); 24 | _tl.progress(e.clientX / (window.innerWidth)); 25 | mouseX = e.clientX, mouseY = e.clientY; 26 | 27 | gsap.set(box, { 28 | x: (mouseX >= (window.innerWidth - 48)) ? mouseX - 48 : mouseX, 29 | y: (mouseY <= 20) ? mouseY + 20 : mouseY - 20 30 | }) 31 | }, 32 | mouseOver = (e) => { 33 | _tl.pause(); 34 | _tl.play(0); 35 | gsap.set(box, { 36 | autoAlpha: 1 37 | }) 38 | }, 39 | mouseOut = (e) => { 40 | _tl.play(e.clientX / (window.innerWidth) * _tl.duration()); 41 | gsap.set(box, { 42 | autoAlpha: 0 43 | }) 44 | }, 45 | box = document.createElement('div'), 46 | mouseX, mouseY, gGreen = '#53A018', 47 | cssText = "position:fixed;border-radius:4px;font-size:14px;padding:5px;user-select:none;pointer-events:none;text-align:center;color:#53A018;top:0;left:0;font-family:Helvetica, Arial, sans-serif;background-color:#262626;line-height:1 !important;z-index:99999999" 48 | box.style.cssText = cssText; 49 | 50 | document.body.appendChild(box); 51 | document.body.onmousemove = mouseMove; 52 | document.body.onmouseover = mouseOver; 53 | document.body.onmouseout = mouseOut; 54 | document.body.ondblclick = (e) => { 55 | if (!document.body.onmousemove) { 56 | gsap.to(box, { 57 | duration: 0.2, 58 | color: gGreen 59 | }) 60 | document.body.onmousemove = mouseMove; 61 | document.body.onmouseover = mouseOver; 62 | document.body.onmouseout = mouseOut; 63 | return; 64 | } 65 | 66 | gsap.to(box, { 67 | duration: 0.2, 68 | color: '#A31632' 69 | }) 70 | document.body.onmousemove = null; 71 | document.body.onmouseover = null; 72 | document.body.onmouseout = null; 73 | _tl.pause(); 74 | 75 | } 76 | 77 | 78 | } 79 | 80 | //console.log(document.body.childNodes) 81 | 82 | Scrubber(mainTl); 83 | 84 | } 85 | --------------------------------------------------------------------------------