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

发布时间: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.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动态截取

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

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

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

<!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个专栏内容。

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

文章来源:https://blog.csdn.net/m0_74802419/article/details/135650169
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。