vue3实现淘宝商品详情页放大镜效果
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
效果展示:
先来一张效果图片,样式参考华为商城(遥遥领先)
使用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、监听缩略图的鼠标悬浮事件:@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'
}
}
至此放大镜的效果就实现了
源代码:
GitHub 加速计划 / vu / vue
207.52 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:1 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 3 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 3 个月前
更多推荐
已为社区贡献2条内容
所有评论(0)