<h1>Wavy Numbers Gauge</h1>
<p>go ahead... drag or throw that slider</p>
<svg id="demo" xmlns="http://www.w3.org/2000/svg" width="1000" height="280" viewBox="0 0 1000 280">
<path id="slider" d="M15.63,1.49.4,27.87A3,3,0,0,0,3,32.35H33.45A3,3,0,0,0,36,27.87L20.81,1.49A3,3,0,0,0,15.63,1.49Z" fill="#5cceee"/></svg>
<div class="branding">
<p>see the tutorial on</p>
<h2><span>motion</span>tricks.com</h2>
<a href="https://www.motiontricks.com/wavy-gauge-numbers/" target="_blank">Tutorial</a>
</div>
body {
font-family: "proxima-nova", sans-serif;
display: flex;
height: 100vh;
background: #000;
overflow: hidden;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
font-size: 20px;
background-image: linear-gradient(
0deg,
rgb(10, 10, 10),
rgb(20, 20, 20),
rgb(43, 59, 105)
);
}
svg {
width: 80vw;
height: auto;
max-width: 1200px;
margin-top: 1rem;
}
svg text {
fill: white;
font-size: 20px;
user-select: none;
}
svg line {
stroke-width: 1.5;
stroke: white;
}
h2 {
font-weight: 400;
}
h2 span {
font-weight: 700;
color: #5cceee;
}
.branding {
display: flex;
flex-direction: column;
align-items: center;
}
h1, h2,
p {
margin: 0;
}
a {
background: #5cceee;
color: white;
padding: 10px;
text-decoration: none;
border-radius: 3px;
margin-top: 10px;
}
a:hover {
background: white;
color: #5cceee;
}
gsap.registerPlugin(InertiaPlugin);
console.clear();
const svgns = "http://www.w3.org/2000/svg";
const demo = document.querySelector("svg");
let positions = 20; // how many numbers
let count = 0; // simple counter for the numbers
let startX = 30; // first line position and fist number position
let dragMin = startX;
let y2Pos = 200; // bottom of each tick line
let y1Pos;
let spacing = 9.4; // space between lines
let jump = 120; // height of number jump during animation
let dur = 1.1; // master duration
let masterStagger = 3; // higher numbers tighten the curve
// move the draggable element into position
gsap.set("#slider", { x: startX, xPercent: -50, y: y2Pos + 20 });
// make a 5 pack of lines for each number
for (let j = 0; j < positions; j++) {
makeNumber();
for (let i = 0; i < 5; i++) {
y1Pos = i === 0 ? y2Pos - 25 : y2Pos - 15; // first line in each pack is slightly taller
makeLine(y1Pos);
startX += spacing;
}
count++;
}
makeNumber(); // need one final number
makeLine(y2Pos - 25); // need 1 extra line for the last number
// creates the line elements
function makeLine(yp) {
let newLine = document.createElementNS(svgns, "line");
demo.appendChild(newLine);
gsap.set(newLine, {
attr: { x1: startX, x2: startX, y1: yp, y2: y2Pos }
});
}
// creates the numbers
function makeNumber() {
let txt = document.createElementNS(svgns, "text");
demo.appendChild(txt);
txt.textContent = count;
gsap.set(txt, {
attr: { x: startX, y: y2Pos - 40, "text-anchor": "middle" }
});
}
// final position of last line is new draggable max
let dragMax = startX;
// main timeline for the number jump
let animNumbers = gsap.timeline({ paused: true });
animNumbers
.to("text", {
duration: dur,
y: -jump,
scale: 1.5,
fill: "#5cceee",
stagger: {
amount: masterStagger,
yoyo: true,
repeat: 1
},
ease: "sine.inOut"
})
.time(dur); // set the time to the end of the first number jump
// Map the drag range to the timeline duration
let mapper = gsap.utils.mapRange(
dragMin,
dragMax,
dur,
animNumbers.duration() - dur
);
// Create the draggable element and set the range
Draggable.create("#slider", {
type: "x",
bounds: {
minX: dragMin,
maxX: dragMax
},
inertia: true,
edgeResistance: 1,
onDrag: updateMeter,
onThrowUpdate: updateMeter
});
// using the mapper, update the current time of the timeline
function updateMeter() {
gsap.set(animNumbers, { time: mapper(this.x) });
}