Smashing Conf Freiburg
This pen.
This pen.
Title and associative aria tags:
<svg aria-labelledby="title"
id="svg"
role="presentation"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 765 587">
<title id="title"
lang="en">
Icons that illustrate Global Warming Solutions
</title>
Title for elements in the SVG DOM
Role to let the screen reader know whether to traverse
This resource, with support charts.
This article by Heather Migliorisi.
This pen.
From this CSS-Tricks Article
This pen.
Ugly storyboards save you time!
CSS/SCSS
GSAP (GreenSock)
React-Motion
This pen.
<StaggeredMotion
defaultStyles = {arr}
styles={this.getStyles}>
{lines =>
<div className="demo">
{lines.map(({x, y, rotate}, i) =>
<div
key={i}
className={`playthings s${i}`}
// we have to subtract half the amount of $amt in the
//CSS panel so that the mouse stays in the center of
//the object we're creating
style={{
WebkitTransform: `translate3d(${x - amtHalf}px, ${y - amtHalf}px, 0) rotate(${rotate}deg)`,
transform: `translate3d(${x - amtHalf}px, ${y - amtHalf}px, 0) rotate(${rotate}deg)`
}} />
)}
</div>
}
</StaggeredMotion>
This pen.
const App = React.createClass({
getInitialState: function() {
return {
score: 500,
startTime: 0,
finalScore: 0
};
},
_startGame: function(setGameToStart) {
this.setState({ isPlaying: setGameToStart,
score: 500,
startTime: Date.now()
});
},
_updateScore: function(scoreDelta) {
let score = Math.min(Math.max(0, this.state.score + scoreDelta), 1270);
this.setState({ score: score });
if (score === 0 || score === 1270) {
this.setState({ isPlaying: false,
finalScore: 10000 - Math.round((Date.now() - this.state.startTime) / 30) });
}
},
...
render() {
return (
<div>
...
<HeartMeter score={this.state.score}/>
...
</div>
)
}
});
const HeartMeter = React.createClass({
render() {
return (
<div>
<svg className="heartmeter" xmlns="http://www.w3.org/2000/svg" width="250" height="50" viewBox="0 0 1741.8 395.6">
<path d="M1741.8 197.7c0 109.3-89 197.8-198.8 197.8a198.6 198.6 0 0 1-158.5-78.4H11.2A11.2 11.2 0 0 1 0 305.9V89.5a11.2 11.2 0 0 1 11.2-11.1h1373.4A198.8 198.8 0 0 1 1543 0c109.8 0 198.8 88.5 198.8 197.7z" fill="#000"/>
<path d="M1591.8 127c-18.3 0-34.1 14.8-41.4 30.3-7.3-15.5-23.1-30.3-41.4-30.3a45.7 45.7 0 0 0-45.7 45.5c0 51.1 51.8 64.5 87.1 115.1 33.4-50.2 87.1-65.6 87.1-115.1a45.7 45.7 0 0 0-45.7-45.5z" fill="#b29968"/>
<rect x="68.2" y="140.8" width={this.props.score} height="101.55" fill="#9391aa"/>
</svg>
</div>
)
}
});
Request Animation Frame
(function getCoords() {
let tCoords = tC1.getBoundingClientRect(),
txCoords = txC1.getBoundingClientRect(),
mCoords = mC1.getBoundingClientRect(),
elCoords = ele1.getBoundingClientRect();
function intersectRect(a, b) {
return Math.max(a.left, b.left + 40) < Math.min(a.right, b.right - 40) &&
Math.max(a.top, b.top + 40) < Math.min(a.bottom, b.bottom - 40);
}
// can't do if/else because sometimes they both come out at once
// and one of them will be ignored
if (intersectRect(tCoords, elCoords)) {
getHitTestIncrease();
}
if (intersectRect(txCoords, elCoords)) {
getHitTestDecrease();
}
if (intersectRect(mCoords, elCoords)) {
getHitTestMargarita();
}
requestAnimationFrame(getCoords);
}());
Bubble.prototype = {
init: function (x, y, vx, vy) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
},
update: function (dt) {
// friction opposes the direction of velocity
var acceleration = -Math.sign(this.vx) * friction;
// distance = velocity * time + 0.5 * acceleration * (time ^ 2)
this.x += this.vx * dt + 0.5 * acceleration * (dt ^ 2);
this.y += this.vy * dt + 0.5 * gravity * (dt ^ 2);
// velocity + acceleration * time
this.vy += gravity * dt;
this.vx += acceleration * dt;
this.circ.setAttribute("cx", this.x);
this.circ.setAttribute("cy", this.y);
this.circ.setAttribute("stroke", "rgba(1,146,190," + this.opacity + ")");
}
};
BT Dubs, this is d3 under the hood
(function animate(currentTime) {
var dt;
requestAnimationFrame(animate);
...
for (var i = 0; i < particles.length; i++) {
particles[i].update(dt);
...
}
}());
GSAP for Animation
_flyBy: function(el, amt, name, delay) {
if (this.props._isGameOver()) return;
...
TweenMax.fromTo(el, amt, {
rotation:0,
y:randY,
x:window.innerWidth + 200
}, {
x: -200,
y:randY,
rotation: 360,
delay: delay,
onComplete:this._flyBy,
onCompleteParams:[el, amt, name, delay],
ease: Power1.easeInOut
});
},
This pen.
Bad transform origin bug on rotation, now solved in Firefox.
More in this CSS-Tricks article.
Chrome
IE
Firefox
Safari (zoomed)
The issue with longer CSS animations:
This pen.
_hitTestIncrease: function() {
if (!this.state.tacoPassed && this.props.isPlaying) {
//animation for wowie
let inWow = this.refs.inWow,
tl = new TimelineLite();
audioIncrease.play();
tl.fromTo(inWow, 0.4, {
autoAlpha: 0,
scale: 0.5
}, {
autoAlpha: 1,
scale: 1,
ease: Power4.easeOut
});
tl.to(inWow, 0.2, {
autoAlpha: 0,
scale: 0.5,
ease: Power2.easeIn
}, "+=0.3");
this.setState({ tacoPassed: true });
this.props._updateScore(75);
}
},
Plugin for GSAP, very simple code.
TweenMax.staggerFromTo($draw, 4,{ drawSVG:'0' }, { drawSVG: true }, 0.1);
Done with stroke-dasharray and stroke-dashoffset
@keyframes dash {
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -274;
}
}
This pen.
This pen.
TweenMax.to($firefly1, 6, {
bezier: {
type:"soft",
values:[{x:10, y:30}, {x:-30, y:20}, {x:-40, y:10},
{x:30, y:20}, {x:10, y:30}],
autoRotate:true
},
ease:Linear.easeNone, repeat:-1}, "start+=3");
function displayVals() {
//get the value from the dropdown
var singleValues = $("#single").val();
//the timeline for the changing animation
tl.to($('.s2'), 1.75, {
rotation: 360,
bezier: {
type: 'thru',
values: bezier,
curviness: singleValues
},
ease: Power1.easeInOut
});
}
displayVals();
This pen.
.container
-(1..3).each do |i|
%div{:class => ['unit', "unit#{i}"]}
.shape
.face.bm
.face.tp
-(1..14).each do |i|
%div{:class => ['face', 'side', "s#{i}"]}
HAML
@mixin colors($max-count, $color-frequency){
$color: 360/$max-count;
@for $i from 1 through $max-count {
.s#{$i} {
background: hsl(90%, ($i - 1)*($color / $color-frequency), 40%);
}
}
}
SASS
var bP = $(".face"),
unit = $(".unit"),
tl = new TimelineLite();
tl.add("start");
tl.staggerFrom(bP, 3, {
cycle:{
backgroundColor:["#00a99d", "#0072bc", "#2e3192"],
y:[1000, -500],
x:[500, -500],
opacity:[0.9, 0.7, 0.5],
rotation: function(i) {
return i * 25
}
},
rotation: 360,
ease:Circ.easeInOut
}, 0.006, "start");
JS
This pen.
//button hue
function hued() {
var ch1 = "hsl(+=110%, +=0%, +=0%)",
tl = new TimelineMax({
paused: true
});
tl.add("hu");
tl.to(mult, 1.25, {
fill: ch1
}, "hu");
...
tl.to(body, 1.25, {
backgroundColor: ch1
}, "hu");
return tl;
}
var hue = hued();
This pen.
Draggable.create($gear, {
type: "rotation",
bounds: {
minRotation: 0,
maxRotation: 360
},
onDrag: function() {
master.progress((this.rotation)/360 );
}
});
Draggable.create(features, {
edgeResistance: 0.65,
type: "x,y",
throwProps: true,
autoScroll: true
});
This pen.
This pen.
Start with this technique from Joe Harrison
This pen.
Compare to using text with photos to illustrate an article.
This pen.
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
});
...
//this button kicks off the panda painting timeline
$("#button").on("click", function(e) {
e.preventDefault();
triggerPaint.restart();
});
...
TweenMax.to("#start", 1, {morphSVG:{shape:"#end"},
ease:Linear.easeNone});
TweenMax.to("#start", 1, {morphSVG:{shape:"#end",
shapeIndex:"1"}});
This pen.
MorphSVGPlugin.convertToPath("ellipse");
function flame() {
var tl = new TimelineMax();
tl.add("begin");
tl.fromTo(blurNode, 2.5, {
attr: {
stdDeviation: 9
}
}, {
attr: {
stdDeviation: 3
}
}, "begin");
var num = 9;
for (var i = 1; i <= num; i++) {
tl.to(fStable, 1, {
morphSVG: {
shape: "#f" + i
},
opacity: ((Math.random() * 0.7) + 0.7),
ease: Linear.easeNone
}, "begin+=" + i);
}
By Blake Bowen
function solve(data) {
var size = data.length;
var last = size - 4;
var path = "M" + [data[0], data[1]];
for (var i = 0; i < size - 2; i +=2) {
var x0 = i ? data[i - 2] : data[0];
var y0 = i ? data[i - 1] : data[1];
var x1 = data[i + 0];
var y1 = data[i + 1];
var x2 = data[i + 2];
var y2 = data[i + 3];
var x3 = i !== last ? data[i + 4] : x2;
var y3 = i !== last ? data[i + 5] : y2;
var cp1x = (-x0 + 6 * x1 + x2) / 6;
var cp1y = (-y0 + 6 * y1 + y2) / 6;
var cp2x = (x1 + 6 * x2 - x3) / 6;
var cp2y = (y1 + 6 * y2 - y3) / 6;
path += "C" + [cp1x, cp1y, cp2x, cp2y, x2, y2];
}
return path;
}
Article about history in computer science.
var poly = document.querySelector("polyline");
var path = document.querySelector("path");
var points = [
100,350,
200,100,
300,350,
400,150,
500,350,
600,200,
700,350
];
poly.setAttribute("points", points);
path.setAttribute("d", solve(points));
var points = [
100,350,
200,150,
300,350,
400,120,
500,350,
600,180,
700,350
];
var points = [
100,350,
200,100,
300,350,
400,150,
500,350,
600,200,
700,350
];
This pen.
This pen.
This pen.
var graphic = document.getElementById("graphic");
function moveChart(origin, end) {
$(origin).on("click", function() {
var moveTo = document.getElementById(end);
var s = moveTo.getBBox();
var amt = 75;
newView = "" + (s.x - amt) + " " + (s.y - amt) + " " + (s.width + amt*2) + " " + (s.height + amt*2);
TweenMax.to(graphic, 1.5, {
attr: { viewBox: newView },
ease:Circ.easeOut
});
});
}
moveChart(".start-yes", "variation");
moveChart(".start-no", "delorean");
//balloon
function balloonGrow(){
var balloon = $("#balloon")[0];
var radius = balloon.getAttribute("r");
var cySet = balloon.getAttribute("cy");
balloon.setAttribute('r', parseInt(radius) + 10);
balloon.setAttribute('cy', parseInt(cySet) - 10);
if (parseInt(radius) > 125) {
ion.sound.play("balloon-pop3");
var balloonTl = new TimelineMax();
balloonTl.add("pop");
balloonTl.to("#balloon", 0.05, {
scale: 5,
opacity: 0,
transformOrigin: "50% 50%",
ease: Circ.easeOut
}, "pop");
...
setTimeout(function(){
balloon.setAttribute("r", "45");
balloon.setAttribute("cy", "233.5");
}, 200);
}
}
//desktop keyboard
$(document).keydown(function(e) {
switch(e.which) {
case 70: // f
//balloon
balloonGrow();
ion.sound.play("hit4");
break;
...
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent scrolling with arrows
});
This pen.
SVG Immersion Podcast and Web Animation Weekly
Launching this week!
🎉
These Slides:
slides.com/sdrasner/innovative