在京東和淘寶等購買東西的時候,咱們會常常預覽左側商品展現圖片,把鼠標放到原圖,右側就會有個大圖顯示出細節。本文將帶領你們寫一個這樣簡單的功能!css
當鼠標移入某一圖片內部時,圖片上部會出現一個相似於掃描的框,這個框內的圖片部分,會以方大形式展現在右邊,以下圖:html
從圖中能夠推測出一下幾點:git
圖片img上層會有一個父元素(如‘div’),在鼠標移入時,父元素內部添加一個子元素表明掃描框,而且整個body會出現一個固定定位的圖片預覽盒子定位在右側(這個圖片是另一張準備好的大圖),展現着掃描框中掃描到的圖片位置,這個掃描框不能在div內部移動,當鼠標移出圖片,掃描框和展現臺都消失。github
所以咱們獲得如下佈局app
<!--整個盒子--> <div> <!--圖片--> <img src="..."> <!--掃描框--> <div class="sweep"></div> </div> <!--掃描展現區域--> <div class="show"> </div>
實際狀況下,咱們不會手動寫上 .sweep 和 .show這兩個空div,他是由js來實現的。所以,今天咱們練習的佈局代碼以下dom
<div> <img src="..." /> </div>
今天,博主準備了兩個練習圖片,一個 200*200用做原圖, 一個400*400用做大圖展現。函數
html:佈局
<div id="box" data-big-img="goods-big.gif"> <img src="goods.gif" alt="咖啡" /> </div>
#box { border: 2px solid #000; width: 200px; height: 200px; margin: 0 auto; }
咱們牢牢須要設置圖片盒子的樣式就能夠了,注意: 圖片盒子須要把寬高設定爲圖片大小200*200。this
js函數參數選定:url
// 將函數命名爲zoom,接收兩個參數, // 第一個參數是原圖的盒子#box, 第二個是對大圖展現臺的設定 function zoom (elem, options) {...} // 記住,一切與圖片打交道的,都放在window.onload內 window.onload = function () { var box = document.querySelector('#box') // 這裏咱們把展現臺設置爲圖片大小 zoom(box, { offsetWidth: 200, // 展現臺寬度 offsetHeight: 200, // 展現臺高度 offsetX: 10, // 展現臺相對圖片盒子的橫向偏移 offsetY: 0 // 展現臺相對圖片盒子的縱向偏移 }) }
對各個元素尺寸的解釋:
原圖200* 200, 大圖預覽是原圖的2倍。
掃描框100*100, 是原圖的1/2
展現臺與原圖大小相同,展現臺中顯示圖片爲400*400的大圖作背景圖,控制其背景圖的位置來改變展現圖的圖樣。
1. 執行zoom函數,咱們須要獲取到掃描框和展現臺,若是沒有,就建立。
2. 給圖片添加一個onmouseenter事件,在鼠標移入圖片,觸發函數,顯示展現臺和掃描框,而且展現臺的圖片內容就是掃描框掃描到的圖片區域的放大部分。
3. 鼠標移出,展現臺和掃描框消失。
4. 鼠標在圖片上移動,咱們在這裏給掃描框添加onmousemove事件,鼠標位置始終在掃描框的中心位置(掃描框緊貼圖片一側除外),只有在鼠標移出了圖片區域,鼠標纔會離開掃描框。
5. 展現臺的圖片隨着掃描框的移動而變化到相應的部分。
開始代碼以下:
window.onload = function () { var box = document.getElementById('box') zoom(box, { offsetWidth: 300, offsetHeight: 300, offsetX: 10, offsetY: 0 }) } function zoom (elem, options) { // .. }
以後的代碼都會在zoom函數內部。
首先,咱們想一下,掃描框.sweep應該在#box內部,其移動是如何實現的,答案是定位,所以#box須要設定爲相對定位給.sweep提供環境
// 將盒子設定爲相對定位,供以後內部的掃描框用 elem.style.position = 'relative'
因爲掃描框的寬高依據圖片所定,因此咱們先拿到圖片的寬高
var innerImg = elem.querySelector('img'), width = innerImg.offsetWidth, height = innerImg.offsetHeight
咱們須要獲取.sweep (掃描框)和 .show(展現臺) 兩個dom元素
var showBox = getShowBox() // 獲取展現臺盒子 var sweepBox = getSweepBox() // 獲取掃描框盒子
因爲咱們在html沒有手動添加兩個元素,咱們須要先建立他,getShowBox以下:
function getShowBox () { var showBox = document.querySelector('.xu-show-box') if (!showBox) { showBox = document.createElement('div') showBox.className = 'xu-show-box' // 糟糕的樣式添加操做 showBox.style.width = (options.offsetWidth || 400) + 'px' showBox.style.height = (options.offsetHeight || 400) + 'px' showBox.style.position = 'fixed' showBox.style.left = elem.offsetLeft + elem.offsetWidth + (options.offsetX || 10) + 'px' showBox.style.top = elem.offsetTop + (options.offsetY || 0) + 'px' showBox.style.background = 'url(' + elem.getAttribute('data-big-img') + ')' showBox.style.display = 'none' document.body.appendChild(showBox) } return showBox }
咱們先獲取到展現臺元素,若是沒有建立,而後定義了一大串css,而後將它加入到body中,咱們能夠看到一大串的showBox.style很糟糕,咱們須要一個css樣式修改函數。
function setStyle(elem, props, value) { if (typeof props === 'object') { // 傳入的對象 for (var key in props) { elem.style[key] = props[key] } } else { elem.style[props] = value } }
咱們接下來用setStyle來設定樣式,代碼變成了以下:
function getShowBox () { var showBox = document.querySelector('.xu-show-box') if (!showBox) { showBox = document.createElement('div') showBox.className = 'xu-show-box' setStyle(showBox, { width: (options.offsetWidth || 400) + 'px', height: (options.offsetHeight || 400) + 'px', position: 'fixed', left: elem.offsetLeft + elem.offsetWidth + (options.offsetX || 10) + 'px', top: elem.offsetTop + (options.offsetY || 0) + 'px', background: 'url(' + elem.getAttribute('data-big-img') + ')', display: 'none' }) document.body.appendChild(showBox) } return showBox }
看起來好多了,咱們再獲取sweep
function getSweepBox () { var sweepBox = elem.querySelector('.xu-sweep-box') if (!sweepBox) { showBox = document.createElement('div') showBox.className = 'xu-sweep-box' setStyle(sweepBox, { border: '1px solid #44f', width: width / 2 - 2+ 'px', height: height / 2 - 2 + 'px', background: '#ff0', opacity: '.4', position: 'absolute', display: 'none', cursor: 'move' }) elem.appendChild(sweepBox) } return sweepBox }
目前咱們的已經獲取到了展現臺和掃描框,目前的代碼以下:
window.onload = function () { var box = document.getElementById('box') zoom(box, { offsetWidth: 300, offsetHeight: 300, offsetX: 10, offsetY: 0 }) } function zoom (elem, options) { elem.style.position = 'relative'
var innerImg = elem.querySelector('img'),
width = innerImg.offsetWidth,
height = innerImg.offsetHeight
var showBox = getShowBox() var sweepBox = getSweepBox() getShowBox(){...} getSweepBox() {...} setStyle(){...} }
接下來,咱們開始書寫鼠標事件的邏輯,在這以前,咱們想一下咱們的需求,以及元素尺寸的概念:
由於掃描框大小是圖片大小的一半,所以掃描框定位取值:
left: 0 到 圖片寬度的一半(也就是掃描框的寬度)
top: 0 到 圖片高度的一半(也就是掃描框的高度)
鼠標移入圖片的位置不一樣,決定掃描框的出現位置:
從左上角移入:左上角
從左下角移入: 左下角
從右上角移入: 右上角
從右下角移入: 右下角
移入的樣子以下:
掃描框的運動是否容許,須要對鼠標位置的判斷,拿左上角移入舉例:若是鼠標移動到掃描框的中心位置並繼續向右移動,此時掃描框纔會移動,若是鼠一直在掃描框的左上部分移動,掃描框是不會移動的。
接下來咱們須要獲取以下數據:
掃描框寬高度,掃描框移動的度量寬高度
// 掃描框寬高 var sweepW = width / 2, sweepH = height / 2, // 掃描框移動的度量寬高 stepW = sweepW / 2, stepH = sweepH / 2
此時,咱們作好了鼠標移入的準備工做,咱們能夠開始編寫移入事件函數了
elem.onmouseenter = function (ev) { // 根據鼠標的位置,加載掃描框和展現臺 load(ev.offsetX, ev.offsetY) }
load函數以下:
function load (x, y) { // 掃描框的橫縱座標偏移量 var offsetX = offsetY = 0 // 不知用什麼switch表達式好,因此用了以下方法來判斷位置,你有沒有好方法? switch ([(x-sweepW) > 0, (y-sweepH) > 0].join(',')) { case 'false,true': // 左下 offsetY = sweepH break; case 'false,false': // 左上 break; case 'true,false': // 右上 offsetX = sweepW break; case 'true,true': // 右下 offsetX = sweepW offsetY = sweepH break; } setStyle(sweepBox, { left: offsetX + 'px', top: offsetY + 'px', display: 'block' }) // 因爲咱們起初設定的展現圖是原圖的2倍,因此偏移都*2 setStyle(showBox, { backgroundPositionX: offsetX * 2 + 'px', backgroundPositionY: offsetY * 2 + 'px', display: 'block' }) }
加載完畢後,再寫鼠標移動事件,根據咱們的需求,咱們須要根據不一樣方位,不一樣鼠標座標,來判斷掃描框是否可運動,咱們經過需求分析,咱們選擇的給掃描框加的鼠標移動事件,以下
sweepBox.onmousemove = function (e) { if (!isMove(e)) { return } // 鼠標移動的距離 var moveX = e.offsetX - stepW var moveY = e.offsetY - stepH // 掃描框的偏移量 var offsetL = this.offsetLeft var offsetT = this.offsetTop // 計算出移動的最終座標 var toX, toY // 沿x軸往右移動,而且掃描框右邊界尚未碰到圖片右邊緣,那麼能夠移動,而且移動的距離最遠到圖片右邊緣 if (moveX > 0 && offsetL < sweepW) { toX = Math.min(offsetL + moveX, sweepW) } // 與之相反,沿x軸往左移動,那麼判斷左邊界未碰到圖片左邊緣,移動而且移動最左只能到0 if (moveX < 0 && offsetL > 0) { toX = Math.max(offsetL + moveX , 0) } // y軸雷同 if(moveY > 0 && offsetT < sweepH) { toY = Math.min(offsetT + moveY, sweepH) } if (moveY < 0 && offsetT > 0) { toY = Math.max(offsetT + moveY, 0) } // 每次移動,分別設置掃描框和展現臺的相應數據 setStyle(this, { left: toX + 'px', top: toY + 'px' }) setStyle(showBox, { backgroundPositionX: -toX * 2 + 'px', backgroundPositionY: -toY * 2 + 'px' }) }
sweepBox.onmousemove = function (e) { if (!isMove(e)) { return } // 鼠標移動的距離 var moveX = e.offsetX - stepW var moveY = e.offsetY - stepH // 掃描框的偏移量 var offsetL = this.offsetLeft var offsetT = this.offsetTop // 計算出移動的最終座標 var toX, toY // 沿x軸往右移動,而且掃描框右邊界尚未碰到圖片右邊緣,那麼能夠移動,而且移動的距離最遠到圖片右邊緣 if (moveX > 0 && offsetL < sweepW) { toX = Math.min(offsetL + moveX, sweepW) } // 與之相反,沿x軸往左移動,那麼判斷左邊界未碰到圖片左邊緣,移動而且移動最左只能到0 if (moveX < 0 && offsetL > 0) { toX = Math.max(offsetL + moveX , 0) } // y軸雷同 if(moveY > 0 && offsetT < sweepH) { toY = Math.min(offsetT + moveY, sweepH) } if (moveY < 0 && offsetT > 0) { toY = Math.max(offsetT + moveY, 0) } // 每次移動,分別設置掃描框和展現臺的相應數據 setStyle(this, { left: toX + 'px', top: toY + 'px' }) setStyle(showBox, { backgroundPositionX: -toX * 2 + 'px', backgroundPositionY: -toY * 2 + 'px' }) }
咱們用了isMove函數來判斷掃描框是否有權移動,函數以下:
function isMove (e) { var offsetX = e.offsetX, offsetY = e.offsetY, offsetLeft = sweepBox.offsetLeft, offsetTop = sweepBox.offsetTop //左上角時,而且鼠標移動的位置小於度量值時,不能移動 if (!offsetLeft && !offsetTop) { // 左上 if (offsetX < stepW && offsetY < stepH ) { return false } } // 右上角時,鼠標移動位置x軸方向大於度量值,y軸方向小於度量值,也就是偏右上角,不能移動 if (offsetLeft === sweepW && !offsetTop) { // 右上 if (offsetY < stepH && offsetX > stepW) { return false } } // 雷同,鼠標移動偏右下角,不能移動 if (offsetLeft === sweepW && offsetTop === sweepH) { // 右下 if (offsetX > stepW && offsetY > stepH) { return false } } // 雷同,鼠標移動偏左下角,不能移動 if(!offsetLeft && offsetTop === sweepH) { // 左下 if (offsetX < stepW && offsetY > stepH) { return false } } // 以上條件都不符合,能夠移動 return true; }
鼠標移出時,咱們須要註銷掉這兩個事件監聽
elem.onmouseleave = function () { sweepBox.onmousemove = null elem.onmouseleave = null unload() // 隱藏展現臺和掃描框 }
unload很簡單,以下
function unload () { showBox.style.display = sweepBox.style.display = 'none' }
到此整個代碼完成,實現了2倍關係的圖像方大查看函數,並無提供多的自定義設置,你能夠本身修改一下,提供更多的自定義數據來提供更強大的功能。
本菜只能寫到這樣了,語言組織能力差,因此你可能沒看懂,不過不要緊,靜下心來默默的想一下,你可能就會寫這個功能了,並且必定比博主寫的好~~~。
本章示例在github上,https://git.oschina.net/xuazheng/zoomjs.git