Skip to content

Instantly share code, notes, and snippets.

@shshaw
Created May 18, 2018 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shshaw/df63607010a5a9cf0beff15fa91818d0 to your computer and use it in GitHub Desktop.
Save shshaw/df63607010a5a9cf0beff15fa91818d0 to your computer and use it in GitHub Desktop.
@keyframers 1.6.0 | Mouse to the Metal
<a href="https://youtu.be/SJu3igAsVco" target="_blank" data-keyframers-credit style="color: #FFF"></a>
<script src="https://codepen.io/shshaw/pen/QmZYMG.js"></script>
<div id="speedometer">
<div class="speed-bg">
<div class="speed-bg__steady"></div>
<div class="speed-bg__pulse"></div>
</div>
<svg class="speed-ticks" viewBox="0 0 100 100">
<circle class="speed-tick__markers" cx="50" cy="50" r="40"></circle>
</svg>
<div class="speed-glow inner"></div>
<div class="speed-glow outer"></div>
<div class="speed-needle"></div>
<div id="speed" data-speed="0"></div>
<div class="speed-instructions">
<strong>MPPS</strong>
<em>(Mouse Pixels Per Second)</em>
<small>Move your mouse quickly to track the speed.</small>
</div>
</div>
console.clear();
const {
map,
withLatestFrom,
scan,
distinctUntilChanged,
bufferCount,
share
} = rxjs.operators;
const MAX_SPEED = 100;
const elApp = document.querySelector('#speedometer');
const elRange = document.querySelector('#range');
const elSpeed = document.querySelector('#speed');
const mouseMove$ = rxjs
.fromEvent(document.body, 'mousemove')
.pipe(
map(e => ({
x: e.clientX,
y: e.clientY
}))
);
const tick$ = rxjs.interval(0, rxjs.animationFrameScheduler);
const value$ = mouseMove$;
const lerp = (previous, current) => {
const delta = current - previous;
return previous + delta * .05;
}; // 1 1.5 1.75 1.875 ..... 2
const smoothValue$ = tick$.pipe(
withLatestFrom(value$, (_, value) => value),
scan((current, next) => {
return {
x: next.x,
y: next.y,
speed: Math.hypot(current.x - next.x, current.y - next.y)
}
}, {x: 0, y: 0, speed: 0}),
map(e => e.speed),
scan(
/* accumulate values by doing */ lerp,
/* start at */ 0));
const smoothRoundedValue$ = smoothValue$.pipe(
map(n => Math.round(n)),
distinctUntilChanged(),
// share()
);
smoothValue$.subscribe(value => {
elApp.style.setProperty('--progress', value / MAX_SPEED);
});
smoothRoundedValue$.subscribe(e => {
const prevSpeed = elApp.style.getPropertyValue('--speed');
elApp.style.setProperty('--prev-speed', prevSpeed);
elSpeed.setAttribute('data-prev-speed', prevSpeed);
// these are the only things that matter right now in this life
elApp.style.setProperty('--speed', e);
elSpeed.setAttribute('data-speed', e);
});
/**
* CSS Var Helper Fns
*/
function calc(v){ return v; }
function readCssVar(element, varName){
const elementStyles = getComputedStyle(element);
var value = elementStyles.getPropertyValue(varName).trim()
try { return value.indexOf('calc') > -1 ? eval(value) : value } catch(e){ return value }
}
function dumpVars(keys, element){
var computed = { el: element };
keys.forEach((key)=>{
computed[key] = readCssVar(element,key);
});
return computed;
}
function tableVars(keys, selector) {
console.table(
Array.prototype.map.call(
document.querySelectorAll(selector),
dumpVars.bind(null, keys)
)
);
}
/*
tableVars([
'--total-chars',
'--center',
'--offset',
'--distance',
'--distance-percent',
'--distance-sine'
], '.spread-out .char' );
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.1.0/rxjs.umd.js"></script>
$accent: #1B53F1;
*, *:before, *:after {
box-sizing: border-box;
position: relative;
}
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
html { font-size: 62.5%; }
body {
display: flex;
justify-content: center;
align-items: center;
color: white;
background-color: black;
}
@import url('https://fonts.googleapis.com/css?family=Oswald');
#speedometer {
font-family: 'Oswald', sans-serif;
width: 80vmin;
height: 80vmin;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
--progress: 0;
background: radial-gradient(
transparent 68%,
#3C4042 70%,
transparent 76%
);
border-top: solid .5vmin #3C4042;
border-radius: 50%;
}
#speed {
font-size: 20vmin;
height: 20vmin;
width: 25vmin;
--delta: calc(var(--speed, 0) - var(--prev-speed, 0));
line-height: 1;
&:before, &:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
}
&:before {
content: attr(data-speed);
}
}
.speed-instructions {
text-align: center;
line-height: 1.6;
margin-bottom: -3em;
max-width: 25%;
font-size: 2vmin;
> * { display: block; }
em { font-style: normal; font-size: 0.8em; }
small { font-size: 0.6em; font-family: monospace; }
}
#range { display: none; }
.speed-needle {
height: 50%;
width: 4px;
margin: -2px;
position: absolute;
left: 50%;
top: 50%;
transform-origin: top center;
transform: scaleY(1.2) rotate(calc(var(--progress, 0) * 1turn));
border-radius: 50%;
background: radial-gradient( #FFF, $accent, transparent 85%);
background-size: 100% 60%;
background-position: center 90%;
background-repeat: no-repeat;
&:after {
mix-blend-mode: overlay;
content: '';
height: 45%;
width: 100%;
position: absolute;
bottom: 12%;
left: 0;
box-shadow: 0 0 4rem 1rem rgba(white, 0.2);
z-index: 1;
}
}
.speed-glow {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
width: 85%;
height: 85%;
margin: auto;
transform: rotate(calc(var(--progress, 0) * 1turn));
//background: radial-gradient(transparent 60%, $accent 61%, transparent 69%);
border-radius: 50%;
filter: blur(0.75vw);
border-bottom: solid 2vw $accent;
&:before {
content: '';
display: block;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
border: inherit;
border-radius: inherit;
border-style: dashed;
// border-color: #FFF;
border-color: lighten($accent,10%);
opacity: 0.3;
// mix-blend-mode: screen;
animation: speed-glow-wobble 5s linear infinite;
animation-duration: calc( ( 1 - var(--progress)) * 3s );
@keyframes speed-glow-wobble {
33% { transform: scale(1.04); }
66% {
transform: scale(0.99);
}
}
}
}
.speed-glow.inner {
width: 70%;
height: 70%;
transition: transform 0.15s linear;
}
.speed-glow.outer {
//background: radial-gradient(transparent 58%, $accent 63%, transparent 69%);
transition: transform 0.3s linear;
&:before {
animation-delay: -2.5s;
}
}
/* ////////////////////////////////////////////////////////////////////////// */
.speed-ticks,
.speed-bg,
.speed-bg__steady,
.speed-bg__pulse {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
width: 85%;
height: 85%;
// border: solid 1px red;
border-radius: 50%;
overflow: hidden;
}
.speed-ticks {
width: 90%;
height: 90%;
stroke: hsla(228, 50, 80, .8);
stroke-dasharray: .3 1.85;
stroke-width: 2;
fill: none;
}
.speed-bg__pulse {
background: radial-gradient(transparent 30%, $accent, transparent 60%);
animation: bg-pulse 5s linear infinite;
animation-duration: calc( 1s + (( 1 - var(--progress)) * 4s) );
@keyframes bg-pulse {
0%, 60% { opacity: 0; transform: scale(0.8); }
80% { opacity: 1; }
100% { opacity: 0; transform: scale(1.4); }
}
}
.speed-bg__steady {
&,
&:before,
&:after {
display: block;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
width: 100%;
height: 100%;
}
&:before,
&:after {
content: '';
font-size: 3.5vw;
word-wrap: break-word;
line-height: 0.5;
font-family: monospace;
content: '......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................';
background: radial-gradient(transparent 30%, $accent, transparent 60%);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
animation: bg-spin 20s linear infinite;
animation-duration: calc(
15s +
(( 1 - var(--progress)) * 5s )
);
@keyframes bg-spin {
50% {
transform: rotate(180deg) scale(0.9);
}
100% {
transform: rotate(360deg) scale(1);
}
}
}
&:after {
width: 90%;
height: 90%;
//animation-delay: -5s;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment