// 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);

External CSS

  1. https://codepen.io/thebabydino/pen/MajxzY.scss

External JavaScript

This Pen doesn't use any external JavaScript resources.