123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /**
- *
- * 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
- *
- */
- define(function() {
- var cachePosition = {x: 0, y: 0};
-
- 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);
- cachePosition.x = e.touches[0]['pageX'];
- cachePosition.y = e.touches[0]['pageY'];
- } 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') {
- //android 4.4以上touchmove会一直触发,判断一下偏移量
- if (Math.abs(e.touches[0]['pageX'] - cachePosition.x) > 10 || Math.abs(e.touches[0]['pageY'] - cachePosition.y) > 10) {
- 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 = '<div id="sw-header"><div id="sw-cancel">取消</' + 'div><div id="sw-done">确认</' + 'div></' + 'div><div id="sw-slots-wrapper"><div id="sw-slots"></' + 'div></' + 'div><div id="sw-frame"></' + 'div>';
- 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 += '<li>' + 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;
- }
- };
-
- return SpinningWheel;
- })
|