最近接到一个需求,需要用户可以放大缩小图片,滚轮滚动还要控制放大的倍数。
在电商网站上,经常看到有商品图片被放大查看的功能,包括另外一些图片展示站点,也有类似的功能。
如果我们想让图片能展示更多细节清晰的内容,实现这样一个放大镜功能,是非常划算的,既能使用小图显示满足大多数用户的查看需求,又能通过放大图片的方式显示更清晰内容。这样,一方面可以节省不必要的大图流量,又能满足对图片清晰度有要求的用户需求。
所以,本文将分享前端技术实现一个简单的图片放大镜功能,希望能给大家带来一定的帮助。
首先,我先大致介绍一下我的实现思路
- 通过 vueuse 的 useMouseInElement 去判断鼠标是否进入到需要放大的图片中,并记录鼠标的坐标位置。
- 根据坐标去计算生成蒙层和放大预览的图片
- 通过监听 wheel 事件去控制图片放大的倍数
大部分都是计算
先安装vueuse
npm i @vueuse/core
<script setup>
import { useMouseInElement } from '@vueuse/core'
import { computed, ref } from 'vue'
const target = ref(null) // 操作的图片
const height = ref(0) // 操作盒子的高
const width = ref(0) // 操作盒子的宽
const layerWidth = ref(100) // 遮罩层宽and高 正方形
const scale = ref(5) // 放大的倍数
const imgUrl = ref('https://okdemo.okaygis.com:18062/cpmini-pc/io/3525?resVer=2')
const { x, y, isOutside } = useMouseInElement(target) // 鼠标是否在图片内
// 放大的位置
const position = computed(() => {
layerWidth.value = (width.value / scale.value).toFixed(0)
let elementX = x.value - (layerWidth.value / 2) - 5 // 让光标处再中间
let elementY = y.value - (layerWidth.value / 2) - 5
if (target.value) {
width.value = target.value?.offsetWidth // 记录操作的div高度
height.value = target.value?.offsetHeight // 记录操作的div宽度
}
elementX = elementX < 0 ? 0 : elementX // 不能超过最左侧
elementY = elementY < 0 ? 0 : elementY // 不能超过最上侧
elementX = elementX > width.value - layerWidth.value ? width.value - layerWidth.value : elementX // 不能超过最右侧
elementY = elementY > height.value - layerWidth.value ? height.value - layerWidth.value : elementY// 不能超过最下侧
return {
x: elementX.toFixed(0) || 0,
y: elementY.toFixed(0) || 0
}
})
// 鼠标滚轮放大缩小
const handleWheel = (e) => {
if (scale.value <= 2 && e.deltaY < 0) return // 最小放大倍数
if (scale.value >= 20 && e.deltaY > 0) return // 最大放大倍数
if (e.deltaY < 0) scale.value-- // 缩小
else scale.value++ // 放大
}
</script>
<template>
<div class="left">
<img :src="imgUrl" alt="" ref="target">
<!-- 遮罩层 -->
<div @wheel="handleWheel" class="layer" v-show="!isOutside"
:style="{
width: layerWidth + 'px',
height: layerWidth + 'px',
left: position.x + 'px',
top: position.y + 'px' }
">
</div>
</div>
<!-- 放大图片 -->
<div class="right" v-show="!isOutside" :style="{
width: layerWidth * scale + 'px',
height: layerWidth * scale + 'px',
left: `${+(position.x) + +(layerWidth) + 8}px`,
top: `${+(position.y) + +(layerWidth) + 8}px`,
backgroundImage: `url(${imgUrl})`,
backgroundPosition: `-${position.x * scale}px -${position.y * scale}px`,
backgroundSize: `${width * scale}px ${height * scale}px`
}">
</div>
放大{{ scale }}倍
</template>
<style scoped lang="less">
.left {
padding: 0;
margin: 0;
box-sizing: border-box;
position: relative;
display: flex;
img {
max-width: 500px;
}
.layer {
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
}
}
.right {
position: absolute; //定位到蒙层旁边
/* margin-top: 10px; */ //固定位置
background-repeat: no-repeat;
}
</style>