<view disable-scroll="{{true}}" catchTap="changeItem" class="photo-item {{!itemInfo.selectEnabled?'noPointer':''}}" style=" transform: rotate({{itemStyle.rotate}}deg);width:{{itemStyle.width}};height:{{itemStyle.height}};left:{{itemStyle.x}};top:{{itemStyle.y}};background:{{bgColor?bgColor:''}};{{changeIdx == idx?'border :2px dashed rgb(255, 76, 136)':''}}"
a:if="{{itemInfo.visible &&imgUrl}}">
<image mode="scaleToFill" src="{{imgUrl}}" style="width:100%;height:100%" onTouchStart="handleTouchStart" onTouchMove="handleTouchMove"
onTouchEnd="handleTouchEnd" />
<view a:if="{{changeIdx == idx}}" class="control-point-left" catchTap="handledelitem">
<image style="width:100%;height:100%;" mode="scaleToFill" src="../../../../assets/imgs/del.png" />
</view>
<view a:if="{{changeIdx == idx}}" class="control-point-right" onTouchStart="handleControlPointStart" onTouchMove="handleControlPointMove"
onTouchEnd="handleControlPointEnd">
<image style="width:100%;height:100%;" mode="scaleToFill" src="../../../../assets/imgs/rotate.png" />
</view>
</view>
上面是大概的页面是我的小贴纸
Component({
mixins: [],
data: {
itemStyle: {
width: 0,
height: 0,
rotate: 0,
x: 0,
y: 0
},
media: {},
bgColor: '',
delX: 0,
delY: 0,
bigx: 0,
bigy: 0,
changeIdx: 0,
startX: 0,
startY: 0,
originalX: 0,
originalY: 0,
controlPoint: {
startX: 0, // 控制点触摸起始X坐标
startY: 0, // 控制点触摸起始Y坐标
startWidth: 0, // 图片初始宽度
startHeight: 0, // 图片初始高度
startRotate: 0, // 图片初始旋转角度
startCenterX: 0, // 图片中心点X坐标
startCenterY: 0, // 图片中心点Y坐标
startScaleFactor: 1
},
currentX: 0,
currentY: 0,
},
props: {
itemInfo: Object,
changeIdx: Number, // 当前选中的图层索引
idx: Number, // 当前组件索引
cachedOffsetLeft: Number,
cachedOffsetTop: Number,
onSaveInfo: () => Void,
onDelItem: () => Void,
},
didMount() {
this.initUi();
// this.cacheElementPosition();
},
didUpdate() {
if (this.props.itemInfo && JSON.stringify(this.props.itemInfo) !== JSON.stringify(this.data.itemInfo)) {
this.setData({
itemInfo: this.props.itemInfo
});
this.initUi();
}
if (this.props.changeIdx !== this.data.changeIdx) {
this.setData({
itemInfo: this.props.itemInfo,
changeIdx: this.props.changeIdx,
bigx: this.data.itemInfo.location.x,
bigy: this.data.itemInfo.location.y,
});
}
},
didUnmount() { },
methods: {
initUi() {
let { itemInfo } = this.props;
let { media, location, visible } = itemInfo;
let { imgPath } = media
let { width, height, x, y, rotation } = location;
let imgUrl = '';
if (visible && imgPath) {
if (imgPath.startsWith("MXYZ-")) {
imgUrl = 'https://ddiy.36588.com.cn/image/server/mongo_yx/' + imgPath;
} else {
imgUrl = 'https://ddiy.36588.com.cn/image/server/fs1020/' + imgPath;
}
}
this.setData({
itemStyle: {
width: width,
height: height,
x: x,
y: y,
rotate: rotation
},
imgUrl,
bigx: x,
bigy: y,
})
},
changeItem() {
this.props.onChangeIdx(this.props.idx)
this.setData({
bigx: this.data.itemInfo.location.x,
bigy: this.data.itemInfo.location.y,
});
},
handleTouchStart(e) {
if (this.props.idx !== this.props.changeIdx) return;
// Record the initial touch position and current item position
this.setData({
startX: e.touches[0].clientX,
startY: e.touches[0].clientY,
originalX: this.data.itemStyle.x,
originalY: this.data.itemStyle.y
});
},
handleTouchMove(e) {
if (this.props.idx !== this.props.changeIdx) return;
// Calculate the distance moved
const deltaX = e.touches[0].clientX - this.data.startX;
const deltaY = e.touches[0].clientY - this.data.startY;
// Apply the movement with scale consideration
const scaleFactor = 1 + (this.props.demoStyle.scale || 0);
const newX = this.data.originalX + deltaX * scaleFactor;
const newY = this.data.originalY + deltaY * scaleFactor;
this.setData({
'itemStyle.x': newX,
'itemStyle.y': newY,
bigx: newX,
bigy: newY
});
},
handleTouchEnd(e) {
if (this.props.idx !== this.props.changeIdx) return;
// Save the final position
const info = JSON.parse(JSON.stringify(this.data.itemInfo));
info.location.x = this.data.itemStyle.x;
info.location.y = this.data.itemStyle.y;
info.location.width = this.data.itemStyle.width;
info.location.height = this.data.itemStyle.height;
info.printLocation.x = this.data.itemStyle.x;
info.printLocation.y = this.data.itemStyle.y;
info.printLocation.width = this.data.itemStyle.width;
info.printLocation.height = this.data.itemStyle.height;
this.props.onSaveInfo(info);
},
// 右下角控制点位aa
handleControlPointStart(e) {
const touch = e.touches[0];
const { x, y, width, height, rotate } = this.data.itemStyle;
const parentScale = this.props.demoStyle.scale || 1; // 获取父元素缩放比例,默认为1
// 计算图片中心点坐标(考虑父元素缩放)
const centerX = x + width / 2;
const centerY = y + height / 2;
// 使用缓存的偏移量
const offsetLeft = this.props.cachedOffsetLeft;
const offsetTop = this.props.cachedOffsetTop;
// 计算触摸点相对于中心点的位置(考虑父元素缩放)
const touchX = (touch.clientX - offsetLeft) / parentScale;
const touchY = (touch.clientY - offsetTop) / parentScale;
this.setData({
controlPoint: {
startX: touchX,
startY: touchY,
startWidth: width,
startHeight: height,
startRotate: rotate,
startCenterX: centerX,
startCenterY: centerY,
parentScale: parentScale,
startDistance: Math.sqrt(
Math.pow(touchX - centerX, 2) +
Math.pow(touchY - centerY, 2)
)
}
});
},
// 处理旋转和缩放
handleControlPointMove(e) {
if (this.props.idx !== this.props.changeIdx) return;
const touch = e.touches[0];
const { controlPoint, itemStyle } = this.data;
const parentScale = controlPoint.parentScale || 1;
// 计算当前触摸点位置(考虑父元素缩放和页面滚动)
const currentX = (touch.clientX - this.props.cachedOffsetLeft) / parentScale;
const currentY = (touch.clientY - this.props.cachedOffsetTop) / parentScale;
// 计算旋转角度
const deltaX = currentX - controlPoint.startCenterX;
const deltaY = currentY - controlPoint.startCenterY;
const startDeltaX = controlPoint.startX - controlPoint.startCenterX;
const startDeltaY = controlPoint.startY - controlPoint.startCenterY;
// 计算当前角度和初始角度
const currentAngle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
const startAngle = Math.atan2(startDeltaY, startDeltaX) * 180 / Math.PI;
// 计算角度变化(保持在0-360度范围内)
let angleDelta = currentAngle - startAngle;
angleDelta = ((angleDelta + 180) % 360) - 180; // 限制在-180到180度之间
// 计算新旋转角度(考虑旋转限制)
let newRotate = controlPoint.startRotate + angleDelta;
// 限制旋转角度在0-360度之间
newRotate = (newRotate % 360 + 360) % 360;
// 计算缩放比例
const currentDistance = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
const scale = currentDistance / controlPoint.startDistance;
// 限制最小和最大缩放比例
const minScale = 0.1;
const maxScale = 5;
const effectiveScale = Math.min(maxScale, Math.max(minScale, scale));
// 计算新尺寸
const newWidth = controlPoint.startWidth * effectiveScale;
const newHeight = controlPoint.startHeight * effectiveScale;
// 更新图片数据(保持中心点不变)
const newX = controlPoint.startCenterX - newWidth / 2;
const newY = controlPoint.startCenterY - newHeight / 2;
this.setData({
itemStyle: {
...itemStyle,
width: newWidth,
height: newHeight,
rotate: newRotate,
x: newX,
y: newY
}
});
},
handleControlPointEnd(e) {
if (this.props.idx !== this.props.changeIdx) return;
// 保存最终状态(这部分不变)
const info = JSON.parse(JSON.stringify(this.data.itemInfo));
info.location.x = this.data.itemStyle.x;
info.location.y = this.data.itemStyle.y;
info.location.width = this.data.itemStyle.width;
info.location.height = this.data.itemStyle.height;
info.location.rotation = this.data.itemStyle.rotate;
info.printLocation.x = this.data.itemStyle.x;
info.printLocation.y = this.data.itemStyle.y;
info.printLocation.width = this.data.itemStyle.width;
info.printLocation.height = this.data.itemStyle.height;
info.printLocation.rotation = this.data.itemStyle.rotate;
this.props.onSaveInfo(info);
},
handledelitem() {
// this.props.onChangeIdx(this.props.idx)
this.props.onDelItem(this.props.idx)
}
},
});
这是js 代码,自己看吧哈哈哈,重点是 :
// 计算当前触摸点位置(考虑父元素缩放和页面滚动)
const currentX = (touch.clientX - this.props.cachedOffsetLeft) / parentScale;
const currentY = (touch.clientY - this.props.cachedOffsetTop) / parentScale;
这里面的 this.props.cachedOffsetLeft this.props.cachedOffsetTop 是获取你的画布的位置,下面是代码
my.createSelectorQuery()
.select('.templateDemo')
.boundingClientRect()
.exec((ret) => {
if (ret && ret[0]) {
this.setData({
cachedOffsetLeft: ret[0].left,
cachedOffsetTop: ret[0].top
});
}
});
嘻嘻嘻,开心!!!