vue3实现淘宝商品详情页放大镜效果

效果展示:

先来一张效果图片,样式参考华为商城(遥遥领先)
在这里插入图片描述

使用html和scss构建基本结构:

html代码:

<div class="zoom-big-container">
	<!-- 图片展示区域 -->
	<div
		id="wapper"
		class="gallery-pic-wrap"
		@mouseover="handleZoomMouseover"
		@mouseout="handleZoomMouseout"
		@mousemove="handleZoomMousemove"
	>
		<img :src="activePicUrl" alt="错误" :style="activeStyle" />
		<!-- 放大镜 -->
		<div id="zoom" class="zoom-box"></div>
	</div>
	<!-- 放大后的图片展示区域 -->
	<div id="big" class="big-pic-box">
		<img
			id="big-pic"
			:src="activePicUrl"
			alt="错误"
			style="width: calc((450 / 160) * 450px); height: calc((450 / 160) * 450px)"
		/>
	</div>
	<!-- 缩略图 -->
	<div class="gallery-nav">
		<div class="prev btn" @click="handlePrev"></div>
		<div class="thumbs">
			<ul class="gallerys" :style="gallerysStyle">
				<li v-for="(pic, index) of picList" :key="index" @mouseover="handleGalleryMouseover(pic, index)">
					<img :src="pic.url" alt="错误" :class="getImgClass(index)" />
				</li>
			</ul>
		</div>
		<div class="next btn" @click="handleNext"></div>
	</div>
</div>

css代码:

.zoom-big-container {
	position: relative;
  // 图片展示区域
	.gallery-pic-wrap {
		position: relative;
		width: 450px;
		height: 450px;
		.zoom-box {
			width: 160px;
			height: 160px;
			background-color: #ffffff;
			border: 1px solid #cccccc;
			box-sizing: border-box;
			opacity: 0.5;
			position: absolute;
			left: 0;
			top: 0;
			display: none;
		}
	}
  // 缩略图区域
	.gallery-nav {
		position: relative;
		width: 450px;
		height: 68px;
		margin-top: 20px;
		.thumbs {
			margin-left: 55px;
			width: 340px;
			height: 68px;
			overflow: hidden;
			.gallerys {
				padding: 0px;
				list-style: none;
				display: flex;
				justify-content: flex-start;
				align-items: center;
				li {
					display: flex;
					justify-content: center;
					align-items: center;
					width: 62px;
					height: 62px;
					margin: 3px;
					cursor: pointer;
					user-select: none;
					.thumb-img {
						width: 62px;
						height: 62px;
						margin: 0px;
						box-sizing: border-box;
					}
					.is-active {
						border: 1px solid #ca141d;
					}
				}
			}
		}
		.btn {
			position: absolute;
			top: 50%;
			transform: translateY(-50%);
			width: 30px;
			height: 30px;
			background-repeat: no-repeat;
			background-size: cover;
			cursor: pointer;
			transition: all 0.3s;
			&:hover {
				opacity: 0.6;
			}
		}
		.prev {
			left: 0;
			background: url('./prev.png');
		}
		.next {
			right: 0;
			background: url('./next.png');
		}
	}
  // 放大的图片展示区域
	.big-pic-box {
		width: 450px;
		height: 450px;
		position: absolute;
		box-sizing: border-box;
		border: 2px solid #cccccc;
		top: 0px;
		left: 460px;
		z-index: 999;
		display: none;
		overflow: hidden;
		.big-pic {
			width: 900px;
			height: 900px;
		}
	}
}

图片切换功能实现:

需求:

  1. 当鼠标悬浮在缩略图上时切换图片
  2. 缩略图超过展示区域可滚动切换

1、监听缩略图的鼠标悬浮事件:@mouseover="handleGalleryMouseover(pic, index)"
2、向左、向右滚动:@click="handlePrev" @click="handleNext"
3、通过marginLeft来控制位移的方向和距离:由下图可以得出:marginLeft的最大值为0,向左/右移动的最大距离为 (缩略图栏宽度 - 展示区域宽度)
在这里插入图片描述

在这里插入图片描述

<div class="gallery-nav">
	<div class="prev btn" @click="handlePrev"></div>
		<div class="thumbs">
			<ul class="gallerys" :style="gallerysStyle">
				<li v-for="(pic, index) of picList" :key="index" @mouseover="handleGalleryMouseover(pic, index)">
					<img :src="pic.url" alt="错误" :class="getImgClass(index)" />
				</li>
			</ul>
		</div>
	<div class="next btn" @click="handleNext"></div>
</div>
// 当前激活的图片索引
const activePicIndex = ref(0)
// 当前激活的图片url
const activePicUrl = ref('')
// 激活图片的样式
const activeStyle = reactive({
	width: '450px',
	height: '450px',
})

