如何简单实现一个图片放大镜功能

在一些电商网站上,经常看到有商品图片被放大查看的功能,包括另外一些图片展示站点,也有类似的功能。
如果我们想让图片能展示更多细节清晰的内容,实现这样一个放大镜功能,是非常划算的,既能使用小图显示满足大多数用户的查看需求,又能通过放大图片的方式显示更清晰内容。这样,一方面可以节省不必要的大图流量,又能满足对图片清晰度有要求的用户需求。
所以,本文将使用前端技术实现一个简单的图片放大镜功能,希望能给大家带来一定的帮助。

图片放大镜效果

我们可以先来看下图片放大镜最后的效果图:

如何简单实现一个图片放大镜功能

如上图所示,
左边的小图方块内,就是需要放大的图片,上面有一个蒙层区域(可视作放大镜),只可以在小图范围内移动,该蒙层遮住的区域就代表图片需要放大的部分;
右边的大图方块内,展示的是蒙层部分放大后的清晰图像,这里本质上是显示的完整像素的图片、或者是比左边更大像素的高清图片。
左边蒙层的实时移动,在右边显示清晰图像,这就形成了一个图片放大镜的功能效果。

接下来,我们具体看看它的实现过程。

实现过程

一、设计UI界面

要实现放大镜功能,就要先设计好界面布局,从上面的效果图里看到,界面上分了两块:一个是原始的缩略小图区域;一个是放大后的预览大图区域。
在html中使用div来布局的话,如下所示:

<div id="el_EnlargeContainer" class="enlarge">
  <!--左边原始小图区域-->
  <div id="el_SmallContainer" class="small">
    <img id="el_SmallImage"/>
    <div id="el_maskEnarge" class="mask"></div>
  </div>
  <!--右边预览大图区域-->
  <div id="el_PreviewContainer" class="preview">
    <img id="el_PreviewImage"/>
  </div>
</div>

从以上html代码可知,
外层定义了一个 el_EnlargeContainer 的整个放大功能整体内容区域,里面有两个子元素:一个是缩略小图的div,包含一个img图片元素用于展示缩略图,还有一个蒙层的div元素;另外一个是预览大图区域,包含有放大后的大图img元素,这里加载的是原始大尺寸图。

主要样式设置

蒙层需要覆盖在缩略小图的上面,所以需要使用绝对定位方式,并且设置带透明度的颜色,鼠标样式也设置为 move,默认隐藏,具体的CSS样式如下所示:

.mask {
  position: absolute;
  top: 0;
  left: 0;
  background: rgba(255, 255, 0, 0.6);
  cursor: move;
  display: none;
}

预览大图区域样式类 preview,由于要显示在右侧区域,也可以使用绝对定位的方式,超出显示在小图区域的右边即可:

.preview {
  position: absolute;
  top: 0;
  left: 150px;
  /* ... */
}

其他的样式设置都较简单,根据布局方式一一展示即可,这里就先略过。
下面,我们需要做的是加载图片。

二、加载缩略小图

上面的代码中,我们只定义了img标签元素,并没有直接加载图片资源,这是由于我们做的是以动态加载图片的方式来实现这个放大镜功能。
当动态加载图片资源后,需要根据图片像素宽高,设置对应界面元素的大小和位置信息。

缩略小图作为首先需要主体展示的资源,我们需要先加载,并设置小图区域容器和图片的大小:

el_SmallImage.onload = () => {
  let { width, height } = el_SmallImage
  const smallScale = Math.min(SMALLSIZE / width, SMALLSIZE / height, 1)
  smallImgWidth = width * smallScale
  smallImgHeight = height * smallScale

  el_EnlargeContainer.style.width = el_SmallImage.style.width = smallImgWidth + 'px'
  el_EnlargeContainer.style.height = el_SmallImage.style.height = smallImgHeight + 'px'

  resolve(el_SmallImage)
}
el_SmallImage.src = imgSrc

以上代码,加载缩略小图后,获取到图片的宽高数据,然后相应的计算。其中 SMALLSIZE 是预先定义的一个常量,表示小图区域的一个固定尺寸大小,可根据需要自定义取值,通过该值计算缩略图的缩放比例——缩略图依据该固定尺寸,计算合适的宽高比例进行展示。
注意,这里设置的是整个放大镜的整体内容区域 el_EnlargeContainer 的大小,就是小图展示的大小,而预览放大的图片是浮在右侧的,并不被包含在这块区域内。

这时候的缩略图,就是我们网站要稳定展示的图片信息,作为用户初始浏览的内容,如下图:

如何简单实现一个图片放大镜功能

一般电商网站上,使用轮播图切换的方式,展示缩略图片信息。

从上图可见,只展示了缩略图片,放大后的预览大图还没涉及到,是因为我们可以在监听事件里进行加载,有利于节省图片加载流量。

三、缩略图事件监听

接下来,我们需要处理缩略小图的事件监听,实现加载预览大图、蒙层移动的效果。
根据上面的html布局,我们在缩略小图的外部容器 el_SmallContainer 上添加鼠标事件,主要是 onmouseenteronmouseleaveonmousemove 三个事件。

onmouseenter 事件,当鼠标进入到页面展示的缩略图上时进行处理,这时需要做两件事:一是加载预览大图的原始图像,二是显示蒙层和预览大图的完整内容区域。

el_SmallContainer.onmouseenter = async () => {
  // 加载预览大图原始图
  if (loadPreviewImage === null) {
    loadPreviewImage = await setPreviewImage(previewImgUrl)
  }

  // 显示蒙层和预览大图区域
  el_maskEnarge.style.display = 'block'
  el_PreviewContainer.style.display = 'block'
}

