鼠标移动到商品图片时,根据鼠标移动的一块区域,在右侧放大并显示出来,鼠标移出后关闭放大的悬窗;
注:本文采用Vue3版本进行开发,原理和方法都一样,改为Vue2也很简单
关键词:鼠标移动。那么就联系到了鼠标事件mousemove(移动)、mouseleave(移出目标区域)
理清了思路,接下来就办了
<div class="pop">
<div
class="pop-box"
:style="{ width: boxStyle.width + 'px', height: boxStyle.height + 'px', cursor: flag ? 'move' : '' }"
>
<img :src="imgsrc" ref="moveDom" alt />
<div
v-if="flag"
class="border"
:style="{ left: styleObj.left + 'px', top: styleObj.top + 'px' }"
></div>
</div>
<!-- 还可以利用background-position实现 -->
<!--弹出来的放大区域图片 利用定位 -->
<div class="pop-image" v-if="flag">
<img :style="popStyle" :src="imgsrc" alt />
</div>
</div>
onMounted(() => {
moveDom.value.addEventListener('mousemove', moveEvent)
moveDom.value.addEventListener('mouseleave', mouseleaveEvent)
})
//不要忘了卸载
onBeforeUnmount(() => {
moveDom.value.removeEventListener('mouseleave', mouseleaveEvent)
moveDom.value.removeEventListener('mousemove', moveEvent)
})
// 鼠标移动事件
const moveEvent = rafThrottle(e => {
let event = e || window.event;
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
flag.value = true
let { offsetX, offsetY } = e;
let { height, width } = boxStyle.value
styleObj.value = {
left: (offsetX - watchWidth / 2) > 0 ? (offsetX + watchWidth / 2) >= width ? width - watchWidth : offsetX - watchWidth / 2 : 0,
top: (offsetY - watchWidth / 2) > 0 ? (offsetY + watchWidth / 2) >= height ? height - watchWidth : offsetY - watchWidth / 2 : 0
}
let { left, top } = styleObj.value;
popStyle.value = `left:-${left * (900 / watchWidth)}px;top:-${top * (900 / watchWidth)}px`
})
<template>
<div class="pop">
<div
class="pop-box"
:style="{ width: boxStyle.width + 'px', height: boxStyle.height + 'px', cursor: flag ? 'move' : '' }"
>
<img :src="imgsrc" ref="moveDom" alt />
<div
v-if="flag"
class="border"
:style="{ left: styleObj.left + 'px', top: styleObj.top + 'px' }"
></div>
</div>
<!-- 还可以利用background-position实现 -->
<!-- 利用定位 -->
<div class="pop-image" v-if="flag">
<img :style="popStyle" :src="imgsrc" alt />
</div>
</div>
</template>
<script>
import { ref, onBeforeUnmount, onMounted } from '@vue/runtime-core'
export default {
name: 'Pop',
setup() {
const imgsrc = ref('http://zs.test//files/products/img/20220317/59fce583589a9463d6a1822d9ae693c9.jpg')
const moveDom = ref(null)
const img = new Image()
const flag = ref(false)
const popStyle = ref('')
const watchWidth = 200;
img.src = imgsrc.value
const boxStyle = ref({
width: 300,//固定了宽高
height: 300//img.height * (300 / img.width)
})
const styleObj = ref({
left: 0, top: 0
})
onMounted(() => {
moveDom.value.addEventListener('mousemove', moveEvent)
moveDom.value.addEventListener('mouseleave', mouseleaveEvent)
})
onBeforeUnmount(() => {
moveDom.value.removeEventListener('mouseleave', mouseleaveEvent)
moveDom.value.removeEventListener('mousemove', moveEvent)
})
const rafThrottle = (fn) => {
let locked = false;
return function (...args) {
if (locked) return;
locked = true;
window.requestAnimationFrame(_ => {
fn.apply(this, args);
locked = false;
});
};
}
// 鼠标移动事件
const moveEvent = rafThrottle(e => {
let event = e || window.event;
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
flag.value = true
let { offsetX, offsetY } = e;
let { height, width } = boxStyle.value
//计算边界条件
styleObj.value = {
left: (offsetX - watchWidth / 2) > 0 ? (offsetX + watchWidth / 2) >= width ? width - watchWidth : offsetX - watchWidth / 2 : 0,
top: (offsetY - watchWidth / 2) > 0 ? (offsetY + watchWidth / 2) >= height ? height - watchWidth : offsetY - watchWidth / 2 : 0
}
let { left, top } = styleObj.value;
//根据比例计算放大的区域
popStyle.value = `left:-${left * 3}px;top:-${top * 3}px`
})
// 鼠标移出
const mouseleaveEvent = rafThrottle(e => {
flag.value = false
})
return {
flag,
popStyle,
moveDom,
styleObj,
imgsrc,
boxStyle,
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.pop {
position: relative;
&-box {
position: relative;
box-sizing: border-box;
overflow: hidden;
border: 1px dashed rgb(253, 253, 253);
img {
width: 100%;
height: 100%;
}
.border {
position: absolute;
width: 200px;
height: 200px;
pointer-events: none;
background-color: rgba(127, 255, 212, 0.315);
}
}
&-image {
width: 600px;
height: 600px;
position: absolute;
top: 0;
overflow: hidden;
left: 300px;
img {
position: absolute;
width: 900px;
height: 900px;
}
}
}
</style>
提示一下:使用vue3获取ref常常遇到的一个坑是,忘记return,会导致获取的ref是null
如果不明白为什么用offsetX, offsetY,建议查阅相关资料弄懂了再来看该文章