Cubiq Spinning Wheel help please

Hi everyone, I hope some can help lol it’s been doing my head in… I’ve just been editing the included sample up to now…
it’s the Cubiq Spinning Wheel on Webkit for iPhone. Basically if someone could just enlighten me on how to change the code in the sample to achieve the following, I’d be eternally grateful:

What I’m trying to achieve is to have the picker load on startup, and be permanently on the screen. i wanted an output on startup too before any wheels were touched, and then live output after that. I’d probably remove the buttons from the picker also.

What I achieved: permanently on the screen and the results output on startup before wheels are spun by adding onload openWeight() done() to the body tag. I also managed to get live results to be output in several ways but with different side effects, either momentum animation was affected or it would seem to work but… If the wheels were spun fast so they spun a while after Mouseup/touchup or whatever and the spin stopped within the boundaries then the output result would not change, however if the spin fell outside the boundaries and bounced back then the result would output. So basically I need the result to be captured after all animation has ended.

Thanks again for reading this long winded post.

Charlie666,

I’m not seeing your example code. Could you re-post it?

Couldn’t add it from my phone sorry, will add it from my mac in a few mins.

This is the large js, not the min one that the demo uses but it’ll be easier to see lol.

/**
 *
 * Find more about the Spinning Wheel function at
 * http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11
 *
 * Copyright (c) 2009 Matteo Spinelli, http://cubiq.org/
 * Released under MIT license
 * http://cubiq.org/dropbox/mit-license.txt
 *
 * Version 1.4 - Last updated: 2009.07.09
 *
 */

var SpinningWheel = {
cellHeight: 44,
friction: 0.003,
slotData: [],


/**
 *
 * Event handler
 *
 */

handleEvent: function (e) {
if (e.type == 'touchstart') {
this.lockScreen(e);
if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
this.tapDown(e);
} else if (e.currentTarget.id == 'sw-frame') {
this.scrollStart(e);
}
} else if (e.type == 'touchmove') {
this.lockScreen(e);

if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
this.tapCancel(e);
} else if (e.currentTarget.id == 'sw-frame') {
this.scrollMove(e);
}
} else if (e.type == 'touchend') {
if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
this.tapUp(e);
} else if (e.currentTarget.id == 'sw-frame') {
this.scrollEnd(e);
}
} else if (e.type == 'webkitTransitionEnd') {
if (e.target.id == 'sw-wrapper') {
this.destroy();
} else {
this.backWithinBoundaries(e);
}
} else if (e.type == 'orientationchange') {
this.onOrientationChange(e);
} else if (e.type == 'scroll') {
this.onScroll(e);
}
},


/**
 *
 * Global events
 *
 */

onOrientationChange: function (e) {
window.scrollTo(0, 0);
this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
this.calculateSlotsWidth();
},

onScroll: function (e) {
this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
},

lockScreen: function (e) {
e.preventDefault();
e.stopPropagation();
},


/**
 *
 * Initialization
 *
 */

reset: function () {
this.slotEl = [];

this.activeSlot = null;

this.swWrapper = undefined;
this.swSlotWrapper = undefined;
this.swSlots = undefined;
this.swFrame = undefined;
},

calculateSlotsWidth: function () {
var div = this.swSlots.getElementsByTagName('div');
for (var i = 0; i < div.length; i += 1) {
this.slotEl[i].slotWidth = div[i].offsetWidth;
}
},