// 缩略图的宽度:即每次移动的left距离
const picWidth = 68
// 最大可移动的距离:绝对值
const maxMarginLeft = ref(0)
// 最大可移动的距离:实际marginLeft可达到的值
const peakMarginLeft = ref(0)

/** 鼠标悬浮 */
const handleGalleryMouseover = (pic, index) => {
	activePicIndex.value = index // 当前索引
	activePicUrl.value = pic.url // 图片url
}
let timer = null
/** 缩略图栏:向右移动 */
const handleNext = () => {
	// 当前移动的距离
	const currentMarginLeft = parseInt(gallerysStyle.marginLeft.replace('px'))
	// 移动次数
	const limit = 10
	// 每次移动多少距离
	const moveDistance = division(picWidth, limit)
	// 如果最大可位移量大于0,并且当前移动的距离大于最大可移动的距离(真实值:负数)
	if (maxMarginLeft.value > 0 && currentMarginLeft > peakMarginLeft.value) {
		// 当前移动了几次:计数
		let count = 0
		// 位移动画
		timer = setInterval(() => {
			count++
			if (count > limit) {
				return clearInterval(timer)
			}
			const moveLeft = currentMarginLeft - moveDistance * count
			gallerysStyle.marginLeft = moveLeft + 'px'
		}, 30)
	}
}
/** 缩略图栏:向左移动 */
const handlePrev = () => {
	// 当前移动的距离
	const currentMarginLeft = parseInt(gallerysStyle.marginLeft.replace('px'))
	// 移动次数
	const limit = 10
	// 每次移动多少距离
	const moveDistance = division(picWidth, limit)
	// 当前移动的距离小于0时,即向右移动过
	if (currentMarginLeft < 0) {
		// 当前移动了几次:计数
		let count = 0
		// 位移动画
		timer = setInterval(() => {
			count++
			if (count > limit) {
				return clearInterval(timer)
			}
			const moveLeft = currentMarginLeft + moveDistance * count
			gallerysStyle.marginLeft = moveLeft + 'px'
		}, 30)
	}
}

/** 获取图片样式:激活或未激活 */
const getImgClass = index => {
	return activePicIndex.value === index ? 'thumb-img is-active' : 'thumb-img'
}

/** 初始化 */
onMounted(() => {
	// 初始化时,默认激活一张图片,索引默认为 0
	activePicUrl.value = picList.value[activePicIndex.value].url
	
	// 图片数量
	const len = picList.value.length
	// 可展示5张图,只有大于5张图才需要切换滚动
	// marginLeft的最大值为0,向左/右移动的最大距离为 (缩略图栏宽度 - 展示区域宽度)
	// 最大可位移的距离:绝对值
	maxMarginLeft.value = len > 5 ? (len - 5) * picWidth : 0
	// 最大可位移的距离:真实值
	peakMarginLeft.value = maxMarginLeft.value * -1
})

放大镜在图片上移动的功能:

通过绝对定位来实现位移:

<div
	id="wapper"
	class="gallery-pic-wrap"
	@mouseover="handleZoomMouseover"
	@mouseout="handleZoomMouseout"
	@mousemove="handleZoomMousemove"
>
	<img :src="activePicUrl" alt="错误" :style="activeStyle" />
	<!-- 放大镜 -->
	<div id="zoom" class="zoom-box"></div>
</div>
.gallery-pic-wrap {
	position: relative;
	width: 450px;
	height: 450px;
	.zoom-box {
		width: 160px;
		height: 160px;
		background-color: #ffffff;
		border: 1px solid #cccccc;
		box-sizing: border-box;
		opacity: 0.5;
		position: absolute;
		left: 0;
		top: 0;
		display: none;
	}
}
// 放大的图片展示区域
.big-pic-box {
	width: 450px;
	height: 450px;
	position: absolute;
	box-sizing: border-box;
	border: 2px solid #cccccc;
	top: 0px;
	left: 460px;
	z-index: 999;
	display: none;
	overflow: hidden;
	.big-pic {
		width: 900px;
		height: 900px;
	}
}

放大镜的位置解析:
在这里插入图片描述
放大镜与放大后的图片解析:
在这里插入图片描述

/** 放大镜:移入商品图片区域 */
const handleZoomMouseover = () => {
	const zoom = document.getElementById('zoom')
	const big = document.getElementById('big')
	if (zoom) {
		zoom.style.display = 'block'
	}
	if (big) {
		big.style.display = 'block'
	}
}
/** 放大镜:移出商品图片区域 */
const handleZoomMouseout = () => {
	const zoom = document.getElementById('zoom')
	const big = document.getElementById('big')
	if (zoom) {
		zoom.style.display = 'none'
	}
	if (big) {
		big.style.display = 'none'
	}
}

