需求:两张图片重叠,中间有一条线作为分界线,拉动线可以分别显示左右图片
实现案例:
实现思路:
- 图2不动;
- 图1给一个div父元素,图1相对于父元素绝对定位,父元素相对组件盒子绝对定位,在分界线移动过程中,此父元素的 left 值与图1的 left 值设置成相反。
实现步骤:
- 获取到组件盒子宽度 imgBoxWidth;
- 点击分界线(黑色实线)时获取到此时分界线距浏览器左侧的距离 startX(mousedown 的时候,保存下来当前鼠标相对于浏览器视口的 x 坐标);
- 获取分界线距组件盒子左侧的距离 divideLineLeft;
- 获取分界线移动的距离 moveLeft(mousemove 的时候,得到新的 x 坐标,与原始 x 坐标相减,得到位移量);
- 计算比例,let L = (divideLineLeft + moveLeft) / imgBoxWidth * 100
- 图1父元素的 left 值为 L%,图1的 left 值为 -L%,分界线的 left 值为 L%。
示例图:
完整代码:
<template>
<div class="main-box" :style="`height: ${height}rem;`">
<div class="img_drag_change">
<div ref="imgBox" class="img-box">
<div ref="beforeImg" class="before-img">
<div class="info">{{props.info[1].title}}</div>
<img :src="props.info[1].url" />
</div>
<div ref="afterImg" class="after-img" :style="`left: ${left}%;`">
<div :style="`right: ${left}%;`" class="info" >{{props.info[0].title}}</div>
<img :style="`left: -${left}%;`" :src="props.info[0].url" />
</div>
</div>
<!-- 分割线 -->
<div class="divide_line" ref="divide_line" :style="`left: ${left}%;`">
<div class="item">
<!-- 需要增加手机端兼容事件 -->
<div class="icon-box" @touchstart="mousedown" @mousedown="mousedown">
<span> 左 </span>
<span> 右 </span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, ref, onMounted } from 'vue';
// 引入两张图片
import img_left from '@/assets/img/common/img1.jpg';
import img_right from '@/assets/img/common/img2.jpg';
// 父组件传值
let props = defineProps({
height: {
type: Number,
default: 0,
},
info: {
type: Array,
default: () => {
return [
{
url: img_right,
title: "右图",
},
{
url: img_left,
title: "左图",
}
]
}
}
})
// imgbox
let imgBox = ref(null); // 盒子
let imgBoxWidth = ref(0); // 盒子宽度
let beforeImg = ref(null); // 第一张图片
let afterImg = ref(null); // 第二张图片
let left = ref(0); // 初始的 left
let rem = ref(0.0625); // px转rem
let height = ref(500); //默认高度
let divide_line = ref('null'); // 分割线节点
if (props.height > 0) {
height.value = props.height * rem.value;
}
else if(props.height == "auto"){
height = "auto";
}
else {
height.value = height.value * rem.value;
}
onMounted(() => {
imgBoxWidth.value = imgBox.value.offsetWidth;
left.value = 50;
})
let range = function(num, min, max) {
if (num < min) {
return min;
}
if (num > max) {
return max;
}
return num;
}
// 需要加兼容手机端事件
// 鼠标按下事件
const mousedown = (e) => {
console.log("按下")
// 拿到盒子宽度
imgBoxWidth.value = imgBox.value.offsetWidth;
let min = 16 / imgBoxWidth.value * 100;
let startX = e.clientX || e.touches[0].pageX; // 鼠标按下的初始 clientX 值
let divideLineLeft = divide_line.value.offsetLeft; //获取盒子的初始left值
console.log(e, startX, divideLineLeft);
// 鼠标移动事件
let onmousemove = (e) => {
let moveLeft = (e.clientX || (e.touches && e.touches[0].pageX) || 0) - startX; // 拿到鼠标移动的 moveLeft,e.touches[0]是兼容移动端,加上 || 0 是因为在 PC 端鼠标向左移出浏览器时会报错
let l = (divideLineLeft + moveLeft) / imgBoxWidth.value * 100;
left.value = range(l, min, 100 - min);
}
document.addEventListener("mousemove", onmousemove);
document.addEventListener("touchmove", onmousemove);
// 鼠标抬起事件
let onmouseup = (e) => {
// 清除移动和抬起事件
document.removeEventListener("mousemove", onmousemove);
document.removeEventListener("touchmove", onmousemove);
document.removeEventListener("mouseup", onmouseup);
document.removeEventListener("touchend", onmouseup);
}
document.addEventListener("mouseup", onmouseup);
document.addEventListener("touchend", onmouseup);
return false; //阻止默认事件
}
</script>
<style lang="scss" scoped>
.main-box{
height: 100%;
}
.img_drag_change {
user-select: none; // 控制页面文字不能被选中
margin: 0;
position: relative;
top: 12px;
height: calc(100% - 24px);
}
.img-box {
font-size: 0;
width: 100%;
top: 8px;
height: calc(100% - 16px);
overflow: hidden;
position: relative;
border-radius: 15px;
>div.after-img {
position: absolute;
left: 0;
z-index: 1;
}
>div {
position: relative;
left: 0;
width: 100%;
height: 100%;
top: 0;
overflow: hidden;
background-color: #AAAAAA;
.info {
position: absolute;
color: #fff;
z-index: 1;
font-size: 20px;
white-space: nowrap;
overflow: hidden;
padding: 20px;
}
>img {
width: 100%;
height: 100%;
object-fit: cover;
position: relative;
top: 0;
left: 0;
}
}
>div.after-img .info {
text-align: right;
right: 0;
}
}
.divide_line {
width: 2px;
height: 100%;
top: 0;
left: 50px;
background: #dddddd;
position: absolute;
z-index: 1;
overflow: visible;
pointer-events: none; // pointer-events:是否对指针事件做出反应。none:元素不能对鼠标事件做出反应
.item {
height: 100%;
display: flex;
align-items: center;
width: 10px;
position: relative;
pointer-events: auto; // auto:默认值,设置该属性链接可以正常点击访问。
}
.icon-box {
position: absolute;
left: -1rem;
font-size: 1rem;
color: #dddddd;
display: flex;
align-items: center;
justify-content: center;
margin-left: 1px;
transition: all .1s;
span {
cursor: pointer;
}
}
}
</style>