create: function () {
var i, l, out, ul, div;

this.reset();// Initialize object variables

// Create the Spinning Wheel main wrapper
div = document.createElement('div');
div.id = 'sw-wrapper';
div.style.top = window.innerHeight + window.pageYOffset + 'px';// Place the SW down the actual viewing screen
div.style.webkitTransitionProperty = '-webkit-transform';
div.innerHTML = '
Cancel
Done
';

document.body.appendChild(div);

this.swWrapper = div;// The SW wrapper
this.swSlotWrapper = document.getElementById('sw-slots-wrapper');// Slots visible area
this.swSlots = document.getElementById('sw-slots');// Pseudo table element (inner wrapper)
this.swFrame = document.getElementById('sw-frame');// The scrolling controller

// Create HTML slot elements
for (l = 0; l < this.slotData.length; l += 1) {
// Create the slot
ul = document.createElement('ul');
out = '';
for (i in this.slotData[l].values) {
out += '
' + this.slotData[l].values[i] + '<' + '/li>';
}
ul.innerHTML = out;

div = document.createElement('div');// Create slot container
div.className = this.slotData[l].style;// Add styles to the container
div.appendChild(ul);

// Append the slot to the wrapper
this.swSlots.appendChild(div);

ul.slotPosition = l;// Save the slot position inside the wrapper
ul.slotYPosition = 0;
ul.slotWidth = 0;
ul.slotMaxScroll = this.swSlotWrapper.clientHeight - ul.clientHeight - 86;
ul.style.webkitTransitionTimingFunction = 'cubic-bezier(0, 0, 0.2, 1)';// Add default transition

this.slotEl.push(ul);// Save the slot for later use

// Place the slot to its default position (if other than 0)
if (this.slotData[l].defaultValue) {
this.scrollToValue(l, this.slotData[l].defaultValue);
}
}

this.calculateSlotsWidth();

// Global events
document.addEventListener('touchstart', this, false);// Prevent page scrolling
document.addEventListener('touchmove', this, false);// Prevent page scrolling
window.addEventListener('orientationchange', this, true);// Optimize SW on orientation change
window.addEventListener('scroll', this, true);// Reposition SW on page scroll

// Cancel/Done buttons events
document.getElementById('sw-cancel').addEventListener('touchstart', this, false);
document.getElementById('sw-done').addEventListener('touchstart', this, false);

// Add scrolling to the slots
this.swFrame.addEventListener('touchstart', this, false);
},

open: function () {
this.create();

this.swWrapper.style.webkitTransitionTimingFunction = 'ease-out';
this.swWrapper.style.webkitTransitionDuration = '400ms';
this.swWrapper.style.webkitTransform = 'translate3d(0, -260px, 0)';
},


/**
 *
 * Unload
 *
 */

destroy: function () {
this.swWrapper.removeEventListener('webkitTransitionEnd', this, false);

this.swFrame.removeEventListener('touchstart', this, false);

document.getElementById('sw-cancel').removeEventListener('touchstart', this, false);
document.getElementById('sw-done').removeEventListener('touchstart', this, false);

document.removeEventListener('touchstart', this, false);
document.removeEventListener('touchmove', this, false);
window.removeEventListener('orientationchange', this, true);
window.removeEventListener('scroll', this, true);

this.slotData = [];
this.cancelAction = function () {
return false;
};

this.cancelDone = function () {
return true;
};

this.reset();

document.body.removeChild(document.getElementById('sw-wrapper'));
},

close: function () {
this.swWrapper.style.webkitTransitionTimingFunction = 'ease-in';
this.swWrapper.style.webkitTransitionDuration = '400ms';
this.swWrapper.style.webkitTransform = 'translate3d(0, 0, 0)';

this.swWrapper.addEventListener('webkitTransitionEnd', this, false);
},


/**
 *
 * Generic methods
 *
 */

addSlot: function (values, style, defaultValue) {
if (!style) {
style = '';
}

style = style.split(' ');

for (var i = 0; i < style.length; i += 1) {
style[i] = 'sw-' + style[i];
}

style = style.join(' ');

var obj = { 'values': values, 'style': style, 'defaultValue': defaultValue };
this.slotData.push(obj);
},

