Stay tuned...
More information in this CSS-Tricks article
Historically bad transform origin bug on rotation in CSS,
now about to be resolved in Firefox.
More in this CSS-Tricks article.
Chrome
IE
Firefox
Safari (zoomed)
Other neat GSAP goodies to come...
TweenLite/TweenMax .to/.from/.fromTo ( variable, seconds { property:amount, ease });
TweenLite.to("div", 2, {
scaleY:0.75, scaleX:1.25, y:100, opacity:0.75, ease:Elastic.easeOut
});
For help with easing: GSAP's Ease Visualizer
.css
%h2 CSS
- @x = 5
- @x.times do
.bar
.sass
%h2 SASS
- @x = 5
- @x.times do
.bar
.gsap
%h2 GSAP
- @x = 5
- @x.times do
.bar
@keyframes staggerFoo {
to {
background: orange;
transform: rotate(90deg);
}
}
.css .bar:nth-child(1) { animation: staggerFoo 1s 0.1s ease-out both; }
.css .bar:nth-child(2) { animation: staggerFoo 1s 0.2s ease-out both; }
.css .bar:nth-child(3) { animation: staggerFoo 1s 0.3s ease-out both; }
.css .bar:nth-child(4) { animation: staggerFoo 1s 0.4s ease-out both; }
.css .bar:nth-child(5) { animation: staggerFoo 1s 0.5s ease-out both; }
.css .bar:nth-child(6) { animation: staggerFoo 1s 0.5s ease-out both; }
@keyframes staggerFoo {
to {
background: orange;
transform: rotate(90deg);
}
}
@for $i from 1 through 6 {
.sass .bar:nth-child(#{$i} ) {
animation: staggerFoo 1s ($i * 0.1s) ease-out both;
}
}
TweenMax.staggerTo(".gsap .bar", 1, {
backgroundColor: "orange",
rotation: 90,
ease: Sine.easeOut
}, 0.1);
Things like stagger are really complicated to do in CSS animation, and in GSAP are one line of code.
TweenMax.staggerTo(".squares", 2, {
y:100, backgroundColor:"#4f9d88", ease:Elastic.easeOut
}, 0.05);
TweenMax.staggerTo(".squares", 2, {
rotation:200, delay:1, scale:0.5, backgroundColor:"#72b165", ease:Elastic.easeOut
}, 0.025);
David Walsh Blog GSAP + SVG Article
tl.staggerFrom(bP, 3, {
cycle:{
fill:["white", "yellow", "#e23e0c"],
opacity:[0.8, 0.2, 0.5, 0.3],
},
ease:Elastic.easeOut
}, 0.001);
TweenMax.set($(".squares"), {
css:{transformPerspective:200, perspective:200, transformStyle:"preserve-3d"}
});
TweenMax.to($(".squares"), 2.5, {
css:{rotationX:230, z:-150}, y:180, opacity:0.2, ease:Power2.easeOut
}, "+=0.2");
This pen.
var tl = new TimelineLite();
//you can add a relative label (optional)
tl.add("start");
//or just add to the timeline without the label
tl.staggerFrom(bP, 3, {
cycle:{
fill:["white", "yellow", "#e23e0c"],
opacity:[0.8, 0.2, 0.5, 0.3],
},
ease:Elastic.easeOut
}, 0.001);
//set properties needed for animation
TweenMax.set($t1, {
perspective: 400
});
// the first scene
function sceneOne() {
var tl = new TimelineMax();
tl.add("label");
tl.to(elem, 1, {vars}, "label");
tl.timeScale(1.2);
return tl;
}
// Create a master timeline
var master = new TimelineMax({options});
// Add the scene function to the master
master.add(sceneOne(), "labelOnMaster");
//use this while you're working to get to a place in time
//master.seek("labelOnMaster+=2");
//set back in js
TweenMax.set(".foo", {
visibility: "visible"
});
//set to hide in CSS
.foo { visibility: hidden; }
var tl = new TimelineLite();
tl.to(".orange", 1, {x:750})
//this just follows the first
.to(".green", 1, {x:750})
//there is a one second gap between these two tweens
.to(".blue", 1, {x:750}, "+=1")
//this goes to two seconds in
.to(".red", 1, {x:750}, "2")
// add a relative label at this part of the timeline
.add("newLabel")
// tween at 3 seconds past the relative label
.to(".purple", 1, {x:750}, "newlabel+=3");
This pen courtesy of GreenSock.
var playBtn = document.getElementById("play"),
tl = new TimelineMax({repeat:1, yoyo:true, paused:true});
tl.staggerTo(".box", 0.5, {x:"100%"}, 0.4);
play.onclick = function() {
tl.restart();
}
Take an SVG, animate it with GSAP, and make it responsive. Use a timeline and the position parameter or labels.
There are starter pens for reference in the repo:
Pieces of the UI move to aid in an informative UX choreography. This can typically be done with well placed CSS, some JS to trigger interactions. Responsive can be achieved with good CSS media queries.
From this CSS Animation Rocks
Another good navigation example at Concrete Matter
Used to illustrate concepts in the body of the page, either alongside or on it's own. Most of the basis of this talk is complex standalone animation. JavaScript is recommended for much longer implementations (explained later).
This pen.
This pen.
Adhering to branding means solving the problem for your users.
Very easy to implement. SVG stays a consistent size.
Since jQuery 3.0, we can now do class operations on SVG. You can also do so with vanilla JS.
$("#close").on("click", function() {
if ($(this).hasClass("contactOpen")) {
contactOut.restart();
} else {
master.reverse();
master.timeScale(1.8);
}
});
animationstart
animationiteration
animationend
var lil = $(".lilguy"),
logIt = $(".logIt"),
log = $(".term-content");
logIt.on('click', function() {
lil.addClass('restart');
});
lil.on('animationstart webkitAnimationStart oanimationstart MSAnimationStart',
function() {
log.empty();
log.append("<p>Animation has started.</p>");
});
lil.on('animationiteration webkitAnimationIteration oanimationiteration MSAnimationIteration',
function() {
log.append("<p>An iteration fired.");
});
lil.on('animationend webkitAnimationEnd oanimationend MSAnimationEnd',
function() {
log.append("<p>Animation has completed.</p>");
lil.removeClass("restart");
});
tl.pause(); // Pause timeline
tl.resume(); // Continue playback
tl.restart(); // Restart the timeline
tl.play(X); // Play from Xs
tl.play(-X); // Play Xs from end
tl.seek(X); // Go to Xs or 'label'
tl.reverse(); // Reverse playback anytime
tl.timeScale(x); // Speed up/slow down timeline
tl.progress(0.5); // Skip to halfway
Also in the repo.
//variable declaration for the global repeated animations
var gear = $("#gear1, #gear2, #gear3");
...
//animation that's repeated for all of the sections
function revolve() {
var tl = new TimelineMax();
tl.add("begin");
tl.to(gear, 4, {
transformOrigin: "50% 50%",
rotation: 360,
ease: Linear.easeNone
}, "begin");
...
return tl;
}
var repeat = new TimelineMax({repeat:-1});
repeat.add(revolve());
function paintPanda() {
var tl = new TimelineMax();
tl.to(lh, 1, {
scaleY: 1.2,
rotation: -5,
transformOrigin: "50% 0",
ease: Circ.easeOut
}, "paintIt+=1");
...
return tl;
}
//create a timeline but initially pause it so that we can control it via click
var triggerPaint = new TimelineMax({
paused: true
});
triggerPaint.add(paintPanda());
//this button kicks off the panda painting timeline
$("#button").on("click", function(e) {
e.preventDefault();
triggerPaint.restart();
});
...
var master = new TimelineMax();
master.add(sceneOne(), "scene1")
.add(sceneTwo(), "scene2")
.add(scThree, "scene3");
//jQueryUI Slider
function updateSlider(){
$slider.slider("value", scThree.progress() * 100);
}
$slider.slider({
range: false,
min: 0,
max: 100,
step:.1,
slide: function ( event, ui ) {
scThree.pause();
scThree.progress( ui.value/100 );
}
});
Draggable.create(".box", {type:"x,y", edgeResistance:0.65,
bounds:"#container", throwProps:true});
Draggable.create(circ, {
type: "x,y",
edgeResistance: 0.5,
bounds: "#container",
throwProps: true
});
TweenMax.staggerTo(circ, 3, {
rotation: 360,
scale: 0.85,
repeat: -1,
yoyo: true,
transformOrigin: "50% 50%",
ease: Elastic.easeInOut
}, 0.35);
Callbacks:
"this" refers to the Draggable instance itself, so you can easily access its "target" or bounds.
If you prefer event listeners, Draggable dispatches events:
yourDraggable.addEventListener("dragend", yourFunc);
var droppables = $(".box");
var overlapThreshold = "50%";
//we'll call onDrop() when a Draggable is dropped on top of one of the "droppables"
function onDrop(dragged, dropped) {
TweenMax.fromTo(dropped, 0.1, {opacity:1}, {opacity:0, repeat:3, yoyo:true});
}
Draggable.create(droppables, {
bounds:window,
onDrag: function(e) {
var i = droppables.length;
while (--i > -1) {
if (this.hitTest(droppables[i], overlapThreshold)) {
$(droppables[i]).addClass("highlight");
} else {
$(droppables[i]).removeClass("highlight");
}
}
},
onDragEnd:function(e) {
var i = droppables.length;
while (--i > -1) {
if (this.hitTest(droppables[i], overlapThreshold)) {
onDrop(this.target, droppables[i]);
}
}
}
});
jQuery 3.0
TweenMax.set($("#flowers1, #radio, #burst, #magnolia, #flowers2, #starfish, #berries1, #berries2, #berries3, #skulls, #tv, #glitch, #shadow, #lights"), {
visibility: "visible"
});
// the first scene
function sceneOne() {
var tl = new TimelineMax();
tl.add("start");
tl.staggerFromTo($f1, 0.3, {
scale: 0
}, {
scale: 1,
ease: Back.easeOut
}, 0.05, "start");
...
return tl;
}
var master = new TimelineMax({paused:true});
master.add(sceneOne(), "scene1");
Draggable.create($gear, {
type: "rotation",
bounds: {
minRotation: 0,
maxRotation: 360
},
onDrag: function() {
master.progress((this.rotation)/360 );
}
});
TweenMax.set(cow, {
svgOrigin:"321.05, 323.3",
rotation:50
});
Problem with hitTest() for SVG
<defs>
<ellipse id="SVGID_3_" class="st2" cx="276" cy="147" rx="272" ry="147"/>
</defs>
<clipPath id="SVGID_4_">
<use xlink:href="#SVGID_3_" />
</clipPath>
.st4 {
clip-path: url(#SVGID_4_);
}
SVG
CSS
<defs>
<clipPath id="clippy">
<ellipse cx="276" cy="147" rx="272" ry="147"/>
</clipPath>
</defs>
.img {
clip-path: url(#clippy);
}
SVG
CSS
-webkit-clip-path: ellipse(25% 35% at 50% 36%);
clip-path: ellipse(25% 35% at 50% 36%);
Source: MDN
Take the your last animation and make it interactive with any of the techniques we just covered.