vue3.0中手写商品放大镜效果
Posted 奥特曼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3.0中手写商品放大镜效果相关的知识,希望对你有一定的参考价值。
实现效果
实现分析
(1) 基础组件布局
goods-info.vue 父组件
<template>
<div class="goods-info">
<!--左边-->
<div class="media">
<GoodsImage />
<!-- 商品图片组件 -->
</div>
</div>
</template>
<script>
import GoodsImage from './components/goods-image.vue'
export default {
components: {
GoodsImage
}
}
</script>
<style lang="less" scoped>
.goods-info {
min-height: 600px;
background: #fff;
display: flex;
.media {
width: 580px;
height: 600px;
padding: 30px 50px;
}
.spec {
flex: 1;
padding: 30px 30px 30px 0;
}
}
</style>
goods-image图片子组件
<template>
<div class="goods-image">
<div class="middle">
<img src="https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg" alt="">
</div>
<ul class="small">
<li v-for="i in 4" :key="i">
<img src="https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg" alt="">
</li>
</ul>
</div>
</template>
<style scoped lang="less">
.goods-image {
width: 480px;
height: 400px;
position: relative;
display: flex;
.middle {
width: 400px;
height: 400px;
background: #f5f5f5;
}
.small {
width: 80px;
li {
width: 68px;
height: 68px;
margin-left: 12px;
margin-bottom: 15px;
cursor: pointer;
&:hover,&.active {
border: 2px solid @xtxColor;
}
}
}
}
</style>
目前效果
(2)父传子 传图片数据
goods-info.vue
<template>
<GoodsImage :picture="list.mainPictures" />
</template>
<script>
import GoodsImage from './components/goods-image.vue'
import { findGoods } from '@/api/goods' // 引入api
import { ref } from 'vue'
export default {
components: {
GoodsImage
},
setup () {
const list = ref([]) // 用来接收数据
findGoods(4005091).then((data) => { // 实际上是路由 route.params.id 模拟采用指定id
list.value = data.result
})
}
}
</script>
goods-image子组件
<template>
<div class="goods-image">
<div class="middle">
<img :src="pictures[currIdx]" alt="">
</div>
<ul class="small">
<li v-for="(img,idx) in pictures" @mouseenter="currIdx=idx" :key="img">
<img :src="img" alt="">
</li>
</ul>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'goods-image',
props: {
pictures: {
type: Array, //接收父组件传过来的数据
default: () => []
}
},
setup () {
const currIdx = ref(0) //定义图片的索引
return { currIdx }
}
}
</script>
@mouseenter="currIdx=idx" 当鼠标进入小图片时 拿到当前的索引 给左图片进行改变 pictures[currIdx]
(3)遮罩效果
goods-image子组件 添加html结构和样式
<div class="middle" ref="target">
<img :src="images[currIndex]" alt="">
+ <div class="layer"></div>
</div>
<style scoped lang="less">
.middle {
width: 400px;
height: 400px;
+ position: relative;
+ cursor: move;
+ .layer {
+ width: 200px;
+ height: 200px;
+ background: rgba(0,0,0,.2);
+ left: 0;
+ top: 0;
+ position: absolute;
+ }
}
</style>
(4)监听要放大元素的鼠标位置
使用vueuse 工具库中的 useMouseInElement 方法 它可以检测到鼠标距离左上角的距离和当前鼠标是否在元素内
1. 使用useMouseInElement 方法 使用vueuse库中都要先安装
npm i @vueuse/core
2. 引入并使用
测试:{{elementX}}, {{elementY}}, {{isOutside}} //最后不需要删掉 即可
<div class="middle" ref="target">
// 导入 useMouseInElement
import { useMouseInElement } from '@vueuse/core'
setup(){
// 以哪个元素为基准,计算鼠标的位置
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)
return { currIndex, target, elementX, elementY, isOutside }
}
目前效果
(5)使遮罩层跟随鼠标的移动
<div class="layer" :style="shadeMove"></div>
setup () {
// 当前图片的索引
const currIdx = ref(0)
// 获取鼠标在哪个当前元素内
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)
// 准备要给遮罩绑定的 left top 值
const shadeMove = {
left: '0px',
top: ' 0px'
}
// 监听鼠标移动的变化
watch([elementX, elementY, isOutside], () => {
// elementX 要加.value 不加不生效 小坑!
const left = elementX.value - 100
const top = elementY.value - 100
shadeMove.left = left + 'px'
shadeMove.top = top + 'px'
})
return { currIdx, target, elementX, elementY, isOutside, shadeMove }
}
注意为什么要 位置-100呢 首先知道 遮罩的大小是200px 如果不-100 遮罩会在鼠标的左上角显示
图解1
图解2
现在的遮罩是 absolute 绝对定位 也就是可以在页面中 自由飞翔~~~ 所以要对遮罩进行控制 只能在当前div块下自由飞翔~~
加一下判断
watch([elementX, elementY, isOutside], () => {
let left = elementX.value
let top = elementY.value
if (left < 0) left = 0
if (left > 200) left = 200
if (top < 0) top = 0
if (top > 200) top = 200
shadeMove.left = left + 'px'
shadeMove.top = top + 'px'
})
(6)准备大图容器 用来放大显示
<div class='goods-image'>
+ <div class="large" :style="{backgroundImage: `url(${pictures[currIdx]})`}"></div>
<!-- 中图 -->
<div class="middle" ref="target"> 省略其他...</div>
<!-- 小图 -->
<ul class="small">省略其他...</ul>
</div>
.goods-image {
width: 480px;
height: 400px;
position: relative;
display: flex;
+ z-index: 500;
+ .large {
+ position: absolute;
+ top: 0;
+ left: 412px;
+ width: 400px;
+ height: 400px;
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
+ background-repeat: no-repeat;
+ background-size: 800px 800px;
+ background-color: #f8f8f8;
+ }
通过background-size进行放大一倍 目前效果图 使用绝对定位 移动到最右边
(7)实现遮罩移动大图跟随移动
<div class="large" :style="{backgroundImage: `url(${pictures[currIdx]})`,...bigImg}"></div>
// 定义用来控制大图的显示位置
const bigImg = {
'background-position-X': '0px',
'background-position-Y': '0px'
}
watch([elementX, elementY, isOutside], () => {
let left = elementX.value - 100
let top = elementY.value - 100
if (left < 0) left = 0
if (left > 200) left = 200
if (top < 0) top = 0
if (top > 200) top = 200
shadeMove.left = left + 'px'
shadeMove.top = top + 'px'
+ // 因为是小图的二倍 所以 * 2
+ bigImg['background-position-X'] = -left * 2 + 'px'
+ bigImg['background-position-Y'] = -top * 2 + 'px'
})
(8)鼠标移出隐藏遮罩和大图
加上两个v-show isOutside代表在盒子外就为true 所以做一个取反
<!-- 大图 -->
<div class="large" v-show="!isOutside" :style="{backgroundImage: `url(${pictures[currIdx]})`,...bigImg}"></div>
<!-- 遮罩 -->
<div class="layer" v-show="!isOutside" :style="shadeMove"></div>
完整代码
goods-info.vue 父组件
<template>
X:{{elementX}}Y:{{elementY}} 是否在元素外 {{isOutside}}
<div class="goods-image">
<!-- 大图 -->
<div class="large" :style="{backgroundImage: `url(${pictures[currIdx]})`,...bigImg}"></div>
<!-- 中图 -->
<div class="middle">
<!-- 左侧图片 -->
<img :src="pictures[currIdx]" alt="" ref="target" >
<!-- 遮罩 -->
<div class="layer" :style="shadeMove"></div>
</div>
<ul class="small">
<!-- 右侧小图片 -->
<li v-for="(img,idx) in pictures" @mouseenter="currIdx=idx" :key="img">
<img :src="img" alt="">
</li>
</ul>
</div>
</template>
<script>
import { ref, watch } from 'vue'
import { useMouseInElement } from '@vueuse/core'
export default {
name: 'goods-image',
props: {
pictures: {
type: Array,
default: () => []
}
},
setup () {
// 当前图片的索引
const currIdx = ref(0)
// 获取鼠标在当前元素内的距离
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)
const shadeMove = {
left: '0px',
top: '0px'
}
const bigImg = {
'background-position-X': '0px',
'background-position-Y': '0px'
}
watch([elementX, elementY, isOutside], () => {
let left = elementX.value - 100
let top = elementY.value - 100
if (left < 0) left = 0
if (left > 200) left = 200
if (top < 0) top = 0
if (top > 200) top = 200
shadeMove.left = left + 'px'
shadeMove.top = top + 'px'
bigImg['background-position-X'] = -left * 2 + 'px'
bigImg['background-position-Y'] = -top * 2 + 'px'
})
return { currIdx, target, elementX, elementY, isOutside, shadeMove, bigImg }
}
}
</script>
<style scoped lang="less">
.goods-image {
width: 480px;
height: 400px;
position: relative;
display: flex;
z-index: 500;
.large {
position: absolute;
top: 0;
left: 412px;
width: 400px;
height: 400px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
background-repeat: no-repeat;
background-size: 800px 800px;
background-color: #f8f8f8;
}
.middle {
width: 400px;
height: 400px;
background: #f5f5f5;
position: relative;
cursor: move;
.layer {
width: 200px;
height: 200px;
background: rgba(0,0,0,.2);
left: 0;
top: 0;
position: absolute;
}
}
.small {
width: 80px;
li {
width: 68px;
height: 68px;
margin-left: 12px;
margin-bottom: 15px;
cursor: pointer;
&:hover,&.active {
border: 2px solid @xtxColor;
}
}
}
}
</style>
goods-image图片子组件
<template>
X:{{elementX}}Y:{{elementY}} 是否在元素外 {{isOutside}}
<div class="goods-image">
<!-- 大图 -->
<div class="large" v-show="!isOutside" :style="{backgroundImage: `url(${pictures[currIdx]})`,...bigImg}"></div>
<!-- 中图 -->
<div class="middle">
<!-- 左侧图片 -->
<img :src="pictures[currIdx]" alt="" ref="target" >
<!-- 遮罩 -->
<div class="layer" v-show="!isOutside" :style="shadeMove"></div>
</div>
<ul class="small">
<!-- 右侧小图片 -->
<li v-for="(img,idx) in pictures" @mouseenter="currIdx=idx" :key="img">
<img :src="img" alt="">
</li>
</ul>
</div>
</template>
<script>
import { ref, watch } from 'vue'
import { useMouseInElement } from '@vueuse/core'
export default {
name: 'goods-image',
props: {
pictures: {
type: Array,
default: () => []
}
},
setup () {
// 当前图片的索引
const currIdx = ref(0)
// 获取鼠标在当前元素内的距离
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)
const shadeMove = {
left: '0px',
top: '0px'
}
const bigImg = {
'background-position-X': '0px',
'background-position-Y': '0px'
}
watch([elementX, elementY, isOutside], () => {
let left = elementX.value - 100
let top = elementY.value - 100
if (left < 0) left = 0
if (left > 200) left = 200
if (top < 0) top = 0
if (top > 200) top = 200
shadeMove.left = left + 'px'
shadeMove.top = top + 'px'
bigImg['background-position-X'] = -left * 2 + 'px'
bigImg['background-position-Y'] = -top * 2 + 'px'
})
return { currIdx, target, elementX, elementY, isOutside, shadeMove, bigImg }
}
}
</script>
<style scoped lang="less">
.goods-image {
width: 480px;
height: 400px;
position: relative;
display: flex;
z-index: 500;
.large {
position: absolute;
top: 0;
left: 412px;
width: 400px;
height: 400px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
background-repeat: no-repeat;
background-size: 800px 800px;
background-color: #f8f8f8;
}
.middle {
width: 400px;
height: 400px;
background: #f5f5f5;
position: relative;
cursor: move;
.layer {
width: 200px;
height: 200px;
background: rgba(0,0,0,.2);
left: 0;
top: 0;
position: absolute;
}
}
.small {
width: 80px;
li {
width: 68px;
height: 68px;
margin-left: 12px;
margin-bottom: 15px;
cursor: pointer;
&:hover,&.active {
border: 2px solid @xtxColor;
}
}
}
}
</style>
以上是关于vue3.0中手写商品放大镜效果的主要内容,如果未能解决你的问题,请参考以下文章