记录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>