『uniapp』自己实现手动图片列表滑动 + 图片手势缩放+ 图片点击缩放(详细图文注释)


欢迎关注 『uniapp』 专栏,持续更新中
欢迎关注 『uniapp』 专栏,持续更新中

预览效果

在这里插入图片描述


思路分析

这个目前有点问题,就是因为监听触摸导致手机上有点卡顿,体验上不如uni-swiper

主要是为了双指缩放和点击缩放综合运用了movable-area和movable-view,抛砖引玉,仅供参考.

后续找机会优化,也期待评论区的朋友们提点建议.


完整代码

<template>
	<view>
		<!-- 通过定位来控制面板显示 -->
		<view class="box" :style="widthStyle">
			<view class="bbox" :style="bboxStyle">
				<movable-area class="movable-area" v-for="(item, index) in localImageList" :style="widthStyle" :key="'key' + index">
					<movable-view @dblclick="dblclick(index)" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd" @scale="scale" @change="handleMovableViewChange" animation="true" damping="200" friction="2" class="movable-view" direction="all" scale="true" scale-min="1" scale-max="4" :scale-value="scaleList[index]">
						<view style="padding: 12px; border-bottom: 1px solid #ccc"> 我是一个安装教程,假装我是pdf, </view>
						<view style="height: 5px; background-color: beige"> </view>
						<view>{{ imageListIndex + 1 }}</view>
						<br />
						<image class="lookimg" :src="localImageList[index]" mode="widthFix"></image>
					</movable-view>
				</movable-area>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				// 开始和结束来记录左滑还是右滑,以及滑动距离
				touchStartX: 0,
				touchEndX: 0,
				changeText: '', // 操作
				moveX: 0, // 内部节点的x定位
				quickStartX: 0, // 快照
				scaleValue: 1, // 缩放倍数
				quickScale: 1, // 快照
				offset: 0, //偏移量
				isOffset: false, // 是否叠加偏移量
				//////
				cscs: false,
				localImageList: [], //图片本地url列表
				imgNum: 1, //pdf页数
				picListIndex: '1/1', //当前图片索引
				pdfUrl: '', //文件url
				language: '', //使用教程pdf语言

				isAndroid: true,
				imageListIndex: 0, //当前处于第几页

				isDisableTouch: false, //是否禁止滑动
				scaleList: new Array(16).fill(1),
				// scale: 1, // 图片放大倍数

				moveDirction: '', //移动方向
				changePageFlag: false, //

				minMoveLength: 50, //最小移动距离阈值

				xNow: 0, //当前x坐标 用于判断是否到边界
				startX: 0, //记录触摸开始时的

				screenWidth: 0,
			}
		},
		onLoad(option) {
			this.screenWidth = uni.getSystemInfoSync().screenWidth

			this.imgNum = 4
			this.picListIndex = '1/' + this.imgNum
			this.localImageList = [
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png',
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png',
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png',
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png',
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png',
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png',
				'../../static/1.png',
				'../../static/2.png',
				'../../static/3.png',
				'../../static/4.png'
			]
		},
		computed: {},
		props: {
			isBack: {
				type: Boolean,
				default: true
			},
			titleSize: {
				type: String,
				default: '34'
			},
			titleColor: {
				type: String,
				default: '#D2D2D2'
			},
			background: {
				type: Object,
				default: () => {
					return {
						background: '#272729'
					}
				}
			}
		},
		methods: {
			//双击缩放
			dblclick(index) {
				if (this.scaleList[index] < 1.5) {
					this.$set(this.scaleList, index, 1.5);
				} else if (this.scaleList[index] < 2) {
					this.$set(this.scaleList, index, 2);
				} else if (this.scaleList[index] >= 2) {
					this.$set(this.scaleList, index, 1);
				}
			},
			reset() {
				// 切换面板重置数据
				this.moveX = 0
				this.quickStartX = 0
				this.quickScale = 1
				this.scaleValue = 1
			},

			scale(event) {
				// 缩放过程事件
				this.scaleValue = event.detail.scale
			},
			handleMovableViewChange(event) {
				// movable-view @change事件
				this.moveX = event.detail.x
			},
			// 触控开始
			handleTouchStart(event) {
				this.quickStartX = this.moveX
				this.quickScale = this.scaleValue
				this.touchStartX = event.touches[0].clientX
			},
			// 触控过程
			handleTouchMove(event) {
				// 是否叠加偏移量
				if (this.moveX == this.quickStartX && this.scaleValue == this.quickScale) {
					this.isOffset = true
				} else {
					this.isOffset = false
				}
				// 偏移量
				this.offset = event.touches[0].clientX - this.touchStartX
			},
			// 触控结束
			handleTouchEnd(event) {
				this.touchEndX = event.changedTouches[0].clientX
				this.detectSwipeDirection()
				// 是否叠加偏移量
				this.isOffset = false
			},
			// 计算操作情况,判断是否要切换面板
			detectSwipeDirection() {
				let changeText = ''
				const touchDistance = this.touchEndX - this.touchStartX
				// 小于120像素的滑动不触发左右切换
				if (Math.abs(touchDistance) < 120) return
				// x快照和x定位不一致,证明非边界情况
				if (this.moveX != this.quickStartX) return
				// x快照和x定位不一致,证明是正在放大缩小
				if (this.scaleValue != this.quickScale) return
				if (touchDistance > 0) {
					console.log('向右滑动')
					changeText = '向右滑动'
					// 到底了
					if (this.imageListIndex == 0) return
					this.imageListIndex--
					// 快照和数据重置
					this.reset()
				} else if (touchDistance < 0) {
					console.log('向左滑动')
					changeText = '向左滑动'
					// 到底了
					if (this.imageListIndex == this.localImageList.length - 1) return
					this.imageListIndex++
					// 快照和数据重置
					this.reset()
				}
				console.log('滑动距离:', Math.abs(touchDistance))
				this.changeText = changeText + '滑动距离:' + Math.abs(touchDistance)
			}
		},
		computed: {
			widthStyle() {
				const width = `${this.screenWidth}px`
				return {
					width
				}
			},
			bboxStyle() {
				// console.log(' uni.getSystemInfoSync().screenWidth', uni.getSystemInfoSync().screenWidth)
				const isOffset = this.isOffset
				const width = `${100 * this.localImageList.length}vw`

				let base = -1 * this.screenWidth * this.imageListIndex
				const left = this.isOffset ? base + this.offset + 'px' : base + 'px'
				const transition = 'left 0.1s'
				// 返回一个对象,包含计算得到的样式
				return {
					width,
					left,
					transition
				}
			}
		}
	}
</script>
<style scoped>
	.box {
		height: 100vh;
		position: relative;
		overflow: hidden;
	}

	.bbox {
		display: flex;
		left: 0;
		position: relative;
		height: 100vh;
		/* background-color: bisque; */
	}

	.movable-view {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		height: 100%;
		width: 100%;
		position: relative;
	}

	.movable-area {
		height: 100%;
		/* background-color: blue; */
	}

	.lookimg {
		height: 100%;
		width: 100%;
		display: block;
		position: relative;
		top: 0px;
		left: 0px;
		border: 0rpx solid #d6d6d7;
		box-shadow: 0 -3rpx 5rpx 1rpx rgba(0, 0, 0, 0.1), 0 2rpx 5rpx 1rpx rgba(0, 0, 0, 0.1);
		/* margin-top: 15%; */
	}
</style>

总结

大家喜欢的话,给个👍,点个关注!给大家分享更多计算机专业学生的求学之路!

版权声明:

发现你走远了@mzh原创作品,转载必须标注原文链接

Copyright 2024 mzh

Crated:2024-4-1

欢迎关注 『uniapp』 专栏,持续更新中
欢迎关注 『uniapp』 专栏,持续更新中
『未完待续』


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发现你走远了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值