Unicycle Range Slider

Unicycle Range Slider developed using HTML, JavaScript and CSS. A range input where a stick figure is on a unicycle whose wheel is the handle. Watch him peddle and the flag display the value as you drag the wheel left and right.

See the Pen Unicycle Range Slider by Jon Kantner (@jkantner) on CodePen.

Created on March 5, 2020 Updated on May 20, 2020. A Pen by Jon Kantner on CodePen. License.

<form>
	<div id="unicycle1" class="unicycle">
		<input type="range" class="unicycle__wheel" name="unicycle" min="0" max="100" value="0">
		<div class="unicycle__marker">
			<div class="unicycle__rider-head"></div>
			<div class="unicycle__rider-body">
				<div class="unicycle__left-arm">
					<div class="unicycle__left-lower-arm"></div>
				</div>
				<div class="unicycle__right-arm">
					<div class="unicycle__right-lower-arm">
						<div class="unicycle__flag-pole">
							<div class="unicycle__flag">0</div>
						</div>
					</div>
				</div>
				<div class="unicycle__left-leg">
					<div class="unicycle__left-lower-leg"></div>
				</div>
				<div class="unicycle__right-leg">
					<div class="unicycle__right-lower-leg"></div>
				</div>
			</div>
			<div class="unicycle__seat"></div>
			<div class="unicycle__bar"></div>
			<div class="unicycle__pedal-arms">
				<div class="unicycle__pedal"></div>
				<div class="unicycle__pedal"></div>
			</div>
		</div>
	</div>
</form>
document.addEventListener("DOMContentLoaded",() => {
	let unicycle = new UnicycleRangeSlider("#unicycle1");
});