以上代码,就是鼠标进入事件的处理,其中有加载并设置预览大图的原始数据信息,具体代码如下所示:

let { width, height } = el_PreviewImage
let previewScale = Math.min(PREVIEWSIZE / width, PREVIEWSIZE / height, 1)

el_PreviewContainer.style.width = width * previewScale + 'px'
el_PreviewContainer.style.height = height * previewScale + 'px'
el_PreviewContainer.style.left = (smallImgWidth + 10) + 'px'

el_maskEnarge.style.width = smallImgWidth * previewScale + 'px'
el_maskEnarge.style.height = smallImgHeight * previewScale + 'px'

以上代码,用于加载预览大图,这时能获取到图片的原始宽高数据,其中 PREVIEWSIZE 也是预先定义的常量,表示预览大图区域的一个固定尺寸大小。大图宽高与PREVIEWSIZE尺寸计算出对应的缩放比,用于计算实际展示区域的尺寸。
el_PreviewContainer 预览大图容器内容需要定位在右侧区域,设置 left 样式属性。
另外,蒙层的大小也得设置,蒙层大小是在缩略小图区域展示的,但要使用预览大图的缩放比,这样才能得到准确的蒙层内容用于在右侧放大区域呈现放大效果。

onmouseleave 事件比较简单,当鼠标离开缩略图内容区域的时候,就隐藏蒙层和预览大图区域,放大镜效果隐藏:

el_SmallContainer.onmouseleave = () => {
  el_maskEnarge.style.display = 'none'
  el_PreviewContainer.style.display = 'none'
}

定义了放大镜的显示和隐藏事件后,就应该处理移动的效果了,移动实现实时放大,使用到 onmousemove 鼠标事件。

四、实现放大

鼠标移动也是在缩略小图内容区域,对 el_SmallContainer 元素进行事件监听:

el_SmallContainer.onmousemove = (event) => {
  // ...
}

在该事件中,我们需要处理以下几件事:

  • 获取鼠标移动的位置信息
let x = event.pageX - el_EnlargeContainer.offsetLeft - el_maskEnarge.offsetWidth / 2
let y = event.pageY - el_EnlargeContainer.offsetTop - el_maskEnarge.offsetHeight / 2

以上代码里,读取了蒙层的offset数据,是为了移动时,能够让鼠标显示在蒙层的中心位置。

  • 限制蒙层移动的范围,不能超出缩略小图内容区域
const xMax = el_SmallContainer.offsetWidth - el_maskEnarge.offsetWidth
x = x < 0 ? 0 : (x > xMax ? xMax : x)
const yMax = el_SmallContainer.offsetHeight - el_maskEnarge.offsetHeight
y = y < 0 ? 0 : (y > yMax ? yMax : y)
  • 更新蒙层的位置信息,实时移动蒙层元素
el_maskEnarge.style.left = x + 'px'
el_maskEnarge.style.top = y + 'px'
  • 设置预览大图显示的位置信息
const rate = el_PreviewImage.offsetWidth / el_SmallContainer.offsetWidth
el_PreviewImage.style.marginTop = -(rate * y) + 'px'
el_PreviewImage.style.marginLeft = -(rate * x) + 'px'

以上代码,是在一定的内容区域里,加载一张完整原始尺寸的大图,但多余的部分先隐藏 overflow: hidden;。然后计算缩略小图和预览大图的比值,右侧的预览大图等比例移动,通过使用 marginTopmarginLeft 属性设置定位的方式来移动。注意,这里处理的 el_PreviewImage 原始大图元素。
数据取负值,是相对于初始加载时候左上角的值。

总结

至此,一个可以非固定尺寸、动态加载图片的放大镜功能就基本完成了。
动态加载图片,可以不用考虑图片的像素宽高,任意尺寸的图片都可以完整放大,缩略小图和预览大图也可以是同一张图片。
另外,就是放大镜的内容显示区域,是根据不同图片的比例进行动态计算的,尺寸大小可变,但能够通过常量设定,控制在一定范围内。

图片放大镜功能的大部分代码都已在文章中列出来了,根据这些内容,还可以适当修改,更改成各种符合我们需求的插件。

固定尺寸

很多网站在处理放大镜功能上,使用的是固定尺寸缩略小图和预览大图的方式。
我们当然也可以处理固定尺寸,这个时候就需要特定尺寸的缩略小图和原始大图,直接设置元素的固定尺寸,缩略图也可以直接加载:

/* 放大镜区域的大小 */
.enlarge {
  /* ... */
  width: 150px;
  height: 150px;
}
/* 缩略小图的大小 */
.small img {
  width: 150px;
  height: 150px;
}
/* 蒙层大小 */
.mask {
  /* ... */
  width: 75px;
  height: 75px;
}
/* 预览大图区域的大小 */
.preview {
  /* ... */
  width: 300px;
  height: 300px;
}
<img id="el_SmallImage" src="chrome.png"/>

以上代码,通过css直接设置各区域的宽高大小,缩略图也是直接加载,如此处理,就不需要手动设定各元素的宽高。
同样可以在鼠标进入事件中加载原始大图,这个时候图片尺寸也是固定的。
其他事件处理,基本相同,这样就可以增加一个固定图像尺寸和固定界面尺寸的放大镜功能了。

其他图片相关知识

前端需要了解的图片基础知识
JavaScript获取图片真实格式
web文件读取上传的四种方式
如何在前端实现图片裁剪功能

原文链接:https://juejin.cn/post/7249380463080259641 作者:jimojianghu

(0)
上一篇 2023年6月28日 上午10:00
下一篇 2023年6月28日 上午10:10

相关推荐

发表回复

登录后才能评论