// Illustration of how the `linear` timing function works when translating an element (the box on the right) from the initial position (at the `0%` keyframe) to a final position (at the `100%` keyframe) `8em` lower. You can drag the slider, the big dot on the figure and the orange box. Oh, and you can hide the controls.
- var d = 2000;
- var s = -.15*d;
- var dt = .5*d;
- var dd = .8*dt;
- var k = 0;
- var cd = k*dd;
- var ct = k*dt;
- var l = .05*d;
- var b = 1.325*dt;
svg(viewbox=`${s} ${.5*s} ${.9*d} ${.6*d}`)
defs
circle#c(r=.1*l)
g#box(transform=`translate(${b})`)
rect(x=-l y=-l width=2*l height=2*l)
use(xlink:href='#c')
clipPath#cp
rect(x=2 y=2 width=b height=b)
g#box-states
use#ini.box-ghost(xlink:href='#box')
use#fin.box-ghost(xlink:href='#box' y=dd)
g.guides
line(x1=dt x2=b)
line(x1=dt x2=dt y2=dd)
line(y1=dd x2=b y2=dd)
g#coord-syst
path(d=`M0,0L${dt + l},0`)
text.var.t(x=(dt + l) y=-.5*l) t
path(d=`M0,0L0,${dd + l}`)
text.var.l(y=(dd + l) x=-.5*l) y
text.code.t(y=-.5*l) 0%
text.code.l(x=-.5*l) 0em
use(xlink:href='#c' x=dt)
text.code.t(x=dt y=-.5*l) 100%
use(xlink:href='#c' y=dd)
text.code.l#max-css(y=dd x=-.5*l) 8em
g#graph
line(x2=dt y2=dd)
use(xlink:href='#c')
use#max(xlink:href='#c' x=dt y=dd)
g#curr
use#bmov(xlink:href='#box' y=cd)
g(clip-path='url(#cp)')
g(transform=`translate(${-dt} ${-dd})`)
g#gmov(transform=`translate(${ct} ${cd})`)
g.guides
line(x1=dt x2=dt y2=dd)
line(y1=dd x2=2*d y2=dd)
line#progress(x2=dt y2=dd)
g#gmov-x(transform=`translate(${ct})`)
rect(x=-.625*l y=-.95*l width=1.25*l height=.7*l)
use(xlink:href='#c')
text.code.t#lbl-t(y=-.5*l) #{~~(k*100)}%
g#gmov-y(transform=`translate(0 ${cd})`)
rect(x=-2.25*l y=-.35*l width=1.875*l height=.7*l)
use(xlink:href='#c')
text.code.l#lbl-y(x=-.5*l) #{~~(k*8)}em
g#trig(transform=`translate(${ct} ${cd})`)
circle(r=.225*l)
use(xlink:href='#c')
form
button toggle controls
input(type='checkbox' id='auto')
label(for='auto') autorun
input(type='range' id='k' value=`${~~(k*100)}`)
label(for='k') #{~~(k*100)}%
View Compiled
@import 'compass/css3';
$theme-base: #e18728;
$theme-hl: #be4c39;
$theme-ll: #ffd86e;
$a: 3;
$b: 2;
body { margin: 0; }
svg { @include proportional-box($a, $b); }
* { vector-effect: non-scaling-stroke; }
path, line { stroke-width: 2; }
text {
font: 7.5vmin comic sans ms, sans-serif;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
@media (min-width: 1200px)
and (min-height: 800px) {
font-size: 4.5vmin;
}
}
.var { font-style: italic; }
.code { font-family: ubuntu mono, courier, monospace; }
.t { text-anchor: middle; }
.l {
dominant-baseline: middle;
text-anchor: end;
}
[id='box'] {
use {
fill: #ccc;
stroke: #000;
stroke-width: 2;
}
}
.box-ghost { fill: #ededed; }
[id='bmov'] {
fill: $theme-base;
cursor: pointer;
}
.guides {
stroke: #ccc;
stroke-dasharray: 5;
}
[id='coord-syst'] {
path { stroke: #000; }
use { stroke: none; fill: #444; }
}
[id='graph'] {
stroke: $theme-ll;
use {
stroke: none;
&:first-of-type { fill: $theme-base; }
}
}
[id='curr'] {
rect { fill: $theme-base; }
text {
fill: #fff;
font-weight: 700;
}
}
[id='progress'] {
stroke: $theme-base;
stroke-width: 4;
}
[id='trig'] {
stroke: $theme-hl;
stroke-width: 3;
cursor: pointer;
> circle {
fill: rgba(#fff, .1);
opacity: .5;
}
use { fill: $theme-base; }
&:hover > circle {
animation: pulse .25s infinite alternate;
}
}
@keyframes pulse { to { opacity: .99; } }
/* =============== CONTROLS =============== */
$ctrl-bg: #262626;
$ctrl-ll: #aaa;
$ctrl-hl: $theme-base;
$track-w: 13em;
$track-h: .25em;
$thumb-d: 1.25em;
$check-d: 1.25em;
@mixin track() {
width: $track-w; height: $track-h;
background: $ctrl-ll;
}
@mixin fill() {
background: $ctrl-hl;
}
@mixin thumb() {
border: none;
width: $thumb-d; height: $thumb-d;
border-radius: 50%;
background: $ctrl-hl;
}
form {
position: fixed;
bottom: 0; left: 50%;
padding: .5em;
transform: translate(-50%);
background: $ctrl-bg;
color: $ctrl-ll;
font: 1em/1.375em trebuchet ms, sans serif;
transition: .35s;
}
form.hidden {
transform: translate(-50%, 100%);
}
button {
position: absolute;
right: 0; bottom: 100%; left: 0;
border: none;
width: 100%;
border-radius: .5em .5em 0 0;
background: $ctrl-ll;
color: $ctrl-bg;
text-indent: -200vw;
transition: inherit;
cursor: pointer;
&:hover {
color: $ctrl-hl;
}
&:before {
$tl: 1em;
$th: $tl*sqrt(3)/2;
position: absolute;
top: 50%; left: 50%;
border: solid 0 transparent;
border-width: $th .5*$tl 0 .5*$tl;
border-top-color: currentColor;
transform: translate(-50%, -50%);
transition: inherit;
content: '';
.hidden & {
transform: translate(-50%, -50%)
rotate(180deg);
}
}
}
input[type='checkbox'] {
transform: translate(-100vw);
+ label {
display: inline-block;
position: relative;
color: $ctrl-ll;
line-height: 2;
cursor: pointer;
&:hover {
color: mix($ctrl-ll, $ctrl-hl);
}
&:before {
box-sizing: border-box;
position: absolute;
top: 50%; right: 100%;
margin: -$check-d/2 $check-d/4;
border: solid .125em $ctrl-ll;
padding: .25em;
width: $check-d; height: $check-d;
color: transparent;
font: 900 1em/.5 sans-serif;
text-indent: -.125em;
transition: .3s ease-out;
content: '✓';
}
}
&:focus + label {
color: mix($ctrl-ll, $ctrl-hl);
}
&:checked + label {
&, &:before { color: $ctrl-hl; }
}
}
input[type='range'] {
&,
&::-webkit-slider-runnable-track,
&::-webkit-slider-thumb {
-webkit-appearance: none;
}
display: block;
margin: 1em auto 0;
padding: 0;
width: $track-w; height: 2em;
background: none;
font-size: 1em;
cursor: pointer;
&::-webkit-slider-runnable-track {
@include track();
}
&::-moz-range-track {
@include track();
}
&::-ms-track {
border: none;
@include track();
color: transparent;
}
&::-moz-range-progress {
height: $track-h;
@include fill();
}
&::-ms-fill-lower {
@include fill();
}
&::-webkit-slider-thumb {
margin-top: ($track-h - $thumb-d)/2;
@include thumb();
}
&::-moz-range-thumb {
@include thumb();
}
&::-ms-thumb {
@include thumb();
}
&::-ms-tooltip { display: none; }
+ label {
display: block;
text-align: center;
}
&:focus { outline: none; }
}
View Compiled
var form = document.querySelector('form'),
toggle = form.querySelector('button'),
auto = document.getElementById('auto'),
kel = document.getElementById('k'),
klbl = form.querySelector('[for=k]'),
svg = document.querySelector('svg'),
vb = svg.getAttribute('viewBox').split(' ').map(function(p) {
return ~~p;
}),
bmov = document.getElementById('bmov'),
trig = document.getElementById('trig'),
gmov = document.getElementById('gmov'),
gmovx = document.getElementById('gmov-x'),
gmovy = document.getElementById('gmov-y'),
lblt = document.getElementById('lbl-t'),
lbly = document.getElementById('lbl-y'),
max = document.getElementById('max'),
maxcss = document.getElementById('max-css'),
ycss = maxcss.textContent.replace('em', ''),
tmax = ~~max.getAttribute('x'),
ymax = ~~max.getAttribute('y'),
f, r, dragged = null, x0 = null, y0 = null,
rID = null;
var size = function() {
r = svg.getBoundingClientRect();
f = vb[2]/r.width;
};
var setCurrent = function(k) {
var ktxt = Math.round(k*100),
t = ~~(k*tmax),
y = (k*ymax).toFixed(2),
tr2d = 'translate(' + t + ' ' + y + ')';
kel.value = ktxt;
klbl.textContent = lblt.textContent = ktxt + '%';
lbly.textContent = (k*ycss).toFixed(2).replace('.00', '') + 'em';
bmov.setAttribute('y', y);
trig.setAttribute('transform', tr2d);
gmov.setAttribute('transform', tr2d);
gmovx.setAttribute('transform', 'translate(' + t + ')');
gmovy.setAttribute('transform', 'translate(0 ' + y + ')');
};
var slide = function() {
if(rID) {
stopani();
}
if(kel.value != klbl.textContent)
setCurrent(kel.value/100);
};
var ani = function(k) {
if(k === 100) k = 0;
setCurrent(k/100);
rID = requestAnimationFrame(ani.bind(this, ++k));
};
var stopani = function(f) {
cancelAnimationFrame(rID);
rID = null;
if(!f)
auto.checked = !auto.checked;
}
size();
if(auto.checked) ani(0);
addEventListener('resize', size, false);
toggle.addEventListener('click', function(e) {
e.preventDefault();
form.classList.toggle('hidden');
}, false);
kel.addEventListener('input', slide, false);
kel.addEventListener('change', slide, false);
addEventListener('mousedown', function(e) {
var tg = e.target.correspondingUseElement || e.target;
if(tg.getAttribute('for') == 'auto') {
if(rID) { stopani(1); }
else { ani(~~kel.value); }
}
if(!dragged) {
if(tg.id === 'bmov') {
y0 = (e.clientY - r.top)*f + vb[1];
dragged = tg;
}
if(tg.parentNode.id === 'trig') {
x0 = (e.clientX - r.left)*f + vb[0];
dragged = tg.parentNode;
}
if(dragged && rID) { stopani(); }
}
}, false);
addEventListener('mouseup', function(e) {
if(dragged) dragged = null;
}, false);
addEventListener('mousemove', function(e) {
var x, y, dx, dy, k;
if(dragged) {
k = kel.value/100;
if(dragged === bmov) {
y = (e.clientY - r.top)*f + vb[1];
dy = y - y0;
k += dy/ymax;
y0 = y;
}
if(dragged === trig) {
x = (e.clientX - r.left)*f + vb[0];
dx = x - x0;
k += dx/tmax;
x0 = x;
}
if(k >= 0 && k <= 1) setCurrent(k);
}
}, false);
This Pen doesn't use any external JavaScript resources.