From 192e127d620541815c77494fb9e188c971535008 Mon Sep 17 00:00:00 2001 From: Peter Rudolfsen Date: Sun, 18 Dec 2011 18:17:34 +0100 Subject: [PATCH 1/7] Added github page. --- bg_respatex_tile.png | Bin 0 -> 8816 bytes index.html | 31 ++++++++ regular.html | 91 ++++++++++++++++++++++ styles.css | 38 ++++++++++ transition.html | 176 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 336 insertions(+) create mode 100644 bg_respatex_tile.png create mode 100644 index.html create mode 100644 regular.html create mode 100644 styles.css create mode 100644 transition.html diff --git a/bg_respatex_tile.png b/bg_respatex_tile.png new file mode 100644 index 0000000000000000000000000000000000000000..c10910b1d78977c31d68886e0127a6ef52cfc80b GIT binary patch literal 8816 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 + + iScroll sticky headers + + + + + + + +

Sticky headers demo

+ + + + + diff --git a/regular.html b/regular.html new file mode 100644 index 0000000..908cfab --- /dev/null +++ b/regular.html @@ -0,0 +1,91 @@ + + + + iScroll sticky headers + + + + + + + + +
+
    +
  • +

    A

    +
      +
    • Aa
    • +
    • Aa
    • +
    • Aa
    • +
    • Aa
    • +
    • Aa
    • +
    +
  • +
  • +

    B

    +
      +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    +
  • +
  • +

    C

    +
      +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    +
  • +
  • +

    D

    +
      +
    • Dd
    • +
    • Dd
    • +
    • Dd
    • +
    • Dd
    • +
    +
  • +
  • +

    E

    +
      +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    +
  • +
