/**
* @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()