getSelectedValues: function () {
var index, count,
    i, l,
keys = [], values = [];

for (i in this.slotEl) {
// Remove any residual animation
this.slotEl[i].removeEventListener('webkitTransitionEnd', this, false);
this.slotEl[i].style.webkitTransitionDuration = '0';

if (this.slotEl[i].slotYPosition > 0) {
this.setPosition(i, 0);
} else if (this.slotEl[i].slotYPosition < this.slotEl[i].slotMaxScroll) {
this.setPosition(i, this.slotEl[i].slotMaxScroll);
}

index = -Math.round(this.slotEl[i].slotYPosition / this.cellHeight);

count = 0;
for (l in this.slotData[i].values) {
if (count == index) {
keys.push(l);
values.push(this.slotData[i].values[l]);
break;
}

count += 1;
}
}

return { 'keys': keys, 'values': values };
},


/**
 *
 * Rolling slots
 *
 */

setPosition: function (slot, pos) {
this.slotEl[slot].slotYPosition = pos;
this.slotEl[slot].style.webkitTransform = 'translate3d(0, ' + pos + 'px, 0)';
},

scrollStart: function (e) {
// Find the clicked slot
var xPos = e.targetTouches[0].clientX - this.swSlots.offsetLeft;// Clicked position minus left offset (should be 11px)

// Find tapped slot
var slot = 0;
for (var i = 0; i < this.slotEl.length; i += 1) {
slot += this.slotEl[i].slotWidth;

if (xPos < slot) {
this.activeSlot = i;
break;
}
}

// If slot is readonly do nothing
if (this.slotData[this.activeSlot].style.match('readonly')) {
this.swFrame.removeEventListener('touchmove', this, false);
this.swFrame.removeEventListener('touchend', this, false);
return false;
}

this.slotEl[this.activeSlot].removeEventListener('webkitTransitionEnd', this, false);// Remove transition event (if any)
this.slotEl[this.activeSlot].style.webkitTransitionDuration = '0';// Remove any residual transition

// Stop and hold slot position
var theTransform = window.getComputedStyle(this.slotEl[this.activeSlot]).webkitTransform;
theTransform = new WebKitCSSMatrix(theTransform).m42;
if (theTransform != this.slotEl[this.activeSlot].slotYPosition) {
this.setPosition(this.activeSlot, theTransform);
}

this.startY = e.targetTouches[0].clientY;
this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
this.scrollStartTime = e.timeStamp;

this.swFrame.addEventListener('touchmove', this, false);
this.swFrame.addEventListener('touchend', this, false);

return true;
},

scrollMove: function (e) {
var topDelta = e.targetTouches[0].clientY - this.startY;

if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
topDelta /= 2;
}

this.setPosition(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition + topDelta);
this.startY = e.targetTouches[0].clientY;

// Prevent slingshot effect
if (e.timeStamp - this.scrollStartTime > 80) {
this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
this.scrollStartTime = e.timeStamp;
}
},

scrollEnd: function (e) {
this.swFrame.removeEventListener('touchmove', this, false);
this.swFrame.removeEventListener('touchend', this, false);

// If we are outside of the boundaries, let's go back to the sheepfold
if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
this.scrollTo(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition > 0 ? 0 : this.slotEl[this.activeSlot].slotMaxScroll);
return false;
}

// Lame formula to calculate a fake deceleration
var scrollDistance = this.slotEl[this.activeSlot].slotYPosition - this.scrollStartY;

// The drag session was too short
if (scrollDistance < this.cellHeight / 1.5 && scrollDistance > -this.cellHeight / 1.5) {
if (this.slotEl[this.activeSlot].slotYPosition % this.cellHeight) {
this.scrollTo(this.activeSlot, Math.round(this.slotEl[this.activeSlot].slotYPosition / this.cellHeight) * this.cellHeight, '100ms');
}

return false;
}

var scrollDuration = e.timeStamp - this.scrollStartTime;

var newDuration = (2 * scrollDistance / scrollDuration) / this.friction;
var newScrollDistance = (this.friction / 2) * (newDuration * newDuration);

if (newDuration < 0) {
newDuration = -newDuration;
newScrollDistance = -newScrollDistance;
}

var newPosition = this.slotEl[this.activeSlot].slotYPosition + newScrollDistance;

