功能问题:如何实现图片放大镜功能?

大家好,我是大澈!

本文约2900+字,整篇阅读大约需要4分钟。

感谢关注微信公众号:"程序员大澈",免费领取"面试礼包"一份,然后免费加入问答群,从此让解决问题的你不再孤单!

1. 需求分析

平常我们一般都是在手机淘宝上购物,不知道大家有没有体验过PC端淘宝,里面有很多好玩的设计。

比如我们今天要实现的功能:图片放大镜,这是一个在电商网站应用非常广的设计。

鼠标移入左侧缩略图时,小盒子右边大图显示,并且小盒子跟随鼠标移动,右侧大图出现放大效果。

再梳理一下,图片放大镜功能设计有如下几个优点

  • 提供更好的用户体验:通过放大镜功能,用户可以在不离开当前页面或进一步导航的情况下,更详细地查看图像的细节。
  • 提供互动性:放大镜功能增加了用户与图像的互动性。通过鼠标悬停或触摸屏幕,用户可以控制放大镜的位置,并实时查看所选区域的放大效果。
  • 增强响应式设计:无论用户使用大屏幕设备还是小屏幕设备,他们都能够在需要时放大图像,轻松查看细节。

图片放大镜功能的实际应用场景有很多,如电子商务、艺术设计展示、图片库等。

要实现图片放大镜功能,一般有两种方式:一种是 利用原生JS动态设置偏移量,一种是 利用原生JS的Canvas动态截取。

当然,实现方式一定不仅仅只有这几种,还有很多可以尝试的其它办法,这里期待朋友们补充指教吧!

下面我们一起来看看,上述两种原生JS的具体实现。

2. 功能实现

先简单聊聊这两种原生JS方式的实现原理,再附上可直接使用的源码,最后置身于真实项目中,对图片放大镜功能的实现做一下小结。

2.1 方式一 动态设置偏移量

此方式中,左侧缩略图和右侧大图插入的是同一个图片,不过原图窗口的图片要适当缩小,放大窗口图片保持原大小,超出部分设置隐藏。

先实现小盒子跟着鼠标移动的功能,且小盒子不能移出边界。

再实现右侧大图随着小盒子的移动实现自身移动,且两者移动方向总是相反的。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>放大镜</title>
 <style>
  *{
   margin: 0;
   padding: 0;
  }
  .container{
   width: 400px;
   height: 250px;
   position: relative;
   left: 250px;
   top: 150px;
  }
  .bigBox{
   width: 500px;
   height: 500px;
   border-radius: 50%;
   position: absolute;
   left: 430px;
   border: 1px solid #ccc;
   overflow: hidden;
   display: none;
  }
  .smallBox{
   position: absolute;
   border: 1px solid #ccc;
   font-size: 0;
  }
  .mask{
   width: 100px;
   height: 100px;
   cursor: move;
   position: absolute;
   top: 0;
   left: 0;
   background-color: rgba(255,255,0,0.4);
   display: none;
  }
  #bigPic{
   position: absolute;
  }
 </style>
</head>

<body>
 <div class="container" id="box">
  <!-- 左侧缩略图 -->
  <div class="smallBox">
   <img src="./01.png" alt="" width="400px">
   <!-- 小盒子 -->
   <div class="mask"></div>
  </div>
  <!-- 右侧大图 -->
  <div class="bigBox">
   <img src="./01.png" alt="" id="bigPic">
  </div>
 </div>
</body>

<script>
window.onload = function(){
    var smallBox = document.getElementsByClassName('smallBox')[0];
    var bigBox = document.getElementsByClassName('bigBox')[0];
    var mask = document.getElementsByClassName('mask')[0];
    var box = document.getElementById('box');
    var bigPic = document.getElementById('bigPic');
    smallBox.onmouseover = function(){
        bigBox.style.display = "block";
        mask.style.display = "block";
    }
    smallBox.onmouseout = function(){
        bigBox.style.display = "none";
        mask.style.display = "none";
    }
    smallBox.onmousemove = function(event){
        //pageX,pageY
        //处理兼容性和滚动条
        var event = event || window.event;
        var scrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;
          var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
        var pageX = event.pageX || event.clientX + scollLeft;
        var pageY = event.pageY || event.clientY + scollTop;
        var targetX = pageX - box.offsetLeft - mask.offsetWidth/2;
        var targetY = pageY - box.offsetTop - mask.offsetHeight/2;
        //左边界
        if(targetX<0){
            targetX = 0;
        }
        //上边界
        if(targetY<0){
            targetY = 0;
        }
        //右边界
        if(targetX>smallBox.offsetWidth - mask.offsetWidth){
            targetX = smallBox.offsetWidth - mask.offsetWidth
        }
        //下边界
        if(targetY>smallBox.offsetHeight - mask.offsetHeight){
            targetY = smallBox.offsetHeight - mask.offsetHeight
        }
        //设置小盒子的左偏移和上偏移
        mask.style.left = targetX + 'px';
        mask.style.top = targetY + 'px';
        var smallMoveX = smallBox.offsetWidth - mask.offsetWidth;
        var bigMoveX = bigPic.offsetWidth - bigBox.offsetWidth;
        var smallMoveY = smallBox.offsetHeight - mask.offsetHeight;
        var bigMoveY = bigPic.offsetHeight - bigBox.offsetHeight;
        //设置右边放大部分跟随鼠标移动
        var rateX = bigMoveX/smallMoveX; 
        var rateY = bigMoveY/smallMoveY;
        bigPic.style.left = -rateX*targetX+'px';
        bigPic.style.top = -rateY*targetY+'px'; 
    }
}
</script>
</html>

