先实现如下效果,图片可自由缩放旋转删除移动。通过两个点控制。
实现效果Demo如下:
这是Demo代码,可直接复制到项目中运行看效果。
支持小程序、APP。
一开始考虑使用的movable-area+movable-view。但因为需要先禁用移动,当点击的是右下角旋转按钮时才允许移动,所以需要在touchstart里面disabled改为false,然后再touchend中再次修改为禁止移动。
但movable-view不支持在touchstart中修改disabled立刻生效,只能在下次点击时生效,所以使用view+css实现。
<template>
<view class="page-body">
<view class="move-area">
<view v-if="!isHide" class="move-box" style="transform-origin:center;"
@touchmove="itemMove" @touchstart="itemtouch" @touchend="itemend"
:style="{transform: 'translate(' + itemX + 'px, ' + itemY +'px) rotate('+tmpRotate+'deg) scale('+tmpScale+')'}">
<view class="opt-icon del-icon" :style="{transform: 'scale(' + (1 / tmpScale) + ')'}" @touchstart="startMode(2)"></view>
<view class="opt-icon edit-icon" :style="{transform: 'scale(' + (1 / tmpScale) + ')'}" @touchstart="startMode(1)"></view>
<view class="move-body" @touchstart="startMode(0)">这是内容</view>
</view>
</view>
</view>
</template>
<script>
const nearDg = 7; // 用于控制当近似垂直或水平时,视为垂直或水平的容差。
let lastX = 0, lastY = 0;
export default {
data() {
return {
itemX: 0,
itemY: 0,
realCenterX: 0,
realCenterY: 0,
halfWidth: 75, // px
halfHeight: 75, // px
realWidth: 0,
realHeight: 0,
offset: 12.5, // px
touchMode: 0, // 0移动 1缩放旋转 2删除
startX: 0,
startY: 0,
startAngle: 0,
startDist: 0,
tmpRotate: 0,
initRotate: 0,
tmpScale: 1,
initScale: 1,
isHide: false,
}
},
onLoad:function(){
this.itemX = 70;
this.itemY = 100;
},
mounted:function(){
let view = uni.createSelectorQuery().in(this).select(".move-box");
view.boundingClientRect(data => {
this.realWidth = data.width;
this.realHeight = data.height;
this.realCenterX = data.right - data.width / 2;
this.realCenterY = data.bottom - data.height / 2;
}).exec();
},
methods: {
itemMove: function(e){
let curX = e.touches[0].clientX;
let curY = e.touches[0].clientY;
if(this.touchMode === 0){
this.itemX += curX - lastX;
this.itemY += curY - lastY;
this.realCenterX += curX - lastX;
this.realCenterY += curY - lastY;
lastX = curX;
lastY = curY;
}
if(this.touchMode !== 1)return;
let angle = this.angle(this.realCenterX, this.realCenterY, curX, curY);
if(this.startAngle === 0){
this.startX = curX;
this.startY = curY;
this.startAngle = angle;
this.startDist = Math.hypot(this.startX - this.realCenterX, this.startY - this.realCenterY);
}
else{
let rotate = this.initRotate + Math.round(angle - this.startAngle);
let leftDg = Math.abs(rotate % 90);
if(leftDg < nearDg || 90 - leftDg < nearDg){
rotate = Math.round(rotate / 90) * 90;
}
this.tmpRotate = rotate;
let curDist = Math.hypot(curX - this.realCenterX, curY - this.realCenterY);
this.tmpScale = this.initScale * curDist / this.startDist;
}
},
itemtouch: function(e){
lastX = e.touches[0].clientX;
lastY = e.touches[0].clientY;
},
itemend: function(e){
this.startAngle = 0;
this.initRotate = this.tmpRotate;
this.initScale = this.tmpScale;
},
startMode: function(mode){
this.touchMode = mode;
if(this.touchMode === 2){
this.isHide = true;
}
},
angle: function(cx, cy, ex, ey){
var dy = ey - cy;
var dx = ex - cx;
var theta = Math.atan2(dy, dx); // range (-PI, PI]
theta *= 180 / Math.PI; // rads to degs, range (-180, 180]
if (theta < 0) theta = 360 + theta; // range [0, 360)
return theta;
}
}
}
</script>
<style lang="scss">
.page-body{
padding: 5px;
}
.move-area{
width: 365px;
height: 500px;
}
.move-box{
position: relative;
width: 175px;
height: 175px;
padding: 1px;
}
.opt-icon{
position: absolute;
width: 25px;
height: 25px;
border-radius: 50%;
}
.del-icon{
background-color: red;
left: 0;
top: 0;
}
.edit-icon{
background-color: green;
right: 0;
bottom: 0;
}
.move-body{
margin: 12.5px;
width: 150px;
height: 150px;
border: 2px solid green;
}
</style>