Emily Emily - 11 months ago 230
CSS Question

How to make a HTML5 spinning list/rotating wheel selector/picker

My question is about an answer given by user ByteHamster here: How to create Javascipt/HTML5 Spinner List with images? in the answer he/she gives an example of how to create a scrolling animation with html, css and Javascript. The animation allows the user to scroll through the numbers by clicking above or below the selected number on screen, and the selected number is shown in a div below the animation.

I was wondering if it’s possible to do something similar to this but instead of having an image moving up and down, can it be turned into a number wheel? By that I mean, in the example above, the scrolling stops in one direction once the number reaches 0, I’m wondering if it’s possible to create a wheel, where a user could constantly spin it from top to bottom, or bottom to top if they wished to do so. Would this require using 3d interactive animation software?

I've seen this question: HTML5/CSS3 - how to make "endless" rotating background - 360 degrees panorama but I'm unsure if the answers are applicable to my project as they don't seem to be interactive.

As user ByteHamster's answer is over 3 years old, I was wondering if there’s a better way to achieve this effect with a html5 animation? And am I correct in thinking that the Javascript in the example would make it not work on some devices/browsers that don’t have Javascript enabled? Would a html5 approach be the best way to ensure the effect works on most devices/browsers?

Answer Source

Here's what I put together from the info provided... works with the mousewheel, swiping and clicking on the top and bottom numbers. Infinite as requested of course. No special perspective style (yet) but I thought it looked quite decent as is. Could still be an option naturally. Didn't use the plugin I linked to in the comments or requestAnimationFrame but jQuery animate() is quite a good tool for this. The library has great cross browser support (that's it's strength actually), all it needs is a link to it for the JavaScript to be able to get executed. You could use a CDN, this version also works for IE8- :

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

For the best cross browser support on using the mousewheel, this plugin was included :

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.js"></script>


Just a basic parent and styling with spans for each number, a few prepended in case of going up.

$(function() {

var gate = $(window),
cog = $('#rotator'),
digit = cog.find('span'),
slot = digit.eq(0).height(),
base = 1.5*slot,
output = $('#result'),


gate.on('load', function() {

	setTimeout(interAction, 50);

function interAction() {


	cog.scrollTop(base).fadeTo(0,1).mousewheel(function(turn, delta) {

		if (isBusy()) return false;

		delta < 0 ? up = true : up = false;


		return false;

	digit.on('touchstart', function(e) {

		var touch = e.originalEvent.touches,
		begin = touch[0].pageY, swipe;

		digit.on('touchmove', function(e) {

			var contact = e.originalEvent.touches,
			end = contact[0].pageY,
			distance = end-begin;

			if (isBusy()) return;

			if (Math.abs(distance) > 30) {
			swipe = true;
			distance > 30 ? up = true : up = false;
		.add(gate).one('touchend', function() {

			if (swipe) {
			swipe = false;

	.on('mousedown touchstart', function(e) {

		if (e.which && e.which != 1) return;

		var item = $(this).index();

		if (item == 1 || item == 3) {

		digit.one('mouseup touchend', function() {

			var same = item == $(this).index();

			if (isBusy() || !same) return cancelIt();

			item == 1 ? up = true : up = false;


			return cancelIt();

		return false;

function isBusy() {

	return cog.is(':animated');

function cancelIt() {

	digit.off('mouseup touchend');

	return false;

function newNumber() {

	var aim = base;

	up ? aim -= slot : aim += slot;

	cog.animate({scrollTop: aim}, 250, function() {

		up ? digit.eq(9).prependTo(cog) : digit.eq(0).appendTo(cog);


		digit = cog.find('span');

body {
  font-family: "Times New Roman";
  background: grey;

#ticker {
  width: 150px;
  text-align: center;
  margin: auto;

#rotator {
  height: 140px;
  background-image: url(//i.imgsafe.org/2d8949191f.png);
  background-size: 100% 100%;
  margin: 0 0 10px;
  overflow: hidden;

#rotator span {
  width: 100%;
  height: 50%;
  display: inline-block;
  font-size: 50px;
  line-height: 70px;
  cursor: default;

#result {
  height: 30px;
  font-size: 20px;
  color: white;
  line-height: 30px;
  box-shadow: 0 0 3px black;
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.js"></script>

<div id="ticker">
 <div id="rotator">
 <div id="result"></div>

Pretty straightforward, animates the scroll position up or down and then appends or prepends the first or last number depending on the direction. The duration of the animation can be set here :

cog.animate({scrollTop: current}, 250, function() {

Updated - now has swipe support for mobile devices (even tested on early Android), the mousewheel plugin instead of relying on the wheel event and added a div where count is kept. Plus minor tweaks.

Meanwhile I've also made a demo that allows to have multiple tickers. It can be found here :


Edited - major touch support improvement after much debugging. Plus more efficient swift clicking.