【vue3】拉动分割线切换左右两张图片

需求:两张图片重叠,中间有一条线作为分界线,拉动线可以分别显示左右图片

实现案例:

实现思路:

  1. 图2不动;
  2. 图1给一个div父元素,图1相对于父元素绝对定位,父元素相对组件盒子绝对定位,在分界线移动过程中,此父元素的 left 值与图1的 left 值设置成相反。

实现步骤:

  1. 获取到组件盒子宽度 imgBoxWidth;
  2. 点击分界线(黑色实线)时获取到此时分界线距浏览器左侧的距离 startX(mousedown 的时候,保存下来当前鼠标相对于浏览器视口的 x 坐标);
  3. 获取分界线距组件盒子左侧的距离 divideLineLeft;
  4. 获取分界线移动的距离 moveLeft(mousemove 的时候,得到新的 x 坐标,与原始 x 坐标相减,得到位移量);
  5. 计算比例,let L = (divideLineLeft + moveLeft) / imgBoxWidth * 100
  6. 图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>
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值