uniapp开发微信小程序实现图片的双指缩放、移动、旋转功能

971 阅读2分钟

记录uniapp开发微信小程序实现图片的双指缩放、移动、旋转功能 直接上代码了

<template>
	<view class="previewImage">
		<movable-area scale-area class="imageScroll">
			<movable-view
				class="img"
				:style="{ width: width + 'px', height: height + 'px' }"
				:disabled="lock"
				:scale="!lock"
				:x="x"
				:y="y"
				direction="all"
				out-of-bounds
				inertia
				scale-min="0.5"
				scale-max="10"
				:scale-value="scale"
				@scale="onScale"
			>
				<image
					v-if="currentImage"
					:src="currentImage"
					:style="{ width: imgWidth + 'px', transform: `rotate(${roateDeg}deg)` }"
					mode="widthFix"
					ref="img"
					@click="doubleClick"
				></image>
			</movable-view>
		</movable-area>
	</view>
	<view class="lock" v-if="lock">
		<up-icon name="lock-fill" color="#fff" size="36"></up-icon>
	</view>
	<view class="options">
		<up-icon name="arrow-left-double" color="#fff" size="24" @click="prev"></up-icon>
		<up-icon v-if="!lock" name="lock-fill" color="#fff" size="24" @click="changeLock"></up-icon>
		<up-icon v-else name="lock-opened-fill" color="#694127" size="24" @click="changeLock"></up-icon>
		<image src="../../static/images/roate.svg" alt="" @click="roate" />
		<image src="../../static/images/1_1.svg" alt="" @click="changeScale(10)" />
		<image src="../../static/images/lashen.svg" alt="" @click="changeScale(1)" />
		<up-icon name="arrow-right-double" color="#fff" size="24" @click="next"></up-icon>
	</view>
</template>

<script setup>
import { nextTick, ref, onUnmounted } from 'vue';
import { onLoad, onShareAppMessage } from '@dcloudio/uni-app';

const cname = ref('');
const currentImage = ref('');
const lock = ref(false); // 是否锁定
const imgUrl = ref([]);
const roateDeg = ref(0);
const scale = ref(1);
const scale_2 = ref(0); //临时存放变化的scale
const x = ref(20);
const y = ref(100);
const width = ref(0);
const imgWidth = ref(0);
const height = ref(0);

// 双击图片
const lastTapDiffTime = ref(0);
const lastTapTimeoutFunc = ref(null);
const doubleClick = () => {
	let curTime = new Date().getTime();
	let lastTime = lastTapDiffTime.value;
	lastTapDiffTime.value = curTime;
	//两次点击间隔小于300ms, 认为是双击
	let diff = curTime - lastTime;
	if (diff < 300) {
		console.log('双击');
		if (scale.value < 10) {
			let num = scale.value + 3;
			changeScale(num);
		} else changeScale(1);
		clearTimeout(lastTapTimeoutFunc.value); // 成功触发双击事件时,取消单击事件的执行
	} else {
		// 单击事件延时300毫秒执行
		lastTapTimeoutFunc.value = setTimeout(() => {
			console.log('单击');
		}, 300);
	}
};

// 当缩放变化
const onScale = (e) => {
	const s = e.detail.scale;
	if (s.toFixed(0) != scale_2.value && s.toFixed(1) > 1) {
		uni.showToast({
			title: `放大${s.toFixed(1)}倍`,
			icon: 'none',
			duration: 500
		});
	} else if (s < 1) {
		uni.showToast({
			title: `缩小${s.toFixed(1)}倍`,
			icon: 'none',
			duration: 500
		});
	} else if (s == 1) {
		uni.showToast({
			title: `恢复原图比例`,
			icon: 'none',
			duration: 500
		});
	}
	// 这个函数会频繁触发只能暂时存储scale值  在切换时在赋值,就不会有变化的感觉了,
	scale_2.value = s;
};

// 上一张图
const prev = () => {
	if (lock.value) return lockToast();
	let index = imgUrl.value.findIndex((item) => item.url === currentImage.value);
	if (index === 0) {
		return uni.showToast({
			title: '已经是第一张了',
			icon: 'none'
		});
	}
	currentImage.value = imgUrl.value[index - 1].url;
	roateDeg.value = 0;
	scale.value = 1;
	getImageInfo(currentImage.value, false);
};