2.2 方式二 Canvas动态截取

此方式中,页面上总共放三块画布。一块放左侧缩略图,一块放小盒子,一块放右侧大图。

先实现小盒子跟随鼠标在左侧缩略图上移动。

再把小盒子内的图片截取出来,按比例放到右侧大图的画布上,从而实现放大效果。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>放大镜</title>
 <style>
  *{
   margin: 0;
   padding: 0;
  }
  .container{
   width: 300px;
   height: 300px;
   border: 1px solid #ccc;
   position: relative;
  }
  canvas{
   border: 1px solid #ccc;
  }
  #big-canvas{
   position: absolute;
   left: 320px;
   top: 100px;
   display: none;
  }
  #small-canvas{
   position: absolute;
   opacity: 0.5;
   left: 0;
   top: 0;
   display: none;
  }
 </style>
</head>

<body>
 <div class="container">
  <!-- 左侧缩略图 -->
  <canvas id="canvas" width="300px" height="300px"></canvas>
  <!-- 右侧大图 -->
  <canvas id="big-canvas" width="500px" height="500px"></canvas>
  <!-- 小盒子 -->
  <canvas id="small-canvas" width="80px" height="80px"></canvas>
 </div>
</body>

<script>
window.onload = function(){
    var container = document.getElementsByClassName('container')[0];
    var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');
    var bigCanvas = document.getElementById('big-canvas');
    var bigContext = bigCanvas.getContext('2d');
    var smallCanvas = document.getElementById('small-canvas');
    var smallContext = smallCanvas.getContext('2d');

    //左侧缩略图
    var img = new Image();
    img.src = './01.png';
    img.onload = function(){
        context.drawImage(img,0,0,300,300);
    }

    //小盒子
    var imgbd = new Image();
    imgbd.src = '';
    imgbd.onload = function(){
        smallContext.drawImage(imgbd,0,0,80,80);
    }

    container.onmousemove = function(event){
        //鼠标移进小盒子和右侧大图都显示
        bigCanvas.style.display = 'block';
        smallCanvas.style.display = 'block';
        // x是开始裁剪图片的位置的横坐标  y是开始裁剪图片的位置的纵坐标
        var x = event.pageX - container.offsetLeft - smallCanvas.offsetWidth/2;
        var y = event.pageY - container.offsetTop - smallCanvas.offsetHeight/2;
        //判断边界
        if (x<0) {
            x = 0;
        }
        if (x> canvas.offsetWidth - smallCanvas.offsetWidth) {
            x = canvas.offsetWidth - smallCanvas.offsetWidth;
        }
        if (y<0) {
            y = 0;
        }
        if (y> canvas.offsetHeight - smallCanvas.offsetHeight) {
            y = canvas.offsetHeight - smallCanvas.offsetHeight;
        }
        // 设置小盒子的位置
        smallCanvas.style.left = x + 'px';
        smallCanvas.style.top = y + 'px';

        // 参数:图像对象,开始剪切的 x 坐标位置,开始剪切的 y坐标位置,被剪切图像的宽度,被剪切图像的高度,绘制位置的起始x坐标,起始y坐标,绘制图像的宽,高
        bigContext.drawImage(canvas,x,y,80,80,0,0,500,500);

    }
    //鼠标移出小盒子和右侧大图都隐藏
    container.onmouseleave = function(){
        bigCanvas.style.display = 'none';
        smallCanvas.style.display = 'none';
    }
}
</script>
</html>

2.3 小结

对于图片放大镜功能的实现,上述两种原生JS方式其实都不算复杂。

我们只需要在使用时,细细梳理一遍代码,再修改一些必要参数即可,不需要钻牛角尖过分纠结。

在真实项目中,我们更应该有如下考虑:

对于Vue2项目,可直接使用vue2.0-zoom库。

对于Vue3项目,可直接使用zoomer-vue3库。

如需高度自定义时,可使用上述两种原生JS方式择其一来手写。

结语

建立这个平台的初衷:

  • 打造一个专注于前端功能问题的问答平台,让大家高效搜索处理同样问题。
  • 遇到有共鸣的问题,与众多同行朋友们一起讨论,一起沉淀成长。
  • 平台现拥有功能问题、技术资讯、实用干货3个专栏内容。

感谢关注微信公众号:"程序员大澈",免费领取"面试礼包"一份,然后免费加入问答群,从此让解决问题的你不再孤单!

相关推荐
吕永强14 分钟前
CSS相关属性和显示模式
前端·css·css3
赵锦川20 分钟前
css三角形:css画箭头向下的三角形
前端·css
qbbmnnnnnn25 分钟前
【WebGis开发 - Cesium】如何确保Cesium场景加载完毕
前端·javascript·vue.js·gis·cesium·webgis·三维可视化开发
f8979070701 小时前
layui动态表格出现 横竖间隔线
前端·javascript·layui
杨荧2 小时前
【JAVA开源】基于Vue和SpringBoot的水果购物网站
java·开发语言·vue.js·spring boot·spring cloud·开源
二十雨辰2 小时前
[uni-app]小兔鲜-04推荐+分类+详情
前端·javascript·uni-app
霸王蟹3 小时前
Vue3 项目中为啥不需要根标签了?
前端·javascript·vue.js·笔记·学习
小白求学13 小时前
CSS计数器
前端·css
儒雅的烤地瓜3 小时前
JS | 如何解决ajax无法后退的问题?
前端·javascript·ajax·pushstate·popstate事件·replacestate
觉醒法师3 小时前
Vue3+TS项目 - ref和useTemplateRef获取组件实例
开发语言·前端·javascript