当前位置: 华文世界 > 手机

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

2024-01-17手机

大家好,我是大澈!

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

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

1. 需求分析

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

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

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

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

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

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

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

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

    2. 功能实现

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

    2.1 方式一 动态设置偏移量

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

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

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

    <!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.getElementsBy className('smallBox')[0];var bigBox = document.getElementsBy className('bigBox')[0];var mask = document.getElementsBy className('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动态截取

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

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

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

    <!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.getElementsBy className('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个专栏内容。
  • 感谢关注微信公众号:「程序员大澈」,免费领取" 面试礼包 "一份,然后免费加入 问答群 ,从此让解决问题的你不再孤单!