This diagram takes the Accelerate, Then Coast easing function and reflects it to apply slow-out. The reflection center r is configurable. With r = .5, the reflection is equivalent to the standard “in-out” symmetric easing; with r = 0, only slow-out is applied, and with r = 1, only slow-in is applied.
Last active
February 9, 2016 01:56
-
-
Save mbostock/10343037 to your computer and use it in GitHub Desktop.
Accelerate, Then Coast II
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
width: 960px; | |
height: 500px; | |
position: relative; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.axis text { | |
font: 10px sans-serif; | |
} | |
.line { | |
fill: none; | |
stroke: #000; | |
stroke-linecap: round; | |
} | |
line { | |
stroke: #aaa; | |
shape-rendering: crispEdges; | |
} | |
#form { | |
position: absolute; | |
bottom: 27px; | |
right: 50px; | |
} | |
input { | |
width: 140px; | |
} | |
span { | |
position: relative; | |
top: -3px; | |
} | |
output { | |
display: inline-block; | |
width: 3.5em; | |
} | |
</style> | |
<body> | |
<div id="form"> | |
<div id="acceleration"> | |
<input type="range" min="0" max="1" step=".01" value=".55"> | |
<span><i>a</i> = <output name="acceleration"></output></span> | |
</div> | |
<div id="reflection"> | |
<input type="range" min="0" max="1" step=".01" value=".5"> | |
<span><i>r</i> = <output name="reflection"></output></span> | |
</div> | |
</div> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script> | |
var margin = {top: 40, right: 340, bottom: 40, left: 200}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
var color = d3.scale.category20c(); | |
var formatNumber = d3.format(".4f"); | |
var x = d3.scale.linear() | |
.domain([0, 1]) | |
.range([0, width]); | |
var y = d3.scale.linear() | |
.domain([0, 1]) | |
.range([height, 0]); | |
var continuousLine = (function() { | |
var samples = d3.range(0, 1 + 1e-6, 2 / width); | |
var discreteLine = d3.svg.line() | |
.x(function(d, i) { return x(samples[i]); }) | |
.y(function(d) { return y(d); }); | |
return function(f) { | |
return discreteLine(samples.map(f)); | |
}; | |
})(); | |
var ease; | |
var svg = d3.select("body").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
svg.append("g") | |
.attr("class", "axis axis--x") | |
.attr("transform", "translate(0," + height + ")") | |
.call(d3.svg.axis() | |
.scale(x) | |
.orient("bottom")) | |
.append("text") | |
.attr("x", width) | |
.attr("y", -3) | |
.attr("dy", "-.35em") | |
.style("font-weight", "bold") | |
.style("text-anchor", "middle") | |
.text("time"); | |
svg.append("g") | |
.attr("class", "axis axis--y") | |
.call(d3.svg.axis() | |
.scale(y) | |
.orient("left")) | |
.append("text") | |
.attr("x", 6) | |
.attr("dy", ".35em") | |
.style("font-weight", "bold") | |
.text("ease(time)"); | |
var lineEase = svg.append("path") | |
.attr("class", "line") | |
.style("stroke", "#000") | |
.style("stroke-width", "1.5px"); | |
var timeReference = svg.append("line").attr("y1", height), | |
easeReference = svg.append("line"); | |
var circle = svg.append("circle") | |
.attr("r", 4.5) | |
.attr("cx", 0) | |
.attr("cy", height); | |
var accelerationInput = d3.select("#acceleration input").on("input", reset), | |
accelerationOutput = d3.select("#acceleration output"), | |
reflectionInput = d3.select("#reflection input").on("input", reset), | |
reflectionOutput = d3.select("#reflection output"); | |
var accelerationInputScale = d3.scale.pow() | |
.exponent(4) | |
.domain([0, 1]) | |
.range([1, 10]); | |
reset(); | |
!function loop() { | |
circle.transition() | |
.ease("linear") | |
.duration(5000) | |
.tween("transform", function() { | |
return function(t) { | |
circle.attr("cx", x(t)).attr("cy", y(ease(t))); | |
timeReference.attr("x1", x(t)).attr("x2", x(t)).attr("y2", y(ease(t))); | |
easeReference.attr("x2", x(t)).attr("y1", y(ease(t))).attr("y2", y(ease(t))); | |
}; | |
}) | |
.each("end", loop); | |
}(); | |
function reset() { | |
var acceleration = accelerationInputScale(accelerationInput.property("value")), | |
reflection = reflectionInput.property("value"); | |
accelerationOutput.text(formatNumber(acceleration)); | |
reflectionOutput.text(formatNumber(reflection)); | |
ease = easeReflect(easeAccelerateThenCoast(acceleration), reflection); | |
lineEase.attr("d", continuousLine(ease)); | |
} | |
// Like in-out reflection, except around the specified center. | |
// If center = .5, this is the same as in-out reflection. | |
function easeReflect(ease, center) { | |
return function(t) { | |
return t < center ? center * ease(t / center) : 1 - ease((1 - t) / (1 - center)) * (1 - center); | |
}; | |
} | |
// Constant acceleration followed by constant speed. | |
// If acceleration = 1, this is the same as quadratic easing. | |
// If acceleration = Infinity, this is the same as linear easing. | |
function easeAccelerateThenCoast(acceleration) { | |
if (acceleration < 1) throw new Error("unpossible"); | |
if (!isFinite(acceleration)) return d3.ease("linear"); | |
var speed = 2 * (acceleration - Math.sqrt((acceleration - 1) * acceleration)), | |
t0 = speed / 2 / acceleration; | |
return function(t) { | |
return t < t0 ? acceleration * t * t : speed * t - speed + 1; | |
}; | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment