在京东和淘宝等购买东西的时候,咱们会常常预览左侧商品展现图片,把鼠标放到原图,右侧就会有个大图显示出细节。本文将带领你们写一个这样简单的功能!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