class UnicycleRangeSlider {
	constructor(el) {
		this.wheel = document.querySelector(`${el} input[type=range]`);
		this.marker = document.querySelector(`${el} .unicycle__marker`);
		this.flag = document.querySelector(`${el} .unicycle__flag`);

		this.updateBodyPos();
		this.wheel.addEventListener("input",() => { this.updateBodyPos(); });
	}
	updateBodyPos() {
		let max = this.wheel.max,
			min = this.wheel.min,
			realValue = this.wheel.value,
			ticks = max - min,
			relValue = realValue - min,
			percent = relValue / ticks,
			revs = 1,
			left = percent * 100,
			emAdjust = percent * 1.5,
			pedalRot = percent * (360 * revs),
			period = (1 / ((ticks / revs) / 2)) * relValue * Math.PI,
			rightLegRot = -22.5 * Math.sin(period + (1.85 * Math.PI)) - 22.5,
			rightLowerLegRot = 45 * Math.sin(period + (0 * Math.PI)) + 45,
			leftLegRot = -22.5 * Math.sin(period + (2.85 * Math.PI)) - 22.5,
			leftLowerLegRot = 45 * Math.sin(period + (1 * Math.PI)) + 45,
			cssVars = {
				"--pedalRot": `${pedalRot}deg`,
				"--rightLegRot": `${rightLegRot}deg`,
				"--rightLowerLegRot": `${rightLowerLegRot}deg`,
				"--leftLegRot": `${leftLegRot}deg`,
				"--leftLowerLegRot": `${leftLowerLegRot}deg`
			};
		// position stick figure and unicycle body
		this.marker.style.left = `calc(${left}% - ${emAdjust}em)`;
		// update the variables in CSS
		for (let v in cssVars)
			this.marker.style.setProperty(v,cssVars[v]);
		// number in the flag
		this.flag.innerHTML = realValue;
	}
}
* {
	border: 0;
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
:root {
	font-size: calc(24px + (48 - 24)*(100vw - 320px)/(2560 - 320));
	--bg: #f1f1f1;
	--fg: #171717;
}
body, input {
	color: var(--fg);
	font: 1em/1.5 "B612 Mono", monospace;
}
body {
	background: var(--bg);
	display: flex;
	height: 100vh;
	overflow-x: hidden;
}
form {
	margin: auto;
	width: 8.5em;
}
.unicycle,
.unicycle__wheel,
.unicycle__rider-body,
.unicycle__pedal-arms {
	position: relative;
}
.unicycle {
	margin-top: 4.25em;
}
.unicycle__wheel {
	background: transparent;
	border-radius: 0.75em;
	box-shadow: 0 0 0 0.1em inset;
	display: block;
	outline: transparent;
	width: 100%;
	height: 1.5em;
	-webkit-appearance: none;
	appearance: none;
}
.unicycle__wheel::-webkit-slider-thumb {
	background: transparent;
	border: 0;
	border-radius: 50%;
	box-shadow: 0 0 0 0.1em inset;
	cursor: pointer;
	width: 1.5em;
	height: 1.5em;
	-webkit-appearance: none;
	appearance: none;
}
.unicycle__wheel::-moz-range-thumb {
	background: transparent;
	border: 0;
	border-radius: 50%;
	box-shadow: 0 0 0 0.1em inset;
	cursor: pointer;
	width: 1.5em;
	height: 1.5em;
}
.unicycle__marker,
.unicycle__right-arm,
.unicycle__right-lower-arm,
.unicycle__left-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__right-lower-leg,
.unicycle__left-leg,
.unicycle__left-lower-leg,
.unicycle__flag-pole,
.unicycle__flag,
.unicycle__pedal {
	position: absolute;
}
.unicycle__marker {
	
	--pedalRot: 0deg;
	--rightLegRot: 0deg;
	--rightLowerLegRot: 0deg;
	--leftLegRot: 0deg;
	--leftLowerLegRot: 0deg;
	top: -2.5em;
	left: 0;
	width: 1.5em;
	height: 4em;
	z-index: -1;
}
.unicycle__marker > div {
	margin: auto;
}
.unicycle__marker .unicycle__rider-body {
	height: 1em;
	margin-bottom: 0.1em;
}
.unicycle__rider-head, .unicycle__flag {
	box-shadow: 0 0 0 0.1em inset;
}
.unicycle__rider-head {
	border-radius: 50%;
	width: 1em;
	height: 1em;
}
.unicycle__rider-head ~ div,
.unicycle__right-arm,
.unicycle__right-lower-arm,
.unicycle__left-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__right-lower-leg,
.unicycle__left-leg,
.unicycle__left-lower-leg,
.unicycle__flag-pole,
.unicycle__pedal {
	background: currentColor;
}
.unicycle__rider-body,
.unicycle__right-arm,
.unicycle__right-lower-arm,
.unicycle__left-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__right-lower-leg,
.unicycle__left-leg,
.unicycle__left-lower-leg,
.unicycle__flag-pole,
.unicycle__bar {
	width: 0.1em;
}
.unicycle__right-lower-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__right-lower-leg,
.unicycle__left-leg,
.unicycle__left-lower-leg,
.unicycle__seat,
.unicycle__pedal-arms,
.unicycle__pedal {
	border-radius: 0.05em;
}
.unicycle__right-arm,
.unicycle__right-lower-arm,
.unicycle__left-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__right-lower-leg,
.unicycle__left-leg,
.unicycle__left-lower-leg {
	transform-origin: 50% 0.05em;
}
.unicycle__right-lower-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__left-leg,
.unicycle__right-lower-leg,
.unicycle__left-lower-leg {
	top: calc(100% - 0.05em);
}
.unicycle__right-arm,
.unicycle__right-lower-arm,
.unicycle__left-arm,
.unicycle__left-lower-arm,
.unicycle__right-leg,
.unicycle__left-leg {
	height: 0.75em;
}
.unicycle__right-lower-leg,
.unicycle__left-lower-leg {
	height: 0.78em;
}
.unicycle__right-arm {
	transform: rotate(-80deg);
}
.unicycle__right-lower-arm {
	transform: rotate(-10deg);
}
.unicycle__left-arm {
	transform: rotate(25deg);
}
.unicycle__left-lower-arm {
	transform: rotate(5deg);
}
.unicycle__right-leg {
	transform: rotate(var(--rightLegRot));
}
.unicycle__right-lower-leg {
	transform: rotate(var(--rightLowerLegRot));
}
.unicycle__left-leg {
	transform: rotate(var(--leftLegRot));
}
.unicycle__left-lower-leg {
	transform: rotate(var(--leftLowerLegRot));
}
.unicycle__flag-pole {
	height: 3em;
	bottom: 0;
	transform: rotate(90deg);
	transform-origin: 50% calc(100% - 0.1em);
}
.unicycle__flag {
	right: 0;
	text-align: center;
	width: 2.25em;
	height: 1.5em;
}
.unicycle__seat,
.unicycle__pedal-arms {
	height: 0.1em;
}
.unicycle__seat {
	display: flex;
	justify-content: center;
	align-items: flex-end;
	width: 0.4em;
}
.unicycle__bar {
	border-radius: 0 0 0.05em 0.05em;
	height: 1.1em;
}
.unicycle__pedal-arms {
	top: -0.1em;
	width: 0.5em;
	transform: rotate(var(--pedalRot));
}
.unicycle__pedal {
	top: 0;
	width: 0.3em;
	height: 0.1em;
	transform: rotate(calc(var(--pedalRot) * -1));
}
.unicycle__pedal:first-child {
	left: -0.15em;
}
.unicycle__pedal:last-child {
	right: -0.15em;
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg: #171717;
		--fg: #f1f1f1;
	}
}
Categories:
W3TWEAKS
Latest posts by W3TWEAKS (see all)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *