/** * @fileoverview This code can help smb to zoom in/out and scroll big images * Tested in IE6, Mozila FireFox 2.0.0.2, Opera 7.23 * * This code is free software, so you can redistribute it and/or * modify it (even eat) under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation. * See licence on http://www.gnu.org/copyleft/lesser.html (or in the package) * * This code is distributed 'cause it could be useful, but WITHOUT ANY GUARANTEE!!! * * @author KSNK {@link http://forum.dklab.ru/users/ksnk/} * @special_thanks SiNaC0m {@link http://forum.dklab.ru/users/SiNaC0m/} * @special_thanks AKS {@link http://forum.dklab.ru/users/aks/} * @version donow :( * @charcode windows-1251 * @detail_description основной смысл -- пробежаться по коду и все картинки с классом "panorama" сделать скроллируемыми и зуммируемыми примерный алгоритм по window.onLoad для каждой картинке обрамляющий ее div устанавливаем - style.overflow='hidden', дабы неповадно было - ожидаем подгрузки картинки, после чего вешаем обработчик onmouseover по приходу в картинку - рисуем колесо прокрутки (roller) верхнем-правом углу - устанавливаем обработчики мыши, чтобы не потерять выход за границу - ставим обработчик клавиатуры. по выходу из картинки - вертаем все в зад устройство такой картинки -
после "подготовки" у DIV'а появится style.overflow=hidden, класс у картинки - пропадет, сама она "впишется" в рамку DIV'а примерный вид Роллера : или или даже так стиль роллера так или иначе должен содержать атрибут position:absolute; все внутренние элементы, содержащие атрибут href вида #ACTION , где ACTION - поля структуры hstore далее по тексту, будет снабжен методами onmousedown="doit(ACTION)", onmouseup="clr()", onclick="return false" для настройки скрипта "по месту" можно сразу за тегом скрипта написать еще такое // настройка объекта по месту... параметры и комментарии к ним - см. далее по тексту ******************************************************** */ (__SCROLLZOOM__={ /************************************************************ * настройка параметров ************************************************************ * имя класса IMG, который будем зуммировать */ zoomable:'zoomable', /** * стиль drag'ируемого окна - по умолчанию - отключен */ // picture_dragged:'panorama_dragged', /** * имя роллера */ use_roller:'ROLLER', /** * использовать ли клавиатуру для зума-скролла */ use_keyboard:true, /** * использовать ли колесо мыши для зума */ use_mouseweel:true, /** * список и расшифровка всех действий, которые можно сделать */ hstore:{ // тут храняться все действия // элементы - якоря перехода, которые вставляются в href соответствующего МAP-сегмента lt:['apply_scroll',-4,0], rt:['apply_scroll', 4,0], up:['apply_scroll', 0,-4], dn:['apply_scroll', 0,4], zu:['apply_Zoom', 1.04], zd:['apply_Zoom', 0.96] }, expand: function(prop) { for(var p in prop) this[p]=prop[p] // имеем дело только с простым объектами // return this }, init:function(){ var /************************************************************ * грустная магия Javascript */ apply_Func=function (aObj, aMethod, par,par2 ) { return function () { aObj[aMethod](par,par2) }; }, /************************************************************ * и еще */ apply_EH =function (o,m,p) { return function (e){return o[m](e,p) } }, /************************************************************* * если клавиатура используется - соответствие клавишей действиям */ // 38 keymap={38:'up',37:'lt',40:'dn',39:'rt',109:'zd',107:'zu'}, // 37 40 39 // +- /************************************************************ * поиск элемента по имени - сокращенная запись */ _i=function (e){return( document.getElementById(e) ||document.getElementsByName(e)[0])}, /************************************************************ * установка обработчиков событий */ add_Handler= function(a,e,o){ if (a.addEventListener) a.addEventListener(e,o,false) else if (a.attachEvent) { try { a.attachEvent('on' + e, o); } catch (aEx) {} } return{a:a,e:e,o:o} // для автоматической чистки хендлов!!! }, clear_handlers=function(_h) { var h; while(h=_h.pop()){ if (h.a.removeEventListener) { h.a.removeEventListener(h.e, h.o, false); } else if (h.a.detachEvent) { try { h.a.detachEvent('on' + h.e, h.o); } catch (aEx) {} } } }, /************************************************************ * вычисление координат элемента в "абсолютных" числах */ getBounds=function (e) { for (var parent = e.offsetParent,left=0,top=e.offsetTop; parent; parent = parent.offsetParent) { left += parent.offsetLeft; top += parent.offsetTop; } return {left: left, top: top, width: e.offsetWidth, height: e.offsetHeight}; }, clear=function (e) { if (e.preventDefault) e.preventDefault() else e.returnValue = false; return false; }, /************************************************************ * устанавливаем */ stopdrag=function(img) { // грязно избавляемся от width и height в классе!!! // После этой операции возможен уезд крыши у дизайна img.img.className='' img.dragged=false; }, /************************************************************ * разМЫШления о browser-undependense */ mouse_target = window, mouse_event = (window.execScript || window.opera) ? (mouse_target = document, 'mousewheel') : 'DOMMouseScroll', mouse_events=['mousemove','mousedown','mouseup'], th=this,preloads=[], Timer // + нечаянная проверка на "неглобальность" if (this.use_mouseweel) mouse_events.push(mouse_event) /************************************************************ * местный onLoad. * утверждается, что эта функция будет запущена при готовом body */ this.onload= function() { // бегаем по всему, что шевелится и корежим ссылки!!! // работаем с роллером. все ссылки с #XXX дополняем doit(XXX) this.roller = _i(this.use_roller); var o= this.roller && this.roller.firstChild, stack=[null] do { if(!o) { o=stack.pop(); continue // go up } if (o.href) { var t=o.href.replace(/.*#/,'').toLowerCase() if (this.hstore[t]) { add_Handler(o,'mousedown',apply_Func(this,'doit',t)); add_Handler(o,'click',function(e){return clear(e)}); } } if (o.firstChild) { stack.push(o.nextSibling); o=o.firstChild; // go deep } else o=o.nextSibling } while(stack.length) // var cs = document.defaultView.getComputedStyle(img.img,''); // ставим на ожидание загрузку картинок var x=document.getElementsByTagName('img'), aimages=[]; for(var i=x.length-1;i>=0;i--) { // надо бы и про композитные классы продумать if (typeof x[i].className =="string") if (x[i].className==this.zoomable) { x[i].galleryimg='no' // кусочек магии от IE //var cs = document.defaultView.getComputedStyle(x[i].parentNode,''); // коррекция x[i].parentNode.style.overflow='hidden'; var img={ img:x[i], // preload:null, // realWidth:100, // realHeight:100, // overflow:cs.overflow, marginLeft:0, marginTop:0 }; (img.preload=preloads[x[i].src]?preloads[x[i].src]:(preloads[x[i].src]=new Image())).src=x[i].src; aimages.push(img); } } // поставить ожидание загрузки картинок var timercheck= function() { cont=0 ; // чекаем до полного просветления... for (var i=0;i0 && img.realHeight>0) { // "раздуть" картинку на весь доступный регион img.img.height=img.img.parentNode.clientHeight; img.img.widht=img.img.parentNode.clientWidth; // перерасчет ZoomFactor // так - разьезжаемся вширь! img.ZoomFactor=img.img.widht/img.realWidth; // а вот так - вписываемся // img.ZoomFactor=Math.min(img.img.height/img.realHeight,img.img.widht/img.realWidth); add_Handler(img.img.parentNode,'mouseover', apply_Func(th,'mouseover',img)); th.apply_Zoom(1,img); } img.preload=null; // чистим хвосты... } } } if (cont) setTimeout(timercheck,100); // просветлились ? } setTimeout(timercheck,50) } /************************************************************ * устанавливаем обработчик onLoad */ add_Handler(window, 'load',apply_Func(this, 'onload')); // var hide_roller_timer, show_roller=function() { th.roller.style.visibility='visible'; hide_roller_timer=null }, hide_roller=function(noshow) { if (th.roller.style.visibility!='hidden') th.roller.style.visibility='hidden'; if (hide_roller_timer) { clearTimeout(hide_roller_timer); hide_roller_timer = null; } if (noshow) return; hide_roller_timer=setTimeout(show_roller,500) } /************************************************************ * общий обработчик мыша */ this.getClick=function(e,img){ e= (window.event) ? window.event : e; // мы можем болтаться по parent текущей картинки и roller'у for (var o= e.target || e.srcElement; o && o!=img.img.parentNode && o!=this.roller; o = o.parentNode ){} switch (e.type) { case 'mousemove': /** * для того, чтобы не потерять mouseout, мы будем его смотреть так. */ if (!o) { // вылезли за границы ... this.mouseout(img); } else if (img.dragged) { hide_roller() var x=img.startx,y=img.starty; this.apply_scroll( x-(img.startx=e.clientX), y-(img.starty=e.clientY)) } else if (Timer && o!=this.roller) this.clr() break case mouse_event: // weel!!!!!!! /** * обработчик колеса мышки - Зумм+/- */ var delta =(e.detail ?e.detail/3 :e.wheelDelta ?e.wheelDelta/60 :0) if (delta) { hide_roller() this.apply_Zoom(1-0.04 * delta); } break case "mousedown": if (o==img.img.parentNode){ hide_roller() img.dragged=true img.startx=e.clientX img.starty=e.clientY // начинаем драг if(this.picture_dragged) img.img.className=this.picture_dragged } break case "mouseup": if(img.dragged) stopdrag(img); if (Timer)this.clr() break } return clear(e); } // обработчик клавиатуры this.keyb_func=function(e,img) { var x=keymap[(e||window.event).keyCode]||''; if (this.hstore[x]) { hide_roller() this.doit(x,true); } //else alert((e||window.event).keyCode) return clear(e); } var handles=[] this.mouseover= function(img) { // ставим обработчик mousemove // показываем в правом-верхнем углу роллер if(this.currentimg && this.currentimg!=img) this.mouseout(this.currentimg); if (!this.currentimg) { var kb_owner= window.HTMLElement? window : document.body, mousemove_func= apply_EH(this,'getClick',img ) stopdrag(img); if(this.use_keyboard) handles.push(add_Handler(kb_owner,'keydown',apply_EH(this,'keyb_func'))) for(var e in mouse_events) handles.push(add_Handler(mouse_target, mouse_events[e], mousemove_func)) this.currentimg=img var r=getBounds(img.img.parentNode); if (this.roller){ this.roller.style.left=(r.left+(r.width-this.roller.offsetWidth)+335)+'px'; this.roller.style.top=(r.top)+'px'; show_roller() } } } /************************************************************ * не обработчик, на самом-то деле. Чистим все, что под руку попадет... */ this.mouseout= function(img) { if (this.currentimg==img) // сравнение ссылок? { clear_handlers(handles) if (this.roller) hide_roller(true) this.currentimg=null; this.clr(); } } /************************************************************ * функция для развешивания на HTML элементах * можно писать onmousedown="__SCROLLZOOM__.doit('lt')" */ this.doit=function (e,single) { if (this.currentimg && !Timer && (e= this.hstore[e])) if (single) { // делаем дважды... this[e[0]](e[1]||0,e[2]||0); this[e[0]](e[1]||0,e[2]||0); }else Timer = setInterval(apply_Func(this,e[0],e[1]||0,e[2]||0), 50); return false; } /************************************************************ * завершение текущей операции */ this.clr=function () { clearInterval(Timer); Timer=null; } }, /************************************************************ * делаем скролл на dl,dt */ apply_scroll:function (dl,dt) { // скроллируем на определенный процент картинки ??? а именно оно надо ??? var img=this.currentimg if(img){ img.img.style.marginLeft = (img.marginLeft-=dl)+'px'; img.img.style.marginTop = (img.marginTop-=dt)+'px'; } }, /************************************************************ * Зумм. Центр остается в центре */ apply_Zoom : function (dx,img) { if (!img) img=this.currentimg; if (img) { if(dx) img.ZoomFactor*=dx; img.img.width=img.realWidth*img.ZoomFactor; img.img.height=img.realHeight*img.ZoomFactor; this.apply_scroll((img.img.parentNode.clientWidth/2-img.marginLeft)*(dx-1), (img.img.parentNode.clientHeight/2-img.marginTop)*(dx-1)) ; } } }).init()