> Spinning wheel on webkit for iPhone/iPod touch

Slot Machine

I’m more and more amazed by the power of mobile webkit. I don’t love the iPhone that much, but as a web developer the beauty of CSS transitions, animations and transforms can’t pass unnoticed. This time I’m giving away a widget that resembles the native Picker View (UIPickerView) functionality but entirely built on javascript.

First things first. Have a look at the demo page or, if you don’t have your device at hand, watch the screencast I baked for you.

Please note that the script is in beta phase, I am publishing it to receive feedback and suggestions. In the next few days we should have a stable version.

Update 2009/03/16: We are out of beta, the script is now stable and ready for production (hope so :) ). In the zip I also included a minified (9kb) version of the script to save some bits of precious bandwidth.

Update 2009/04/19: the script is now compatible with full screen mode apps. It seems that in full screen mode preventDefault() and stopPropagation() placed in touchStart event are not enough to block the propagation of the touch event. Adding preventDefault() to touchMove event solved the problem.

Update 2009/06/18: the script has been updated for OS3.0 compatibility.

Update 2009/07/09: it turned out that Apple is well aware of the webkit refresh bug, fortunately they are kind enough to offer a workaround. All we have to do is to use translate3d instead of translate and all our animations will be fluid as never before! Go get the latest version of the script!

How to use the script

The widget is composed of two parts: the stylesheet and the javascript. No HTML is needed as all the elements are created by the script on the fly. Include both the JS and the CSS into your page and you are ready to spin. You’ll be also surprised to see that the spinning wheel itself is built with just two images, while other three images are needed for the header and buttons. The PNGs altogether are 4.3kb.

The code does not need initialization on window load. You cannot have more than one picker at a time, so the SpinningWheel object is unique and it is created as soon as you include the JS file.

The first thing you need to do is to define the slots with:

SpinningWheel.addSlot(obj values, str styles, str defaultValue)

values is in the form of: { key: value, key2: value, ... }. Keys are the identifiers that won’t be shown in the picker (think of them as the value parameter in the <option value="foo">bar</option> tag). Values are the labels printed on the slots.
styles is a list of space separated predefined styles to be applied to the slot. The available values are:

  • right, align text inside the slot to the right;
  • readonly, the slot can’t be spun;
  • shrink, shrink the slot width to the minimum possible.

The first element of the slot will be selected if no defaultValue is defined.

When all the slots have been created, set the default actions for the cancel and done buttons.

SpinningWheel.setCancelAction( function(){ } );
SpinningWheel.setDoneAction( function() { } );

Finally show the picker:

SpinningWheel.open();

Voila, the Picker View is ready for countless hours of spinning pleasure.

To get the actual selected values call:

var result = SpinningWheel.getSelectedValues();

result.keys will be filled with an array of the selected keys while result.values will hold the list of the selected values (or labels).

Let’s wrap everything together.

function swExample() {
	var numbers = { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9 };
	SpinningWheel.addSlot(numbers, 'right');
	SpinningWheel.addSlot(numbers, 'right');
	SpinningWheel.addSlot({ separator: '.' }, 'readonly shrink');
	SpinningWheel.addSlot(numbers, 'right');
	SpinningWheel.addSlot({ Kg: 'Kg', Lb: 'Lb', St: 'St' }, 'shrink');

	SpinningWheel.setCancelAction(cancel);
	SpinningWheel.setDoneAction(done);

	SpinningWheel.open();
}

function done() {
	var results = SpinningWheel.getSelectedValues();
	alert('values:' + results.values.join(', ') + ' - keys: ' + results.keys.join(', '));
}

function cancel() {
	alert('cancelled!');
}

Look at the demo for more examples.

Create custom styles

I preconfigured for you three styles for the slots, but you can add as many as you need. Say you want a slot with center aligned text. Add the following to the stylesheet:

#sw-slots .sw-center { text-align:center; }

To apply the style create the slot like this:

SpinningWheel.addSlot(values, 'center');

A piece of cake.

By default the slots try to fit their content. Slots with long text will be wider than ones with short text. (Same as the <table /> cell elements). With custom style you can override this behavior. If you have two slots and you want them to be exactly the same width you may add the following style:

#sw-slots .sw-50percent { width:50%; }

and create the slots with:

SpinningWheel.addSlot(values, '50percent');
SpinningWheel.addSlot(values);

You don’t need to apply the style to both slots as the second will fit the remaining space (or the other 50% of the screen width).

Limitations

None that I can tell if not those imposed by the device small CPU. All animations are hardware accelerated, the “birth date” example in the demo creates more than one hundred elements and all animations are pretty fluid.

The script is also compatible with both landscape and portrait mode and you can freely switch from one to the other while the spinning wheel is opened. (That is more than what the native Picker View has to offer).

Note that once closed the spinning wheel is completely inaccessible and all variables will be null or undefined. So basically you can’t programmatically query the SpinningWheel object while it is not visible.

