//kdickson@questacon.edu.au //1 June 2009 var ImageScroller = new Class({ Extends: Fx.Elements, options: { autoscroll: 50, //if the mouse is within this many pixels of the edge, the image will autoscroll scrollspeed: 25, //if autoscrolling, the image will speed up by this amount the closer the mouse is to the edge interval: 25 //the interval between checking if the mouse is near the edge, lower is smoother but more processor intensive }, initialize: function(sb, si, options) { this.setOptions(options); this.sb = $(sb); //transparent element displaying a scroll bar this.si = $(si); //element we'll actually scroll this.sbSize = this.sb.getSize(); this.sbScrollSize = this.sb.getScrollSize(); this.siSize = this.si.getSize(); this.siScrollSize = this.si.getScrollSize(); this.startScroll = {'x': 0, 'y': 0}; //get the dimensions of a scroll bar on this client var inner = new Element('div', {style: 'width: 100px; height: 100px;'}); var outer = new Element('div', {style: 'visibility: hidden; position: absolute; left: 0; top: 0; width: 100px; height: 100px;'}).adopt(inner).injectInside(document.body); var w1 = inner.offsetWidth; var h1 = inner.offsetHeight; outer.setStyle('overflow', 'scroll'); var w2 = inner.offsetWidth; var h2 = inner.offsetHeight; if (w1 == w2) w2 = outer.clientWidth; if (h1 == h2) h2 = outer.clientHeight; outer.dispose(); this.scrollBarSize = {'x': w1 - w2, 'y': h1 - h2}; if (this.sb.getStyle('overflow-x') != 'scroll') this.scrollBarSize.y = 0; if (this.sb.getStyle('overflow-y') != 'scroll') this.scrollBarSize.x = 0; this.isMouseMove = false; this.isScroll = false; this.isTickTock = false; this.sb.addEvent('scroll', function(event) { this.scroll(event); }.bind(this)); //center the image this.adjustImage({'x': this.siScrollSize['x'] / 4, 'y': this.siScrollSize['y'] / 4}); }, start: function(event) { //start listening to mouse movements this.sbPosition = this.sb.getPosition(); this.siPosition = this.si.getPosition(); this.startMouse = this.getMousePosition(event); this.startScroll = this.si.getScroll(); this.sb.addEvent('mousemove', function(event) { this.mousemove(event); }.bind(this)); if (!this.timer) this.timer = this.ticktock.periodical(this.options.interval, this); }, stop: function(event) { //stop listening to mouse movements this.sb.removeEvent('mousemove'); this.timer = $clear(this.timer); }, getMousePosition: function(event) { return (this.sb == window) ? event.client : event.page; }, onscrollbar: function(curMouse) { //returns true if the mouse pointer is over the scroll bar for (var z in curMouse) { if (this.scrollBarSize[z] > 0 && curMouse[z] > (this.siSize[z] + this.siPosition[z] - this.scrollBarSize[z])) { return true; } } return false; }, scroll: function(event) { if (this.isMouseMove) return; if (this.isTickTock) return; this.isScroll = true; //calculate the percentage the scroll bar represents sbScroll = this.sb.getScroll(); var percentScroll = {'x': 0, 'y': 0}; for (var z in percentScroll) { percentScroll[z] = sbScroll[z] / this.sbScrollSize[z] * 2; } //calculate the new image position siSize = this.si.getSize(); siScrollSize = this.si.getScrollSize(); var newScroll = {'x': 0, 'y': 0}; for (var z in newScroll) { newScroll[z] = ((siScrollSize[z] / 2)) * percentScroll[z]; } //move into position this.si.scrollTo(newScroll.x, newScroll.y); this.startScroll = newScroll; this.isScroll = false; }, mousemove: function(event) { if (this.isScroll) return; if (this.isTickTock) return; //ignore this if we're over the scroll bar var curMouse = this.getMousePosition(event); if (this.onscrollbar(curMouse)) { //we need to preserve the event location, just in case the ticktock is the next event // as ticktock is on a timer it doesn't receive events therefore can't get the mouse loc on its own this.startMouse = curMouse; return; } this.isMouseMove = true; //figure out the delta from the last mouse event var changeMouse = {'x': 0, 'y': 0}; for (var z in changeMouse) { changeMouse[z] = this.startMouse[z] - curMouse[z]; } this.startMouse = curMouse; //do the move this.adjustImage(changeMouse); this.isMouseMove = false; }, ticktock: function() { if (this.isScroll) return; if (this.isMouseMove) return; //ignore this if we're over the scroll bar var curMouse = this.startMouse; if (this.onscrollbar(curMouse)) return; this.isTickTock = true; //figure out the mouse is near the edges of the image, defined in the "autoscroll" option this.siPosition = this.si.getPosition(); var changeMouse = {'x': 0, 'y': 0}; for (var z in changeMouse) { autoScrollLeft = this.siPosition[z] + this.startScroll[z]; autoScrollRight = autoScrollLeft + this.siSize[z]; if (curMouse[z] < (autoScrollLeft + this.options.autoscroll)) { //left, top percentScrollspeed = 1 - ((curMouse[z] - autoScrollLeft) / this.options.autoscroll); changeMouse[z] = percentScrollspeed * this.options.scrollspeed; } else if (curMouse[z] > (autoScrollRight - this.options.autoscroll)) { //right, bottom percentScrollspeed = 1 - ((autoScrollRight - curMouse[z]) / this.options.autoscroll); changeMouse[z] = percentScrollspeed * this.options.scrollspeed * -1; } } //do the move this.adjustImage(changeMouse); this.isTickTock = false; }, adjustImage: function(changeMouse) { //calculate the new position to match the mouse var newScroll = {'x': 0, 'y': 0}; for (var z in newScroll) { newScroll[z] = this.startScroll[z] + changeMouse[z]; //adjust if we're about to position beyond the extents if (newScroll[z] < (this.siSize[z] / 2)) { newScroll[z] += (this.siScrollSize[z] / 2); } else if (newScroll[z] > (this.siScrollSize[z] - this.siSize[z])) { newScroll[z] -= (this.siScrollSize[z] / 2); } } //move into position this.si.scrollTo(newScroll.x, newScroll.y); this.startScroll = newScroll; //calculate the percentage the image represents var percentScroll = {'x': 0, 'y': 0}; for (var z in percentScroll) { percentScroll[z] = (this.startScroll[z] % (this.siScrollSize[z] / 2)) / (this.siScrollSize[z] / 2); } //calculate the new scroll bar position var newScroll = {'x': 0, 'y': 0}; for (var z in newScroll) { newScroll[z] = this.sbSize[z] * percentScroll[z]; } //move into position this.sb.scrollTo(newScroll.x, newScroll.y); } }); ImageScroller.implement(new Events, new Options);