if (newPosition > 0) {
// Prevent the slot to be dragged outside the visible area (top margin)
newPosition /= 2;
newDuration /= 3;

if (newPosition > this.swSlotWrapper.clientHeight / 4) {
newPosition = this.swSlotWrapper.clientHeight / 4;
}
} else if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
// Prevent the slot to be dragged outside the visible area (bottom margin)
newPosition = (newPosition - this.slotEl[this.activeSlot].slotMaxScroll) / 2 + this.slotEl[this.activeSlot].slotMaxScroll;
newDuration /= 3;

if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4) {
newPosition = this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4;
}
} else {
newPosition = Math.round(newPosition / this.cellHeight) * this.cellHeight;
}

this.scrollTo(this.activeSlot, Math.round(newPosition), Math.round(newDuration) + 'ms');

return true;
},

scrollTo: function (slotNum, dest, runtime) {
this.slotEl[slotNum].style.webkitTransitionDuration = runtime ? runtime : '100ms';
this.setPosition(slotNum, dest ? dest : 0);

// If we are outside of the boundaries go back to the sheepfold
if (this.slotEl[slotNum].slotYPosition > 0 || this.slotEl[slotNum].slotYPosition < this.slotEl[slotNum].slotMaxScroll) {
this.slotEl[slotNum].addEventListener('webkitTransitionEnd', this, false);
}
},

scrollToValue: function (slot, value) {
var yPos, count, i;

this.slotEl[slot].removeEventListener('webkitTransitionEnd', this, false);
this.slotEl[slot].style.webkitTransitionDuration = '0';

count = 0;
for (i in this.slotData[slot].values) {
if (i == value) {
yPos = count * this.cellHeight;
this.setPosition(slot, yPos);
break;
}

count -= 1;
}
},

backWithinBoundaries: function (e) {
e.target.removeEventListener('webkitTransitionEnd', this, false);

this.scrollTo(e.target.slotPosition, e.target.slotYPosition > 0 ? 0 : e.target.slotMaxScroll, '150ms');
return false;
},


/**
 *
 * Buttons
 *
 */

tapDown: function (e) {
e.currentTarget.addEventListener('touchmove', this, false);
e.currentTarget.addEventListener('touchend', this, false);
e.currentTarget.className = 'sw-pressed';
},

tapCancel: function (e) {
e.currentTarget.removeEventListener('touchmove', this, false);
e.currentTarget.removeEventListener('touchend', this, false);
e.currentTarget.className = '';
},

tapUp: function (e) {
this.tapCancel(e);

if (e.currentTarget.id == 'sw-cancel') {
this.cancelAction();
} else {
this.doneAction();
}

this.close();
},

setCancelAction: function (action) {
this.cancelAction = action;
},

setDoneAction: function (action) {
this.doneAction = action;
},

cancelAction: function () {
return false;
},

cancelDone: function () {
return true;
}
};

And this is the untouched demo html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" id="iphone-viewport" content="minimum-scale=1.0, maximum-scale=1.0, width=device-width" />
<meta name="apple-mobile-web-app-capable" content="yes" />

<link rel="stylesheet" href="spinningwheel.css" type="text/css" media="all" />
<script type="text/javascript" src="spinningwheel-min.js?v=1.4"></script>

<title>Spinning Wheel for iPhone/iPod touch</title>