// 放大比例:
const rate = 450 / 160
/** 在商品图片区域上移动 */
const handleZoomMousemove = e => {
	const zoom = document.getElementById('zoom')
	const bigPic = document.getElementById('big-pic')
	const wapper = document.getElementById('wapper')
	const wapperOffset = getViewportOffset(wapper)
	// 获得鼠标如何移动的方式:根据鼠标方向移动:移动的像素是多少
	// e.clientX  e.clientY : 鼠标相对于页面左上角的位置
	// wapperOffset.left  wapperOffset.top :图片展示框距离页面左上角的位置
	// e.clientX - wapperOffset.left:鼠标相对于图片展示框的位置
	// 80 = 放大镜框的宽度 / 2; 宽高一致
	// zoomX、zoomY:放大镜框相对于图片展示框的位置
	let zoomX = e.clientX - wapperOffset.left - 80
	let zoomY = e.clientY - wapperOffset.top - 80

	// zoom边界的处理:即放大镜框最大可移动区域
	//左边界
	if (zoomX < 0) {
		zoomX = 0
	}
	// 右边界:290 = 450 - 160,即图片展示框宽度 - 放大镜宽度
	if (zoomX >= 290) {
		zoomX = 290
	}
	// 上边界
	if (zoomY < 0) {
		zoomY = 0
	}
	// 下边界
	if (zoomY >= 290) {
		zoomY = 290
	}
	// 放大镜移动
	if (zoom) {
		zoom.style.left = zoomX + 'px'
		zoom.style.top = zoomY + 'px'
	}
	// 放大后的图片跟随移动
	if (bigPic) {
		bigPic.style.marginLeft = -zoomX * rate + 'px'
		bigPic.style.marginTop = -zoomY * rate + 'px'
	}
}

至此放大镜的效果就实现了

源代码:

vue3实现淘宝商品详情页放大镜效果组件源码

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现淘宝图片放大镜功能,可以通过以下步骤实现: 1. 在 Vue 件中引入所需的依赖库,例如 `element-ui` 等。 2. 在模板中,将需要实现放大镜效果的图片绑定到一个 `<div>` 元素上,并设置其样式。 3. 在该 `<div>` 元素上绑定 `@mousemove` 事件,监听鼠标在该元素上的移动。 4. 在事件处理函数中,获取鼠标在元素上的坐标,并根据元素的大小和图片的大小计算出放大镜的位置。 5. 使用 `element-ui` 中的 `Dialog` 件,将放大的图片作为弹出框的内容,并设置其样式,并在需要放大的图片上绑定 `@click` 事件,当点击该图片时,弹出放大镜。 下面是一个简单的 Vue 件示例代码: ```html <template> <div class="image-container" @mousemove="handleMouseMove"> <img src="./image.jpg" alt="image" class="image" @click="showDialog"> <el-dialog :visible.sync="dialogVisible" width="60%"> <img src="./image.jpg" alt="image" class="dialog-image"> </el-dialog> </div> </template> <script> export default { data() { return { dialogVisible: false, zoomedImageStyle: { position: 'absolute', border: '1px solid #ccc', width: '200px', height: '200px', display: 'none', zIndex: 9999 } } }, methods: { handleMouseMove(e) { const { clientX, clientY } = e const { left, top, width, height } = e.target.getBoundingClientRect() const x = clientX - left const y = clientY - top const zoomedImage = document.querySelector('.zoomed-image') const percentageX = x / width const percentageY = y / height const imageWidth = zoomedImage.width const imageHeight = zoomedImage.height const leftOffset = -percentageX * (imageWidth - width) const topOffset = -percentageY * (imageHeight - height) this.zoomedImageStyle.display = 'block' this.zoomedImageStyle.left = `${x}px` this.zoomedImageStyle.top = `${y}px` this.zoomedImageStyle.backgroundImage = `url(${zoomedImage.src})` this.zoomedImageStyle.backgroundSize = `${imageWidth}px ${imageHeight}px` this.zoomedImageStyle.backgroundPositionX = `${leftOffset}px` this.zoomedImageStyle.backgroundPositionY = `${topOffset}px` }, showDialog() { this.dialogVisible = true } } } </script> <style> .image-container { position: relative; } .image { width: 100%; height: auto; } .dialog-image { width: 100%; height: auto; } .zoomed-image { position: absolute; border: 1px solid #ccc; width: 200px; height: 200px; display: none; z-index: 9999; } </style> ``` 在该示例代码中,通过绑定 `@mousemove` 事件,监听鼠标在元素上的移动,并在事件处理函数中计算出放大镜的位置,然后通过设置 `zoomedImageStyle` 对象的属性来实现放大镜。同时,在需要放大的图片上绑定 `@click` 事件,当用户点击该图片时,弹出包含放大图片的 `Dialog` 对话框。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值