Web Widget - Make an image zoom guide function

這個功能主要是參考yahoo奇摩在購物時,會提供給使用者某項便利的服務。例如:當您在

觀看某一項商品的細部內容時,一開始會有一張圖,此圖可能是縮小版的,因此當您滑鼠

移入該商品時,右邊的區塊就會呈現你滑鼠移入的局部放大圖示!因此本篇文章就是在探

討這個功能大概是如何實作的。

功能示範如下(來源:yahoo奇摩購物中心頁面,相關連結):

正常頁面

滑鼠移入(注意左方的透明方框)

在這邊只是補充這功能的說明。


==========================正文如下==========================

首先要說明的是,原圖大小1000 x 1000 px、左圖400 x 400 px、小方框160 x 160 px

所以,圖與圖之間比例為2.5。1000/2.5 = 400, 400/2.5 = 160

因此,當你在左圖的方框圈選到的區域(160x160),在右邊(400 x 400長寬的限制下)

只會呈現1000x1000的某一塊400x400 px區域,可見下面的簡易圖:

若方框移到 left : 240的位置,換算成右圖1000x1000px只能呈現他內圖的(-600, 0)延伸

長、寬各400的區域!




所以,圖導覽的方式是以這種比例與概念去呈現的!

接下來就是自行實作的程式碼部分如下:

HTML部分:

<div style="width:820px;">
   <div id="source_block">
      <div id="guide_square"></div>
      <img id="source" />
   </div>
   <div id="tip_info">...</div>
   <div id="enlarge"></div>
</div>

CSS相關語法:
body{
   margin: 0;
   padding: 50px;
}
#guide_square{
   position:absolute;
   display:none;
   width:160px;
   height:160px;
   background-color: #ccc;
   opacity: 0.5;
   z-index:2;
   cursor: move;
}
#source_block{
   width: 400px;
   float: left;
}
#tip_info{
   float:right;
   border: 1px solid #000000; 
   width:400px;
   height:400px;
}
#enlarge{
   float:right; 
}

在這邊會先設定一個外圍的div區塊,寬度820px

source_block是左圖的區塊,裡面包含縮小圖(400x400)方框(160x160)

tip_info區塊為右圖區塊的說明(以yahoo來想像的話是產品說明)

enlarge區塊為呈現方框導覽到的區域

JavaScript&jQuery部分

1. 初始化部分

var img_obj = new Image();
var SCALE = 2.5;
img_obj.src = "CIMG4428.JPG";
var guide_obj = null;
guide_obj = document.getElementById('guide_square');
   img_obj.onload = function(){
      $("li").attr("type", "none");
      $("#source").css({
         "width" : img_obj.width/SCALE,
         "height": img_obj.height/SCALE,
         "background-size" : (img_obj.width/SCALE)+"px "+(img_obj.height/SCALE)+"px",
         "background-image": "url("+img_obj.src+")"
      });
      $("#enlarge").css({
         "display" : "none",
         "width" : img_obj.width/SCALE,
         "height": img_obj.height/SCALE,
         "background-size" : img_obj.width+"px "+img_obj.height+"px",
         "background-image": "url("+img_obj.src+")"
      });
   };

這個部分會載入指定的圖片(1000x1000),並且分別定義source_block區塊與enlarge區塊

的圖片設定。請注意enlarge部分,background-size為原圖大小,但是長寬只呈現400x400!


2. 進入source區域(左圖)

var m_curr = {
   state: false,
   x : 0,
   y : 0
};
var DEFAULT_START_POS = 80;
var MIN_POS = 50;
var MAX_POS = MIN_POS + 240;
$("#source").mouseenter(function(e){
   if(!m_curr.state){
      m_curr.x = outside_check(e.pageX-DEFAULT_START_POS, MIN_POS, MAX_POS);
      m_curr.y = outside_check(e.pageY-DEFAULT_START_POS, MIN_POS, MAX_POS);
      $("#guide_square").css({
         display: "block",
         top: m_curr.y,
         left: m_curr.x
      });
      $("#tip_info").hide();
      $("#enlarge").show();
      m_curr.state = true;
   } 
});
function outside_check(value, bottom, top){
  if(value < bottom){
     value = bottom;
  }else if(value > top){
     value = top;
  }
  return value;
}

這個部分是定義,當你的滑鼠移進左圖時,會帶出方框,但為什麼要減去

DEFAULT_START_POS,目的是當你滑鼠一移進去,剛好在方框正中間的位置!