<script type="text/javascript">
function openWeight() {

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(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 openBirthDate() {
var now = new Date();
var days = { };
var years = { };
var months = { 1: 'Gen', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec' };

for( var i = 1; i < 32; i += 1 ) {
days[i] = i;
}

for( i = now.getFullYear()-100; i < now.getFullYear()+1; i += 1 ) {
years[i] = i;
}

SpinningWheel.addSlot(years, 'right', 1999);
SpinningWheel.addSlot(months, '', 4);
SpinningWheel.addSlot(days, 'right', 12);

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

SpinningWheel.open();
}

function openOneSlot() {
SpinningWheel.addSlot({1: 'Ichi', 2: 'Ni', 3: 'San', 4: 'Shi', 5: 'Go'});

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

SpinningWheel.open();
}

function done() {
var results = SpinningWheel.getSelectedValues();
document.getElementById('result').innerHTML = 'values: ' + results.values.join(' ') + '<br />keys: ' + results.keys.join(', ');
}

function cancel() {
document.getElementById('result').innerHTML = 'cancelled!';
}


window.addEventListener('load', function(){ setTimeout(function(){ window.scrollTo(0,0); }, 100); }, true);

</script>


<style type="text/css">
body { text-align:center; font-family:helvetica; }
button { font-size:16px; }
#result { margin:10px; background:#aaa; -webkit-border-radius:8px; padding:8px; font-size:18px; }
</style>

</head>

<body>
<p>Spinning Wheel slot machine alike widget for iPhone and iPod touch.
This demo works only on the simulator and the real devices, it does not work on any other browser.</p>
<button onclick="openBirthDate()">birth date</button>
<button onclick="openWeight()">weight</button>
<button onclick="openOneSlot()">1 slot</button>
<p id="result">results</p>
<p><a href="http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11">Read the related article on cubiq.org</a></p>

<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>

this text is here for the sole purpose of making the page longer
</body>

</html>

And this is the CSS:

#sw-wrapper {
position:absolute; z-index:1000;
left:0;
width:100%;
font-family:helvetica, sans-serif;
background:rgba(0,0,0,0.7);
text-align:left;
}

#sw-header {
position:relative;
width:100%; height:43px;
border-top:1px solid #000; border-bottom:1px solid #000;
background:url(sw-header.png) 0 0 repeat-x;
opacity: 0.9;
}

#sw-cancel, #sw-done {
position:absolute;
top:7px;
height:20px; line-height:20px;
padding:0 5px; margin:0;
border-width:5px;
font-size:12px; font-weight:bold;
text-shadow:rgba(0,0,0,0.8) 0 -1px 0;
color:#fff;
}

#sw-cancel {
left:7px;
float:left;
-webkit-border-image:url(sw-button-cancel.png) 5;
}

#sw-done {
right:7px;
float:right;
-webkit-border-image:url(sw-button-done.png) 5;
}

.sw-pressed { opacity:0.4; }

#sw-slots-wrapper {
position:relative; z-index:999;
display:block;
height:215px;
padding:0 11px;
overflow:hidden;
}

#sw-slots {
display:table;
width:100%;
background:#fcfcfc;
}

#sw-slots div {
display:table-cell;
height:100%;
padding-top:86px;
border-left:2px solid #0d0e0f;
background-color:#fcfcfc;
background-image: url(sw-slot-border.png);
background-position: 0 0, 100% 0;
background-repeat: repeat-y;
}

#sw-slots div:first-child { border:0; }

#sw-slots ul {
padding:0 0 85px 0; margin:0;
list-style:none;
}

#sw-slots .sw-right { text-align:right; }
#sw-slots .sw-shrink { width:1%; }
#sw-slots .sw-readonly { background:#ddd; }

#sw-slots li {
padding:0 8px;
height:44px;
overflow:hidden;
font:bold 24px/44px Helvetica,sans-serif;
}

#sw-frame {
position:absolute; z-index:1000;
left:0; right:0; bottom:0;
height:183px;
border-width:16px;
-webkit-border-image:url(sw-alpha.png) 16;
}

Ok I posted lol don’t see it though. Maybe too long a message?

–EDIT-- ok it appeared when I posted this lol.

Thank you @Pullo, could you split it up into js, html and CSS as they are all in the same code tag now? Thank you again.

Charlie,

Could you please use code tags when submitting large blocks of code.
Here’s how: http://www.sitepoint.com/forums/showthread.php?1041498-Forum-Posting-Basics&p=5407985&viewfull=1#post5407985

Cheers!

Will do now that I know. Sorry.

There is a working demo here(only works fully from mobile browsers):

http://cubiq.org/dropbox/sw/