I’m now working on code optimization to reduce memory usage, I hope to release a stable version as soon as possible. The code is now stable, please leave your comments and suggestions.

As always released under MIT license for all your coding needs.

Download the script

/Share the joy

/Reactions

    • Author: Aresinferno
    • Posted on: 2009/03/08
    • At: 16:09

    Looking very nice. Liking the fluid animation when the picker appears. Often for me animation is jumpy.

    • Author: Henry
    • Posted on: 2009/03/08
    • At: 18:04

    This is a marvel. Wonderfully done. My only comment would be that flicking each spinner could be a bit more fluid but otherwise a wonder!

    • Author: keithlamont
    • Posted on: 2009/03/08
    • At: 18:11

    Beautiful! Can’t wait to try out it out.

    • Author: Jan
    • Posted on: 2009/03/09
    • At: 11:26

    This is amazing!
    I’ve got a website where I can use it with!

    Best regards
    Jan
    Gummy Media Ltd

    • Author: Cristopher
    • Posted on: 2009/03/09
    • At: 18:27

    awsome as always :D
    congrats dude!!!

    • Author: neuronic
    • Posted on: 2009/03/14
    • At: 17:04

    Genial, hacia tiempo que buscava algo similar, gran trabajo “good job” thanks

    • Author: Steve S.
    • Posted on: 2009/03/17
    • At: 04:10

    Is there a way to get a similar control working on firefox or a non-mobile browser?

  • @Steve,
    yes, it’s not that difficult. First of all you need to remove all the CSS animations and related event handlers (ontransitionend) and replace them with a timed (setInterval) function (it’s a lot easier if you use something like jquery). You also need to replace touchstart with mousedown, touchmove with mousemove and touchend with mouseup. Probably you want to open a popup instead of making the control slide up from the bottom of the page.

    • Author: Russ
    • Posted on: 2009/03/24
    • At: 05:23

    Is there a way to programatically set the wheels before or just after it appears? I’m looking to remember what the user chooses, then set it back to their previous setting the next time the spinner comes back up.

  • @Russ, the “birth date” example in the demo page shows you how to do that. The third parameter of the addSlot method sets the selected value.

    • Author: Chris
    • Posted on: 2009/03/29
    • At: 14:50

    Great work Matteo. I’ve posted about it on my blog, hope you don’t mind.

    http://iphoneized.com/?p=326

    Keep up the awesome work!

    Cheers,

    Chris

    • Author: krystian
    • Posted on: 2009/04/07
    • At: 17:34

    it no work in fullscreen mode

    • Author: AwayBBL
    • Posted on: 2009/04/12
    • At: 00:17

    dumb questions:

    Could images be used in the pickers? Sorta like your image of a slot machine. And then could the wheels be set into motion in a circular fashion, by clicking a button (or shaking like in UrbanSpoon)?

  • @AwayBBL, images can be used but you probably need to play a bit with CSS and cellHeight (now fixed at 44px). Regarding the circular motion is feasible but probably it’s easier setting a 3d rotation, there’s an example about it on the apple developers’ site.

    • Author: AwayBBL
    • Posted on: 2009/04/12
    • At: 13:00

    Thx Matteo, and Buona Pasqua

    • Author: Steven
    • Posted on: 2009/04/16
    • At: 16:13

    Hi MAtteo, will be an update planned to correct full-screen mode ?

  • @Steven, I’ll have a look at it this weekend. I bet it can be solved very easily.

  • @Steven and all, the script seems now compatible with full screen mode.

    • Author: Abe
    • Posted on: 2009/04/20
    • At: 00:50

    Hi,

    I am able to get the spinning wheel working, but not when I use it with jQTouch.

    The javascript is all called correctly, but the spinning wheel doesn’t appear. I think there are some css conflicts or something.

    Anyone have any idea how to get the two to work together?

    • Author: Steven
    • Posted on: 2009/04/20
    • At: 11:33

    Thank you very much Matteo, you’re the best ;)

    • Author: Steven
    • Posted on: 2009/04/20
    • At: 14:40

    Have you think about put a title in the top bar ? It would be very handy to know from which button the spinning wheel have been opened…
    Bye

    • Author: AwayBBL
    • Posted on: 2009/05/03
    • At: 14:01

    @Matteo

    The script no longer works in 3.0 beta. Perhaps it’s an Apple bug, or maybe they’ve changed the way they handle touches?

  • @AwayBBL, surely something has changed in os3.0, but the beta is still too buggy. I hope the next beta will be something we can work with, because so far it seems Apple forgot their loyal web developers ;)

    • Author: pugwash
    • Posted on: 2009/05/12
    • At: 07:05

    It doesn’t work in 3.0 beta as the webkitTransform call in getComputedStyle() returns a 2d matrix() and not a matrix3d(). I’m not sure why. Changing the index from 13 I think to 5 will make it work but still not as smooth as OS 2.2.1

    • Author: pugwash
    • Posted on: 2009/05/16
    • At: 03:40

    The reason for returning a 2d matrix is an optimization. A 3d matrix is returned if z-axis is used.

  • There are better ways to get the y-axis than “split”, as soon as I get better I’ll show you how. (You can get a clue looking at the “Rotating wheel” code).

    • Author: pugwasg
    • Posted on: 2009/05/22
    • At: 02:49

    Any reason why the spinner for the number of days flickers when selecting the year or month. This only happens in 3.0 beta and doesn’t happen in 2.2.1 where it’s fluid.

  • I hope it depends on the “beta” state of OS3.

    • Author: HalfMoon
    • Posted on: 2009/05/28
    • At: 18:31

    Dumb question here…

    How would I be able to get the single wheel to load a new page when clicked?

    • Author: Javier
    • Posted on: 2009/06/12
    • At: 19:35

    I am trying to make it work in iUI, but no luck, as soon as I load the iUI CSS, it stops working. Help will be much appreciated

    • Author: AwayBBL
    • Posted on: 2009/06/14
    • At: 01:17

    @Matteo

    Hope you are feeling well. Have you had a chance to look at how this works in 3.0GM?

  • @AwayBBL: I’m still trying to recover from a very obstinate kind of illness. BTW, I’ll surely update my scripts as soon as the os3.0 is out.

    • Author: pugwash
    • Posted on: 2009/06/14
    • At: 18:06

    The spinner still flashes in 3.0 GM seed.
    It’s not as fluid as in OS 2.2.1

    • Author: AwayBBL
    • Posted on: 2009/06/16
    • At: 11:46

    Pugwash,

    Are you saying it works? I wasn’t able to make it work a all. It’d freeze on first item.

    • Author: Fred
    • Posted on: 2009/06/17
    • At: 19:07

    Just updated to official OS 3.0 today and same problem.
    The wheel is also glued on the first item. We need to investigate this problem.

  • The script has been updated. It should be compatible with OS3.0. I’ll try to find out why it is more fluid on OS2 than on OS3, maybe something changed in the css transactions engine.

    • Author: Fred
    • Posted on: 2009/06/18
    • At: 10:44

    Ok, the wheel is now “wheelable” on OS3.
    But effectively, the animation is quite unusable because really not so smooth as it was on OS2.

    • Author: Fred
    • Posted on: 2009/06/18
    • At: 21:59

    Is there an iPhone forum/board explaining the major/minor diffs in the webkit between OS2 and OS3 ? This may be useful to understand why the wheel’s animation is not smooth with OS3.

  • I’m investigating on the OS3 laziness. Probably we just have to do things in a slightly different way, but documentation lacks.

    • Author: AwayBBL
    • Posted on: 2009/06/19
    • At: 16:10

    @Matteo

    Thanks for getting spinning wheel working again.

    Agree with the other folks that it’s still a little choppy, and it flashes the background, but at least it works.

    Hope you can make it work smoothly again.

    • Author: name
    • Posted on: 2009/06/23
    • At: 14:52

    I am struggling to figure out the changes between 2.0 and 3.0. The reported “freeze” after a touchstart event is similar to what I am experiencing. All is fine until the first touchend is fired. What was changed in this to make it 3.0 compatible?

    • Author: Fred
    • Posted on: 2009/06/25
    • At: 13:23

    This is actually not the solution we need to re-smooth the ui, but here is a good directive to use in your document css body (or div) to disable the user selection (cut/paste) in the UI :

    “-webkit-user-select: none”

    Please read this article.
    http://groups.google.com/group/iphonewebdev/browse_thread/thread/d49897d5d24718a4#

    Fred.

    PS: Where are thoses kind of webkit infos/update available ?
    I mean a full “API” doc.

    • Author: Fred
    • Posted on: 2009/06/25
    • At: 14:34

    Ok, it was a noob question.

    http://developer.apple.com/documentation/AppleApplications/Reference/SafariCSSRef/Articles/StandardCSSProperties.html#//apple_ref/doc/uid/TP30001266-SW1

    But i really can’t find a table with OS3 webkit enhancements compared to OS2.

    Fred.

  • Script updated guys! Get the latest version for blazing fast performance!

    • Author: Fred
    • Posted on: 2009/07/09
    • At: 17:10

    Yeepa ! Thanks a lot. Works now like a charm.

    • Author: momo
    • Posted on: 2009/07/28
    • At: 14:09

    I have te same problem Javier had (http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11#comment-1727). I can not make it running with iUi which I am using for my app. It’s just not appearing. :( Any ideas so far?

    • Author: richie
    • Posted on: 2009/08/06
    • At: 16:23

    this is a great product, has anyone gotten it working with the iWebkit framework? I have been unsuccessful, it fails on the var results = SpinningWheel.getSelectedValues(); call, just sits there and does nothing.

      • Author: alex
      • Posted on: 2011/11/18
      • At: 17:59

      Did you find a solution?
      i have this problem too!

    • Author: Mike
    • Posted on: 2009/08/22
    • At: 19:04

    Did anyone figure out how to get it to open a html link when the “Done” button is clicked?