zoukankan      html  css  js  c++  java
  • javascript实现 京东淘宝等商城的商品图片大图预览功能(图片放大器)

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

    一、实现原理

    当鼠标移入某一图片内部时,图片上部会出现一个类似于扫描的框,这个框内的图片部分,会以方大形式展示在右边,如下图:

    从图中可以推测出一下几点:

    图片img上层会有一个父元素(如‘div’),在鼠标移入时,父元素内部添加一个子元素代表扫描框,并且整个body会出现一个固定定位的图片预览盒子定位在右侧(这个图片是另外一张准备好的大图),展示着扫描框中扫描到的图片位置,这个扫描框不能在div内部移动,当鼠标移出图片,扫描框和展示台都消失。

    因此我们得到以下布局

    复制代码
    <!--整个盒子-->
    <div>
      <!--图片-->
      <img src="...">
        <!--扫描框-->
      <div class="sweep"></div>        
    </div>
    
    <!--扫描展示区域-->
    <div class="show">
    </div>    
    复制代码

    实际情况下,我们不会手动写上 .sweep 和 .show这两个空div,他是由js来实现的。因此,今天我们练习的布局代码如下

    <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;
       200px;
      height: 200px;
      margin: 0 auto;
    }

    我们紧紧需要设置图片盒子的样式就可以了,注意: 图片盒子需要把宽高设定为图片大小200*200。

    js函数参数选定:

    复制代码
    // 将函数命名为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, {
           (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 / 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倍关系的图像方大查看函数,并没有提供多的自定义设置,你可以自己修改一下,提供更多的自定义数据来提供更强大的功能。

    结尾

    本菜只能写到这样了,语言组织能力差,所以你可能没看懂,不过没关系,静下心来默默的想一下,你可能就会写这个功能了,而且一定比博主写的好~~~。

  • 相关阅读:
    一步一步写平衡二叉树(AVL树)
    sql关键字
    Remoting技术的应用
    算法:最大公约数
    算法冒泡排序
    C#编码好习惯
    利用VB.Net编程实现PC与掌上电脑PPC间的双向通信
    .Net Remoting与Server 对象详解
    算法迭代和递归
    SQL关键字系列之:minus与intersect
  • 原文地址:https://www.cnblogs.com/libin-1/p/6287237.html
Copyright © 2011-2022 走看看