而outside_check的目的就是調整方框不要超出左圖的範圍,最小是50,最大是290

照理說應該是0跟240,因為我調整body padding:50,因此得多算這50的部分!


3. 移動方框

var delta = {
   move_x: false,
   move_y: false,
   x : 0,
   y : 0
};
$(document).mousemove(function(e){
   if(m_curr.state && (e.target.nodeName == 'BODY' || e.target.nodeName == 'HTML')){
      reset_setting();
   }
   if(m_curr.state){
      delta.x = e.pageX - (guide_obj.offsetLeft + DEFAULT_START_POS);   
      delta.y = e.pageY - (guide_obj.offsetTop  + DEFAULT_START_POS);
      if(Math.abs(delta.x) > DEFAULT_START_RANGE && !delta.move_x){
         delta.x = 0;
      }else{
         delta.move_x = true;
      }
      if(Math.abs(delta.y) > DEFAULT_START_RANGE && !delta.move_y){
         delta.y = 0;
      }else{
         delta.move_y = true;
      }
      m_move_x = outside_check(guide_obj.offsetLeft + delta.x, MIN_POS, MAX_POS);
      m_move_y = outside_check(guide_obj.offsetTop  + delta.y, MIN_POS, MAX_POS);
   
      guide_obj.style.left = m_move_x+"px";
      guide_obj.style.top  = m_move_y+"px";
   
      start_pos = "-"+(m_move_x-MIN_POS)*SCALE+"px -"+(m_move_y-MIN_POS)*SCALE+"px"
      $("#enlarge").css({
         "background-position" : start_pos,
      });
   }
});
$("#guide_square").mouseout(function(e){
   if(m_curr.state){
      reset_setting();
   } 
});
function reset_setting(){
    guide_obj.style.display = 'none';
    $("#guide_square").hide();
    $("#enlarge").hide();
    $("#tip_info").show();
    delta.move_x = false;
    delta.move_y = false;
    m_curr.state = false;
}

當進入方框後,此時m_curr.state = true,因此就會執行

delta.x = e.pageX - (guide_obj.offsetLeft + DEFAULT_START_POS);
delta.y = e.pageY - (guide_obj.offsetTop  + DEFAULT_START_POS);

上面這兩段code,e.pageX, e.pageY為當下滑鼠移動位置減去方框當下的座標(左上頂點)

此時方框還不會移動,因為在此設定要接近方框的正中間即可以自由移動!

所以才加DEFAULT_START_POS,否則delta.x = 0, delta.y = 0(表次原地踏步)

緊接著,當方框移動時也得去檢查是否過界!

最後就是去計算當下右圖的呈現座標位置(由於是圖的一部分,因此座標是負的)

最後,滑鼠移出左圖後,方框就會消失!但您若移很快方框還是會跟著動,因為在這邊

的mousemove選擇器是塞document XD

PS. 當方框可以移動後,就不用限制一定要在正中間才會移動,所以多了delta.move_x判斷,

一旦可以移動基本上就不需要考慮正中間的問題,否則移動起來會卡卡的。

成果圖如下:

初始畫面



一進去左圖,方框停在正中間,可以上下移動


移動至正中間方框,才可以左右移動



問題探討:

最後有碰到一個問題,就是當滑鼠移動很快的時候,會出現一種情況,請見示意圖

這是我寫的code的特性碰到的問題。


我的code是設定一移出方框就會執行

m_curr.state = false; // 對方框失去移動控制作用

但是$("#source").mouseenter(..)事件觸發是在滑鼠一碰到左圖

m_curr.state等於false時,m_curr.state 會設成 true;

假設,今天我移動飛快(方框跟不上),此時出方框區域 (1),雖然m_curr.state = false;

但此時還在左圖區域,又觸發$("#source").mouseenter(..)事件,m_curr.state 會設成 true,

屆時,我雖然移出左圖區域 (2),但是還可以控制方框囉(m_curr.state = true)!

面對這種情況,我是在mousemove多加了一個判斷

m_curr.state && (e.target.nodeName == 'BODY' || e.target.nodeName == 'HTML')

只要一出這個區域,若m_curr.state = true,且target = BODY or HTML強制reset

,因此就不會出現上述的bug囉!

PS. 最後,如果您要demo這個功能,可以去yahoo的頁面玩玩看XD,說真的這個功能頗實

用的,因為網頁版面沒有那麼大,但如果要看到比較大的圖,用這種方式導覽還不錯囉!

留言