+
+ + + + + + + + diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..cb5b463 --- /dev/null +++ b/styles.css @@ -0,0 +1,38 @@ +body { + background: url(bg_respatex_tile.png); + margin: 0; + padding: 0; + font-family: Helvetica, Arial; +} + +ul { + margin: 0; + padding: 0; + list-style-type: none; +} + +.list ul > li { + padding: 10px; + border-top: 1px solid #AAA; +} + +.list ul { + background: rgb(240, 240, 240); +} + +h1 { + background: -webkit-linear-gradient(top, #333 0%, #2E2E2E 100%); + border-top: 1px solid #404040; + border-bottom: 1px solid #1A1A1A; + color: #EEE; + padding: 0 10px; + margin: 0; + opacity: .8; +} + +#scroll { + width: 100%; + position: absolute; + top: 0; + bottom: 0; +} \ No newline at end of file diff --git a/transition.html b/transition.html new file mode 100644 index 0000000..386424e --- /dev/null +++ b/transition.html @@ -0,0 +1,176 @@ + + + + iScroll transitioned sticky headers + + + + + + + + +
+
    +
  • +

    A

    +
      +
    • Aa
    • +
    • Aa
    • +
    • Aa
    • +
    • Aa
    • +
    • Aa
    • +
    +
  • +
  • +

    B

    +
      +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    • Bb
    • +
    +
  • +
  • +

    C

    +
      +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    • Cc
    • +
    +
  • +
  • +

    D

    +
      +
    • Dd
    • +
    • Dd
    • +
    • Dd
    • +
    • Dd
    • +
    +
  • +
  • +

    E

    +
      +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    • Ee
    • +
    +
  • +

    F

    +
      +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    • Ff
    • +
    +
  • +

    G

    +
      +
    • Gg
    • +
    +
  • +

    H

    +
      +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    • Hh
    • +
    +
  • +

    I

    +
      +
    • Ii
    • +
    • Ii
    • +
    • Ii
    • +
    +
  • +

    J

    +
      +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    • Jj
    • +
    +
  • +

    K

    +
      +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    • Kk
    • +
    +
  • +
+
+ + + + + + + + From 0d068604647da7cf56a79de2b81e3f0fab89f250 Mon Sep 17 00:00:00 2001 From: Peter Rudolfsen Date: Sun, 18 Dec 2011 18:19:31 +0100 Subject: [PATCH 2/7] Added iscroll js. --- iscroll-src.js | 1073 +++++++++++++++++++++++++++++++++++++++++++++ iscroll-sticky.js | 106 +++++ 2 files changed, 1179 insertions(+) create mode 100644 iscroll-src.js create mode 100644 iscroll-sticky.js diff --git a/iscroll-src.js b/iscroll-src.js new file mode 100644 index 0000000..5ce0f39 --- /dev/null +++ b/iscroll-src.js @@ -0,0 +1,1073 @@ +/*! + * iScroll v4.1.9 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org + * Released under MIT license, http://cubiq.org/license + */ +(function(){ +var m = Math, + mround = function (r) { return r >> 0; }, + vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : + (/firefox/i).test(navigator.userAgent) ? 'Moz' : + 'opera' in window ? 'O' : '', + + // Browser capabilities + isAndroid = (/android/gi).test(navigator.appVersion), + isIDevice = (/iphone|ipad/gi).test(navigator.appVersion), + isPlaybook = (/playbook/gi).test(navigator.appVersion), + isTouchPad = (/hp-tablet/gi).test(navigator.appVersion), + + has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), + hasTouch = 'ontouchstart' in window && !isTouchPad, + hasTransform = vendor + 'Transform' in document.documentElement.style, + hasTransitionEnd = true, + + nextFrame = (function() { + return window.requestAnimationFrame + || window.webkitRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.oRequestAnimationFrame + || window.msRequestAnimationFrame + || function(callback) { return setTimeout(callback, 1); } + })(), + cancelFrame = (function () { + return window.cancelRequestAnimationFrame + || window.webkitCancelAnimationFrame + || window.webkitCancelRequestAnimationFrame + || window.mozCancelRequestAnimationFrame + || window.oCancelRequestAnimationFrame + || window.msCancelRequestAnimationFrame + || clearTimeout + })(), + + // Events + RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize', + START_EV = hasTouch ? 'touchstart' : 'mousedown', + MOVE_EV = hasTouch ? 'touchmove' : 'mousemove', + END_EV = hasTouch ? 'touchend' : 'mouseup', + CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup', + WHEEL_EV = vendor == 'Moz' ? 'DOMMouseScroll' : 'mousewheel', + + // Helpers + trnOpen = 'translate' + (has3d ? '3d(' : '('), + trnClose = has3d ? ',0)' : ')', + + // Constructor + iScroll = function (el, options) { + var that = this, + doc = document, + i; + + that.wrapper = typeof el == 'object' ? el : doc.getElementById(el); + that.wrapper.style.overflow = 'hidden'; + that.scroller = that.wrapper.children[0]; + + // Default options + that.options = { + hScroll: true, + vScroll: true, + x: 0, + y: 0, + bounce: true, + bounceLock: false, + momentum: true, + lockDirection: true, + useTransform: true, + useTransition: false, + topOffset: 0, + checkDOMChanges: false, // Experimental + + // Scrollbar + hScrollbar: true, + vScrollbar: true, + fixedScrollbar: isAndroid, + hideScrollbar: isIDevice, + fadeScrollbar: isIDevice && has3d, + scrollbarClass: '', + + // Zoom + zoom: false, + zoomMin: 1, + zoomMax: 4, + doubleTapZoom: 2, + wheelAction: 'scroll', + + // Snap + snap: false, + snapThreshold: 1, + + // Events + onRefresh: null, + onBeforeScrollStart: function (e) { e.preventDefault(); }, + onScrollStart: null, + onBeforeScrollMove: null, + onScrollMove: null, + onBeforeScrollEnd: null, + onScrollEnd: null, + onTouchEnd: null, + onDestroy: null, + onZoomStart: null, + onZoom: null, + onZoomEnd: null + }; + + // User defined options + for (i in options) that.options[i] = options[i]; + + // Set starting position + that.x = that.options.x; + that.y = that.options.y; + + // Normalize options + that.options.useTransform = hasTransform ? that.options.useTransform : false; + that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar; + that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar; + that.options.zoom = that.options.useTransform && that.options.zoom; + that.options.useTransition = hasTransitionEnd && that.options.useTransition; + + // Helpers FIX ANDROID BUG! + // translate3d and scale doesn't work together! + // Ignoring 3d ONLY WHEN YOU SET that.options.zoom + if ( that.options.zoom && isAndroid ){ + trnOpen = 'translate('; + trnClose = ')'; + } + + // Set some default styles + that.scroller.style[vendor + 'TransitionProperty'] = that.options.useTransform ? '-' + vendor.toLowerCase() + '-transform' : 'top left'; + that.scroller.style[vendor + 'TransitionDuration'] = '0'; + that.scroller.style[vendor + 'TransformOrigin'] = '0 0'; + if (that.options.useTransition) that.scroller.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(0.33,0.66,0.66,1)'; + + if (that.options.useTransform) that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose; + else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; + + if (that.options.useTransition) that.options.fixedScrollbar = true; + + that.refresh(); + + that._bind(RESIZE_EV, window); + that._bind(START_EV); + if (!hasTouch) { + that._bind('mouseout', that.wrapper); + if (that.options.wheelAction != 'none') + that._bind(WHEEL_EV); + } + + if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () { + that._checkDOMChanges(); + }, 500); + }; + +// Prototype +iScroll.prototype = { + enabled: true, + x: 0, + y: 0, + steps: [], + scale: 1, + currPageX: 0, currPageY: 0, + pagesX: [], pagesY: [], + aniTime: null, + wheelZoomCount: 0, + + handleEvent: function (e) { + var that = this; + switch(e.type) { + case START_EV: + if (!hasTouch && e.button !== 0) return; + that._start(e); + break; + case MOVE_EV: that._move(e); break; + case END_EV: + case CANCEL_EV: that._end(e); break; + case RESIZE_EV: that._resize(); break; + case WHEEL_EV: that._wheel(e); break; + case 'mouseout': that._mouseout(e); break; + case 'webkitTransitionEnd': that._transitionEnd(e); break; + } + }, + + _checkDOMChanges: function () { + if (this.moved || this.zoomed || this.animating || + (this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) return; + + this.refresh(); + }, + + _scrollbar: function (dir) { + var that = this, + doc = document, + bar; + + if (!that[dir + 'Scrollbar']) { + if (that[dir + 'ScrollbarWrapper']) { + if (hasTransform) that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = ''; + that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']); + that[dir + 'ScrollbarWrapper'] = null; + that[dir + 'ScrollbarIndicator'] = null; + } + + return; + } + + if (!that[dir + 'ScrollbarWrapper']) { + // Create the scrollbar wrapper + bar = doc.createElement('div'); + + if (that.options.scrollbarClass) bar.className = that.options.scrollbarClass + dir.toUpperCase(); + else bar.style.cssText = 'position:absolute;z-index:100;' + (dir == 'h' ? 'height:7px;bottom:1px;left:2px;right:' + (that.vScrollbar ? '7' : '2') + 'px' : 'width:7px;bottom:' + (that.hScrollbar ? '7' : '2') + 'px;top:2px;right:1px'); + + bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:opacity;-' + vendor + '-transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1'); + + that.wrapper.appendChild(bar); + that[dir + 'ScrollbarWrapper'] = bar; + + // Create the scrollbar indicator + bar = doc.createElement('div'); + if (!that.options.scrollbarClass) { + bar.style.cssText = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);-' + vendor + '-background-clip:padding-box;-' + vendor + '-box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';-' + vendor + '-border-radius:3px;border-radius:3px'; + } + bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:-' + vendor + '-transform;-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);-' + vendor + '-transition-duration:0;-' + vendor + '-transform:' + trnOpen + '0,0' + trnClose; + if (that.options.useTransition) bar.style.cssText += ';-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)'; + + that[dir + 'ScrollbarWrapper'].appendChild(bar); + that[dir + 'ScrollbarIndicator'] = bar; + } + + if (dir == 'h') { + that.hScrollbarSize = that.hScrollbarWrapper.clientWidth; + that.hScrollbarIndicatorSize = m.max(mround(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8); + that.hScrollbarIndicator.style.width = that.hScrollbarIndicatorSize + 'px'; + that.hScrollbarMaxScroll = that.hScrollbarSize - that.hScrollbarIndicatorSize; + that.hScrollbarProp = that.hScrollbarMaxScroll / that.maxScrollX; + } else { + that.vScrollbarSize = that.vScrollbarWrapper.clientHeight; + that.vScrollbarIndicatorSize = m.max(mround(that.vScrollbarSize * that.vScrollbarSize / that.scrollerH), 8); + that.vScrollbarIndicator.style.height = that.vScrollbarIndicatorSize + 'px'; + that.vScrollbarMaxScroll = that.vScrollbarSize - that.vScrollbarIndicatorSize; + that.vScrollbarProp = that.vScrollbarMaxScroll / that.maxScrollY; + } + + // Reset position + that._scrollbarPos(dir, true); + }, + + _resize: function () { + var that = this; + setTimeout(function () { that.refresh(); }, isAndroid ? 200 : 0); + }, + + _pos: function (x, y) { + x = this.hScroll ? x : 0; + y = this.vScroll ? y : 0; + + if (this.options.useTransform) { + this.scroller.style[vendor + 'Transform'] = trnOpen + x + 'px,' + y + 'px' + trnClose + ' scale(' + this.scale + ')'; + } else { + x = mround(x); + y = mround(y); + this.scroller.style.left = x + 'px'; + this.scroller.style.top = y + 'px'; + } + + this.x = x; + this.y = y; + + this._scrollbarPos('h'); + this._scrollbarPos('v'); + }, + + _scrollbarPos: function (dir, hidden) { + var that = this, + pos = dir == 'h' ? that.x : that.y, + size; + + if (!that[dir + 'Scrollbar']) return; + + pos = that[dir + 'ScrollbarProp'] * pos; + + if (pos < 0) { + if (!that.options.fixedScrollbar) { + size = that[dir + 'ScrollbarIndicatorSize'] + mround(pos * 3); + if (size < 8) size = 8; + that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; + } + pos = 0; + } else if (pos > that[dir + 'ScrollbarMaxScroll']) { + if (!that.options.fixedScrollbar) { + size = that[dir + 'ScrollbarIndicatorSize'] - mround((pos - that[dir + 'ScrollbarMaxScroll']) * 3); + if (size < 8) size = 8; + that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; + pos = that[dir + 'ScrollbarMaxScroll'] + (that[dir + 'ScrollbarIndicatorSize'] - size); + } else { + pos = that[dir + 'ScrollbarMaxScroll']; + } + } + + that[dir + 'ScrollbarWrapper'].style[vendor + 'TransitionDelay'] = '0'; + that[dir + 'ScrollbarWrapper'].style.opacity = hidden && that.options.hideScrollbar ? '0' : '1'; + that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = trnOpen + (dir == 'h' ? pos + 'px,0' : '0,' + pos + 'px') + trnClose; + }, + + _start: function (e) { + var that = this, + point = hasTouch ? e.touches[0] : e, + matrix, x, y, + c1, c2; + + if (!that.enabled) return; + + if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e); + + if (that.options.useTransition || that.options.zoom) that._transitionTime(0); + + that.moved = false; + that.animating = false; + that.zoomed = false; + that.distX = 0; + that.distY = 0; + that.absDistX = 0; + that.absDistY = 0; + that.dirX = 0; + that.dirY = 0; + + // Gesture start + if (that.options.zoom && hasTouch && e.touches.length > 1) { + c1 = m.abs(e.touches[0].pageX-e.touches[1].pageX); + c2 = m.abs(e.touches[0].pageY-e.touches[1].pageY); + that.touchesDistStart = m.sqrt(c1 * c1 + c2 * c2); + + that.originX = m.abs(e.touches[0].pageX + e.touches[1].pageX - that.wrapperOffsetLeft * 2) / 2 - that.x; + that.originY = m.abs(e.touches[0].pageY + e.touches[1].pageY - that.wrapperOffsetTop * 2) / 2 - that.y; + + if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); + } + + if (that.options.momentum) { + if (that.options.useTransform) { + // Very lame general purpose alternative to CSSMatrix + matrix = getComputedStyle(that.scroller, null)[vendor + 'Transform'].replace(/[^0-9-.,]/g, '').split(','); + x = matrix[4] * 1; + y = matrix[5] * 1; + } else { + x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1; + y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1; + } + + if (x != that.x || y != that.y) { + if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + else cancelFrame(that.aniTime); + that.steps = []; + that._pos(x, y); + } + } + + that.absStartX = that.x; // Needed by snap threshold + that.absStartY = that.y; + + that.startX = that.x; + that.startY = that.y; + that.pointX = point.pageX; + that.pointY = point.pageY; + + that.startTime = e.timeStamp || Date.now(); + + if (that.options.onScrollStart) that.options.onScrollStart.call(that, e); + + that._bind(MOVE_EV); + that._bind(END_EV); + that._bind(CANCEL_EV); + }, + + _move: function (e) { + var that = this, + point = hasTouch ? e.touches[0] : e, + deltaX = point.pageX - that.pointX, + deltaY = point.pageY - that.pointY, + newX = that.x + deltaX, + newY = that.y + deltaY, + c1, c2, scale, + timestamp = e.timeStamp || Date.now(); + + if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e); + + // Zoom + if (that.options.zoom && hasTouch && e.touches.length > 1) { + c1 = m.abs(e.touches[0].pageX - e.touches[1].pageX); + c2 = m.abs(e.touches[0].pageY - e.touches[1].pageY); + that.touchesDist = m.sqrt(c1*c1+c2*c2); + + that.zoomed = true; + + scale = 1 / that.touchesDistStart * that.touchesDist * this.scale; + + if (scale < that.options.zoomMin) scale = 0.5 * that.options.zoomMin * Math.pow(2.0, scale / that.options.zoomMin); + else if (scale > that.options.zoomMax) scale = 2.0 * that.options.zoomMax * Math.pow(0.5, that.options.zoomMax / scale); + + that.lastScale = scale / this.scale; + + newX = this.originX - this.originX * that.lastScale + this.x, + newY = this.originY - this.originY * that.lastScale + this.y; + + this.scroller.style[vendor + 'Transform'] = trnOpen + newX + 'px,' + newY + 'px' + trnClose + ' scale(' + scale + ')'; + + if (that.options.onZoom) that.options.onZoom.call(that, e); + return; + } + + that.pointX = point.pageX; + that.pointY = point.pageY; + + // Slow down if outside of the boundaries + if (newX > 0 || newX < that.maxScrollX) { + newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX; + } + if (newY > that.minScrollY || newY < that.maxScrollY) { + newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= that.minScrollY || that.maxScrollY >= 0 ? that.minScrollY : that.maxScrollY; + } + + if (that.absDistX < 6 && that.absDistY < 6) { + that.distX += deltaX; + that.distY += deltaY; + that.absDistX = m.abs(that.distX); + that.absDistY = m.abs(that.distY); + + return; + } + + // Lock direction + if (that.options.lockDirection) { + if (that.absDistX > that.absDistY + 5) { + newY = that.y; + deltaY = 0; + } else if (that.absDistY > that.absDistX + 5) { + newX = that.x; + deltaX = 0; + } + } + + that.moved = true; + that._pos(newX, newY); + that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; + that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; + + if (timestamp - that.startTime > 300) { + that.startTime = timestamp; + that.startX = that.x; + that.startY = that.y; + } + + if (that.options.onScrollMove) that.options.onScrollMove.call(that, e); + }, + + _end: function (e) { + if (hasTouch && e.touches.length != 0) return; + + var that = this, + point = hasTouch ? e.changedTouches[0] : e, + target, ev, + momentumX = { dist:0, time:0 }, + momentumY = { dist:0, time:0 }, + duration = (e.timeStamp || Date.now()) - that.startTime, + newPosX = that.x, + newPosY = that.y, + distX, distY, + newDuration, + snap, + scale; + + that._unbind(MOVE_EV); + that._unbind(END_EV); + that._unbind(CANCEL_EV); + + if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e); + + if (that.zoomed) { + scale = that.scale * that.lastScale; + scale = Math.max(that.options.zoomMin, scale); + scale = Math.min(that.options.zoomMax, scale); + that.lastScale = scale / that.scale; + that.scale = scale; + + that.x = that.originX - that.originX * that.lastScale + that.x; + that.y = that.originY - that.originY * that.lastScale + that.y; + + that.scroller.style[vendor + 'TransitionDuration'] = '200ms'; + that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + that.scale + ')'; + + that.zoomed = false; + that.refresh(); + + if (that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); + return; + } + + if (!that.moved) { + if (hasTouch) { + if (that.doubleTapTimer && that.options.zoom) { + // Double tapped + clearTimeout(that.doubleTapTimer); + that.doubleTapTimer = null; + if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); + that.zoom(that.pointX, that.pointY, that.scale == 1 ? that.options.doubleTapZoom : 1); + if (that.options.onZoomEnd) { + setTimeout(function() { + that.options.onZoomEnd.call(that, e); + }, 200); // 200 is default zoom duration + } + } else { + that.doubleTapTimer = setTimeout(function () { + that.doubleTapTimer = null; + + // Find the last touched element + target = point.target; + while (target.nodeType != 1) target = target.parentNode; + + if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { + ev = document.createEvent('MouseEvents'); + ev.initMouseEvent('click', true, true, e.view, 1, + point.screenX, point.screenY, point.clientX, point.clientY, + e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, + 0, null); + ev._fake = true; + target.dispatchEvent(ev); + } + }, that.options.zoom ? 250 : 0); + } + } + + that._resetPos(200); + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + if (duration < 300 && that.options.momentum) { + momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX; + momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y - that.minScrollY : 0), that.options.bounce ? that.wrapperH : 0) : momentumY; + + newPosX = that.x + momentumX.dist; + newPosY = that.y + momentumY.dist; + + if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 }; + if ((that.y > that.minScrollY && newPosY > that.minScrollY) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 }; + } + + if (momentumX.dist || momentumY.dist) { + newDuration = m.max(m.max(momentumX.time, momentumY.time), 10); + + // Do we need to snap? + if (that.options.snap) { + distX = newPosX - that.absStartX; + distY = newPosY - that.absStartY; + if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) { that.scrollTo(that.absStartX, that.absStartY, 200); } + else { + snap = that._snap(newPosX, newPosY); + newPosX = snap.x; + newPosY = snap.y; + newDuration = m.max(snap.time, newDuration); + } + } + + that.scrollTo(mround(newPosX), mround(newPosY), newDuration); + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + // Do we need to snap? + if (that.options.snap) { + distX = newPosX - that.absStartX; + distY = newPosY - that.absStartY; + if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) that.scrollTo(that.absStartX, that.absStartY, 200); + else { + snap = that._snap(that.x, that.y); + if (snap.x != that.x || snap.y != that.y) that.scrollTo(snap.x, snap.y, snap.time); + } + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + that._resetPos(200); + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + }, + + _resetPos: function (time) { + var that = this, + resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x, + resetY = that.y >= that.minScrollY || that.maxScrollY > 0 ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; + + if (resetX == that.x && resetY == that.y) { + if (that.moved) { + that.moved = false; + if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); // Execute custom code on scroll end + } + + if (that.hScrollbar && that.options.hideScrollbar) { + if (vendor == 'webkit') that.hScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + that.hScrollbarWrapper.style.opacity = '0'; + } + if (that.vScrollbar && that.options.hideScrollbar) { + if (vendor == 'webkit') that.vScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + that.vScrollbarWrapper.style.opacity = '0'; + } + + return; + } + + that.scrollTo(resetX, resetY, time || 0); + }, + + _wheel: function (e) { + var that = this, + wheelDeltaX, wheelDeltaY, + deltaX, deltaY, + deltaScale; + + if ('wheelDeltaX' in e) { + wheelDeltaX = e.wheelDeltaX / 12; + wheelDeltaY = e.wheelDeltaY / 12; + } else if ('detail' in e) { + wheelDeltaX = wheelDeltaY = -e.detail * 3; + } else { + wheelDeltaX = wheelDeltaY = -e.wheelDelta; + } + + if (that.options.wheelAction == 'zoom') { + deltaScale = that.scale * Math.pow(2, 1/3 * (wheelDeltaY ? wheelDeltaY / Math.abs(wheelDeltaY) : 0)); + if (deltaScale < that.options.zoomMin) deltaScale = that.options.zoomMin; + if (deltaScale > that.options.zoomMax) deltaScale = that.options.zoomMax; + + if (deltaScale != that.scale) { + if (!that.wheelZoomCount && that.options.onZoomStart) that.options.onZoomStart.call(that, e); + that.wheelZoomCount++; + + that.zoom(e.pageX, e.pageY, deltaScale, 400); + + setTimeout(function() { + that.wheelZoomCount--; + if (!that.wheelZoomCount && that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); + }, 400); + } + + return; + } + + deltaX = that.x + wheelDeltaX; + deltaY = that.y + wheelDeltaY; + + if (deltaX > 0) deltaX = 0; + else if (deltaX < that.maxScrollX) deltaX = that.maxScrollX; + + if (deltaY > that.minScrollY) deltaY = that.minScrollY; + else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY; + + that.scrollTo(deltaX, deltaY, 0); + }, + + _mouseout: function (e) { + var t = e.relatedTarget; + + if (!t) { + this._end(e); + return; + } + + while (t = t.parentNode) if (t == this.wrapper) return; + + this._end(e); + }, + + _transitionEnd: function (e) { + var that = this; + + if (e.target != that.scroller) return; + + that._unbind('webkitTransitionEnd'); + + that._startAni(); + }, + + + /** + * + * Utilities + * + */ + _startAni: function () { + var that = this, + startX = that.x, startY = that.y, + startTime = Date.now(), + step, easeOut, + animate; + + if (that.animating) return; + + if (!that.steps.length) { + that._resetPos(400); + return; + } + + step = that.steps.shift(); + + if (step.x == startX && step.y == startY) step.time = 0; + + that.animating = true; + that.moved = true; + + if (that.options.useTransition) { + that._transitionTime(step.time); + that._pos(step.x, step.y); + that.animating = false; + if (step.time) that._bind('webkitTransitionEnd'); + else that._resetPos(0); + return; + } + + animate = function () { + var now = Date.now(), + newX, newY; + + if (now >= startTime + step.time) { + that._pos(step.x, step.y); + that.animating = false; + if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that); // Execute custom code on animation end + that._startAni(); + return; + } + + now = (now - startTime) / step.time - 1; + easeOut = m.sqrt(1 - now * now); + newX = (step.x - startX) * easeOut + startX; + newY = (step.y - startY) * easeOut + startY; + that._pos(newX, newY); + if (that.animating) that.aniTime = nextFrame(animate); + }; + + animate(); + }, + + _transitionTime: function (time) { + time += 'ms'; + this.scroller.style[vendor + 'TransitionDuration'] = time; + if (this.hScrollbar) this.hScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; + if (this.vScrollbar) this.vScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; + }, + + _momentum: function (dist, time, maxDistUpper, maxDistLower, size) { + var deceleration = 0.0006, + speed = m.abs(dist) / time, + newDist = (speed * speed) / (2 * deceleration), + newTime = 0, outsideDist = 0; + + // Proportinally reduce speed if we are outside of the boundaries + if (dist > 0 && newDist > maxDistUpper) { + outsideDist = size / (6 / (newDist / speed * deceleration)); + maxDistUpper = maxDistUpper + outsideDist; + speed = speed * maxDistUpper / newDist; + newDist = maxDistUpper; + } else if (dist < 0 && newDist > maxDistLower) { + outsideDist = size / (6 / (newDist / speed * deceleration)); + maxDistLower = maxDistLower + outsideDist; + speed = speed * maxDistLower / newDist; + newDist = maxDistLower; + } + + newDist = newDist * (dist < 0 ? -1 : 1); + newTime = speed / deceleration; + + return { dist: newDist, time: mround(newTime) }; + }, + + _offset: function (el) { + var left = -el.offsetLeft, + top = -el.offsetTop; + + while (el = el.offsetParent) { + left -= el.offsetLeft; + top -= el.offsetTop; + } + + if (el != this.wrapper) { + left *= this.scale; + top *= this.scale; + } + + return { left: left, top: top }; + }, + + _snap: function (x, y) { + var that = this, + i, l, + page, time, + sizeX, sizeY; + + // Check page X + page = that.pagesX.length - 1; + for (i=0, l=that.pagesX.length; i= that.pagesX[i]) { + page = i; + break; + } + } + if (page == that.currPageX && page > 0 && that.dirX < 0) page--; + x = that.pagesX[page]; + sizeX = m.abs(x - that.pagesX[that.currPageX]); + sizeX = sizeX ? m.abs(that.x - x) / sizeX * 500 : 0; + that.currPageX = page; + + // Check page Y + page = that.pagesY.length-1; + for (i=0; i= that.pagesY[i]) { + page = i; + break; + } + } + if (page == that.currPageY && page > 0 && that.dirY < 0) page--; + y = that.pagesY[page]; + sizeY = m.abs(y - that.pagesY[that.currPageY]); + sizeY = sizeY ? m.abs(that.y - y) / sizeY * 500 : 0; + that.currPageY = page; + + // Snap with constant speed (proportional duration) + time = mround(m.max(sizeX, sizeY)) || 200; + + return { x: x, y: y, time: time }; + }, + + _bind: function (type, el, bubble) { + (el || this.scroller).addEventListener(type, this, !!bubble); + }, + + _unbind: function (type, el, bubble) { + (el || this.scroller).removeEventListener(type, this, !!bubble); + }, + + + /** + * + * Public methods + * + */ + destroy: function () { + var that = this; + + that.scroller.style[vendor + 'Transform'] = ''; + + // Remove the scrollbars + that.hScrollbar = false; + that.vScrollbar = false; + that._scrollbar('h'); + that._scrollbar('v'); + + // Remove the event listeners + that._unbind(RESIZE_EV, window); + that._unbind(START_EV); + that._unbind(MOVE_EV); + that._unbind(END_EV); + that._unbind(CANCEL_EV); + + if (!that.options.hasTouch) { + that._unbind('mouseout', that.wrapper); + that._unbind(WHEEL_EV); + } + + if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + + if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime); + + if (that.options.onDestroy) that.options.onDestroy.call(that); + }, + + refresh: function () { + var that = this, + offset, + i, l, + els, + pos = 0, + page = 0; + + if (that.scale < that.options.zoomMin) that.scale = that.options.zoomMin; + that.wrapperW = that.wrapper.clientWidth || 1; + that.wrapperH = that.wrapper.clientHeight || 1; + + that.minScrollY = -that.options.topOffset || 0; + that.scrollerW = mround(that.scroller.offsetWidth * that.scale); + that.scrollerH = mround((that.scroller.offsetHeight + that.minScrollY) * that.scale); + that.maxScrollX = that.wrapperW - that.scrollerW; + that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY; + that.dirX = 0; + that.dirY = 0; + + if (that.options.onRefresh) that.options.onRefresh.call(that); + + that.hScroll = that.options.hScroll && that.maxScrollX < 0; + that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH); + + that.hScrollbar = that.hScroll && that.options.hScrollbar; + that.vScrollbar = that.vScroll && that.options.vScrollbar && that.scrollerH > that.wrapperH; + + offset = that._offset(that.wrapper); + that.wrapperOffsetLeft = -offset.left; + that.wrapperOffsetTop = -offset.top; + + // Prepare snap + if (typeof that.options.snap == 'string') { + that.pagesX = []; + that.pagesY = []; + els = that.scroller.querySelectorAll(that.options.snap); + for (i=0, l=els.length; i= that.maxScrollX) { + that.pagesX[page] = pos; + pos = pos - that.wrapperW; + page++; + } + if (that.maxScrollX%that.wrapperW) that.pagesX[that.pagesX.length] = that.maxScrollX - that.pagesX[that.pagesX.length-1] + that.pagesX[that.pagesX.length-1]; + + pos = 0; + page = 0; + that.pagesY = []; + while (pos >= that.maxScrollY) { + that.pagesY[page] = pos; + pos = pos - that.wrapperH; + page++; + } + if (that.maxScrollY%that.wrapperH) that.pagesY[that.pagesY.length] = that.maxScrollY - that.pagesY[that.pagesY.length-1] + that.pagesY[that.pagesY.length-1]; + } + + // Prepare the scrollbars + that._scrollbar('h'); + that._scrollbar('v'); + + if (!that.zoomed) { + that.scroller.style[vendor + 'TransitionDuration'] = '0'; + that._resetPos(200); + } + }, + + scrollTo: function (x, y, time, relative) { + var that = this, + step = x, + i, l; + + that.stop(); + + if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }]; + + for (i=0, l=step.length; i 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left; + pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top; + time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time; + + that.scrollTo(pos.left, pos.top, time); + }, + + scrollToPage: function (pageX, pageY, time) { + var that = this, x, y; + + time = time === undefined ? 400 : time; + + if (that.options.onScrollStart) that.options.onScrollStart.call(that); + + if (that.options.snap) { + pageX = pageX == 'next' ? that.currPageX+1 : pageX == 'prev' ? that.currPageX-1 : pageX; + pageY = pageY == 'next' ? that.currPageY+1 : pageY == 'prev' ? that.currPageY-1 : pageY; + + pageX = pageX < 0 ? 0 : pageX > that.pagesX.length-1 ? that.pagesX.length-1 : pageX; + pageY = pageY < 0 ? 0 : pageY > that.pagesY.length-1 ? that.pagesY.length-1 : pageY; + + that.currPageX = pageX; + that.currPageY = pageY; + x = that.pagesX[pageX]; + y = that.pagesY[pageY]; + } else { + x = -that.wrapperW * pageX; + y = -that.wrapperH * pageY; + if (x < that.maxScrollX) x = that.maxScrollX; + if (y < that.maxScrollY) y = that.maxScrollY; + } + + that.scrollTo(x, y, time); + }, + + disable: function () { + this.stop(); + this._resetPos(0); + this.enabled = false; + + // If disabled after touchstart we make sure that there are no left over events + this._unbind(MOVE_EV); + this._unbind(END_EV); + this._unbind(CANCEL_EV); + }, + + enable: function () { + this.enabled = true; + }, + + stop: function () { + if (this.options.useTransition) this._unbind('webkitTransitionEnd'); + else cancelFrame(this.aniTime); + this.steps = []; + this.moved = false; + this.animating = false; + }, + + zoom: function (x, y, scale, time) { + var that = this, + relScale = scale / that.scale; + + if (!that.options.useTransform) return; + + that.zoomed = true; + time = time === undefined ? 200 : time; + x = x - that.wrapperOffsetLeft - that.x; + y = y - that.wrapperOffsetTop - that.y; + that.x = x - x * relScale + that.x; + that.y = y - y * relScale + that.y; + + that.scale = scale; + that.refresh(); + + that.x = that.x > 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x; + that.y = that.y > that.minScrollY ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; + + that.scroller.style[vendor + 'TransitionDuration'] = time + 'ms'; + that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + scale + ')'; + that.zoomed = false; + }, + + isReady: function () { + return !this.moved && !this.zoomed && !this.animating; + } +}; + +if (typeof exports !== 'undefined') exports.iScroll = iScroll; +else window.iScroll = iScroll; + +})(); \ No newline at end of file diff --git a/iscroll-sticky.js b/iscroll-sticky.js new file mode 100644 index 0000000..e48490a --- /dev/null +++ b/iscroll-sticky.js @@ -0,0 +1,106 @@ +(function(iScroll) { + + var _transitionTime = iScroll.prototype._transitionTime, + _pos = iScroll.prototype._pos, + vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : + (/firefox/i).test(navigator.userAgent) ? 'Moz' : + 'opera' in window ? 'O' : '', + m = Math; + + iScroll.prototype.enableStickyHeaders = function (selector) { + return new iScrollStickyHeaders(this, selector); + }; + + var iScrollStickyHeaders = function (iscroll, selector) { + if (!iscroll.options.useTransform) { + return; + } + + this.iscroll = iscroll; + this.selector = selector; + + this.initialize(); + } + + iScrollStickyHeaders.prototype = { + + headers: [], + + initialize: function () { + var that = this; + + this._augment(); + + this.iscroll.options.onRefresh = function () { + that.refresh(); + }; + this.iscroll.refresh(); + }, + + refresh: function () { + var i, ii, + elms = this.iscroll.scroller.querySelectorAll(this.selector); + + for (i = 0, ii = elms.length; i < ii; i++) { + var header = { + elm: elms[i], + minY: elms[i].offsetTop, + maxY: elms[i].offsetHeight + elms[i].offsetTop + }, + prevHeader = this.headers[i - 1]; + + if (prevHeader) { + prevHeader.maxY = m.abs(prevHeader.maxY - header.minY); + } + + header.elm.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(.33, .66, .66, 1)'; + header.elm.style[vendor + 'TransitionProperty'] = '-' + vendor.toLowerCase() + '-transform'; + header.elm.style[vendor + 'TransitionDuration'] = '0'; + header.elm.style[vendor + 'TransformOrigin'] = '0 0'; + + this.headers.push(header); + } + }, + + _position: function () { + var roundedAbsY = m.round(m.abs(this.iscroll.y)), + preventTranslate = this.iscroll.y > 0; + + for (var i = 0, ii = this.headers.length; i < ii; i++) { + var header = this.headers[i], + translateY = roundedAbsY - header.minY; + + if (preventTranslate || translateY < 0) { + translateY = 0; + } else if (translateY > header.maxY) { + // Make sure it never exceeds it's max allowed position + translateY = header.maxY; + } + + header.elm.style[vendor + 'Transform'] = 'translate3d(0, ' + translateY + 'px, 0)'; + } + }, + + _transition: function (time) { + for (var i = 0, ii = this.headers.length; i < ii; i++) { + this.headers[i].elm.style[vendor + 'TransitionDuration'] = time + 'ms'; + } + }, + + _augment: function () { + var that = this; + + this.iscroll._pos = function () { + _pos.apply(this, [].slice.call(arguments)); + that._position(); + }; + + this.iscroll._transitionTime = function (time) { + _transitionTime.apply(this, [].slice.call(arguments)); + that._transition(time); + }; + } + + }; + +}(window.iScroll)); From 8b4f0b3590b39c86f834c6603b79d22c20c09aa6 Mon Sep 17 00:00:00 2001 From: Peter Rudolfsen Date: Sun, 18 Dec 2011 18:20:50 +0100 Subject: [PATCH 3/7] Fixed examples. --- iscroll-src.js => iscroll.js | 0 transition.html | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename iscroll-src.js => iscroll.js (100%) diff --git a/iscroll-src.js b/iscroll.js similarity index 100% rename from iscroll-src.js rename to iscroll.js diff --git a/transition.html b/transition.html index 386424e..e814d7f 100644 --- a/transition.html +++ b/transition.html @@ -150,7 +150,7 @@

K

- + + + diff --git a/iscroll.js b/examples/iscroll.js similarity index 99% rename from iscroll.js rename to examples/iscroll.js index 5ce0f39..1da05a7 100644 --- a/iscroll.js +++ b/examples/iscroll.js @@ -18,7 +18,7 @@ var m = Math, has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), hasTouch = 'ontouchstart' in window && !isTouchPad, hasTransform = vendor + 'Transform' in document.documentElement.style, - hasTransitionEnd = true, + hasTransitionEnd = 'ontransitionend' in window || 'on' + vendor + 'transitionend' in window, nextFrame = (function() { return window.requestAnimationFrame diff --git a/regular.html b/examples/regular.html similarity index 96% rename from regular.html rename to examples/regular.html index 908cfab..dd03bdd 100644 --- a/regular.html +++ b/examples/regular.html @@ -68,7 +68,7 @@

E

- + - +