// 下一张图
const next = () => {
	if (lock.value) return lockToast();
	let index = imgUrl.value.findIndex((item) => item.url === currentImage.value);
	if (index === imgUrl.value.length - 1) {
		return uni.showToast({
			title: '已经是最后一张了',
			icon: 'none'
		});
	}
	currentImage.value = imgUrl.value[index + 1].url;
	roateDeg.value = 0;
	scale.value = 1;
	getImageInfo(currentImage.value, false);
};

// 初始化图片样式
const initImage = async () => {
	scale.value = scale_2.value; // 记录双指缩放的值,因为双指缩放时scale的值并没有变化
	wx.nextTick(async () => {
		scale.value = 1; // 初始化缩放比例
		if ((roateDeg.value / 90) % 2 !== 0) {
			// 横向
			await getImageInfo(currentImage.value, true);
		} else {
			// 纵向
			await getImageInfo(currentImage.value, false);
		}
	});
};

// 旋转图片
const roate = async () => {
	if (lock.value) return lockToast();
	await initImage();
	roateDeg.value += 90;
};

// 缩放图片
const changeScale = (val) => {
	if (lock.value) return lockToast();
	scale.value = scale_2.value; // 记录双指缩放的值,因为双指缩放时scale的值并没有变化
	wx.nextTick(async () => {
		if (val == 1) await initImage();
		else scale.value = val;
	});
};

// 切换锁定
const changeLock = () => {
	lock.value = !lock.value;
	uni.showToast({
		title: lock.value ? '图像已锁定' : '图像已解锁',
		icon: 'none'
	});
};

// 获取图片信息 并设置样式
const getImageInfo = (url, rotateFlag) => {
	return new Promise((resolve, reject) => {
		uni.getImageInfo({
			src: url,
			success: (res) => {
				const ratio = res.width / res.height; // 计算图片宽高比 >1为横图 <1为竖图
				const window = wx.getWindowInfo(); // 获取屏幕宽高
				// 旋转成90 270 时,宽高 互换
				if (rotateFlag) {
					height.value = window.screenWidth * 0.9;
					width.value = height.value / ratio;
					imgWidth.value = height.value;
					if (ratio < 1) {
						// 纵向长图时 顶左边  上下为中间
						x.value = 0;
					} else {
						// 横向长图时
						x.value = (window.screenWidth - width.value) / 2;
					}
				} else {
					// 旋转成0 180 360时
					width.value = window.screenWidth * 0.9;
					height.value = width.value / ratio;
					imgWidth.value = width.value;
					x.value = (window.screenWidth - width.value) / 2;
				}
				y.value = (window.screenHeight - height.value) / 3.5;
				resolve();
			}
		});
	});
};

// 图像锁定提示
const lockToast = () => {
	uni.showToast({
		title: '图像锁定,无法操作',
		icon: 'none'
	});
};

/**
 * 分享
 */
onShareAppMessage(() => {
	return {
		title: cname.value,
		imageUrl: currentImage.value,
		path: '/pages/previewImage/previewImage?cname=' + encodeURIComponent(JSON.stringify(cname.value)) + '&imgUrl=' + encodeURIComponent(JSON.stringify(currentImage.value))
	};
});

onLoad((options) => {
	// 获取页面参数
	imgUrl.value = JSON.parse(decodeURIComponent(options.imgUrl));
	cname.value = JSON.parse(decodeURIComponent(options.cname));
	uni.setNavigationBarTitle({
		title: cname.value
	});
	currentImage.value = imgUrl.value[0].url;
	getImageInfo(currentImage.value, false);
});
// 页面卸载时清空定时器
onUnmounted(() => {
	clearTimeout(lastTapTimeoutFunc.value); // 成功触发双击事件时,取消单击事件的执行
	lastTapDiffTime.value = 0;
	lastTapTimeoutFunc.value = null;
});
</script>

<style lang="scss" scoped>
.previewImage {
	width: 100vw;
	height: 100vh;
	background: linear-gradient(to bottom, #e3e3e3, #5a5859);
	.imageScroll {
		width: 100vw;
		height: calc(100vh - 120rpx);
		.img {
			display: flex;
			align-items: center;
			justify-content: center;
			// background-color: green;
			image {
				flex-shrink: 0;
				width: 100%;
				transition: 0.2s;
			}
		}
	}
}
.lock {
	position: fixed;
	bottom: 200rpx;
	left: 50rpx;
	z-index: 999;
}
.options {
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: space-between;
	height: 120rpx;
	width: 100%;
	background-color: #313131;
	position: fixed;
	bottom: 0;
	padding: 20rpx 32rpx 50rpx;
	image {
		width: 48rpx;
		height: 48rpx;
	}
}
</style>