完成靶子放大编辑

This commit is contained in:
kron
2025-08-09 12:19:39 +08:00
parent e164d4736e
commit 1f79280301
2 changed files with 135 additions and 76 deletions

View File

@@ -26,19 +26,40 @@ const arrow = ref(null);
const isDragging = ref(false); const isDragging = ref(false);
const dragStartPos = ref({ x: 0, y: 0 }); const dragStartPos = ref({ x: 0, y: 0 });
const capsuleHeight = ref(0); const capsuleHeight = ref(0);
const scale = ref(1);
const zoomPos = ref({ x: 0, y: 0 });
const targetPos = ref({ x: 0, y: 0 });
let lastMoveTime = 0;
// 点击靶纸创建新的点 // 点击靶纸创建新的点
const onClick = async (e) => { const onClick = async (e) => {
if (arrow.value !== null || !props.onChange) return; if (
arrow.value !== null ||
!props.onChange ||
Date.now() - lastMoveTime < 300
) {
return;
}
const newArrow = { const newArrow = {
x: e.detail.x, x: e.detail.x - zoomPos.value.x - 6 / scale.value,
y: e.detail.y - rect.value.top - capsuleHeight.value, y:
e.detail.y -
rect.value.top -
capsuleHeight.value -
zoomPos.value.y -
6 / scale.value,
};
targetPos.value = {
x: zoomPos.value.x,
y: zoomPos.value.y,
}; };
const side = rect.value.width; const side = rect.value.width;
newArrow.ring = calcRing( newArrow.ring = calcRing(
props.id, props.id,
newArrow.x - rect.value.width * 0.05, newArrow.x / scale.value - rect.value.width * 0.05,
newArrow.y - 10, newArrow.y / scale.value - rect.value.width * 0.05,
rect.value.width * 0.9, rect.value.width * 0.9,
rect.value.width * 0.9 rect.value.width * 0.9
); );
@@ -51,18 +72,27 @@ const onClick = async (e) => {
// 确认添加箭矢 // 确认添加箭矢
const confirmAdd = () => { const confirmAdd = () => {
if (props.onChange) if (props.onChange) {
props.onChange({ ...arrow.value, ring: arrow.value.ring || "M" }); props.onChange({
x: arrow.value.x / scale.value,
y: arrow.value.y / scale.value,
ring: arrow.value.ring || "M",
});
}
arrow.value = null; arrow.value = null;
}; };
// 删除箭矢 // 删除箭矢
const deleteArrow = () => { const deleteArrow = () => {
arrow.value = null; arrow.value = null;
targetPos.value = {
x: zoomPos.value.x,
y: zoomPos.value.y,
};
}; };
// 开始拖拽 - 同样修复坐标获取 // 开始拖拽 - 同样修复坐标获取
const startDrag = async (e, index) => { const startDrag = async (e) => {
if (!e.touches[0]) return; if (!e.touches[0]) return;
isDragging.value = true; isDragging.value = true;
dragStartPos.value = { dragStartPos.value = {
@@ -73,27 +103,34 @@ const startDrag = async (e, index) => {
// 拖拽移动 - 同样修复坐标获取 // 拖拽移动 - 同样修复坐标获取
const onDrag = async (e) => { const onDrag = async (e) => {
lastMoveTime = Date.now();
if (!isDragging.value || !e.touches[0] || !arrow.value) return; if (!isDragging.value || !e.touches[0] || !arrow.value) return;
let clientX = e.touches[0].clientX; let clientX = e.touches[0].clientX;
let clientY = e.touches[0].clientY; let clientY = e.touches[0].clientY;
// 计算移动距离 // 计算移动距离
const deltaX = clientX - dragStartPos.value.x; const deltaX = clientX - dragStartPos.value.x;
const deltaY = clientY - dragStartPos.value.y; const deltaY = clientY - dragStartPos.value.y;
const side = rect.value.width; const side = rect.value.width;
// 更新坐标 // 更新坐标
arrow.value.x = Math.max(0, Math.min(side, arrow.value.x * side + deltaX)); arrow.value.x = Math.max(
arrow.value.y = Math.max(0, Math.min(side, arrow.value.y * side + deltaY)); 0,
Math.min(side * scale.value, arrow.value.x * side + deltaX)
);
arrow.value.y = Math.max(
0,
Math.min(side * scale.value, arrow.value.y * side + deltaY)
);
arrow.value.ring = calcRing( arrow.value.ring = calcRing(
props.id, props.id,
arrow.value.x - rect.value.width * 0.05, arrow.value.x / scale.value - rect.value.width * 0.05,
arrow.value.y - 10, arrow.value.y / scale.value - rect.value.width * 0.05,
rect.value.width * 0.9, rect.value.width * 0.9,
rect.value.width * 0.9 rect.value.width * 0.9
); );
arrow.value.x = arrow.value.x / side; arrow.value.x = arrow.value.x / side;
arrow.value.y = arrow.value.y / side; arrow.value.y = arrow.value.y / side;
@@ -102,17 +139,24 @@ const onDrag = async (e) => {
}; };
// 结束拖拽 // 结束拖拽
const endDrag = () => { const endDrag = (e) => {
isDragging.value = false; isDragging.value = false;
}; };
const scale = ref(1);
const onScale = (e) => { const onScale = (e) => {
scale.value = e.detail.scale; // 返回 1 ~ 2 lastMoveTime = Date.now();
const lastScale = scale.value;
scale.value = e.detail.scale;
zoomPos.value = { x: e.detail.x, y: e.detail.y };
if (arrow.value) {
arrow.value.x = arrow.value.x * (scale.value / lastScale);
arrow.value.y = arrow.value.y * (scale.value / lastScale);
}
}; };
const onMove = (e) => { const onMove = (e) => {
console.log(e); if (e.detail.source) {
zoomPos.value = { x: e.detail.x, y: e.detail.y };
}
}; };
onMounted(async () => { onMounted(async () => {
@@ -126,7 +170,7 @@ onMounted(async () => {
<template> <template>
<view <view
class="container" class="container"
@click="onClick" @tap="onClick"
@touchmove="onDrag" @touchmove="onDrag"
@touchend="endDrag" @touchend="endDrag"
> >
@@ -135,6 +179,8 @@ onMounted(async () => {
class="move-view" class="move-view"
direction="all" direction="all"
scale scale
:x="targetPos.x"
:y="targetPos.y"
:scale-min="1" :scale-min="1"
:scale-max="3" :scale-max="3"
:scale-value="scale" :scale-value="scale"
@@ -144,61 +190,68 @@ onMounted(async () => {
:out-of-bounds="true" :out-of-bounds="true"
> >
<image :src="src" mode="widthFix" /> <image :src="src" mode="widthFix" />
</movable-view>
<view
v-for="(arrow, index) in arrows"
:key="index"
class="arrow-point"
:style="{
left: (arrow.x !== undefined ? arrow.x : 0) * 100 + '%',
top: (arrow.y !== undefined ? arrow.y : 0) * 100 + '%',
}"
>
<view <view
v-if="arrow.x !== undefined && arrow.y !== undefined" v-for="(arrow, index) in arrows"
class="point" :key="index"
><text>{{ index + 1 }}</text></view class="arrow-point"
:style="{
left: (arrow.x !== undefined ? arrow.x : 0) * 100 + '%',
top: (arrow.y !== undefined ? arrow.y : 0) * 100 + '%',
}"
> >
</view>
<movable-view
v-if="arrow"
class="arrow-point"
direction="all"
:animation="false"
:x="rect.width * arrow.x"
:y="rect.width * arrow.y"
>
<!-- 箭矢点 -->
<view class="point"> </view>
<!-- 编辑按钮组只在编辑状态下显示 -->
<view class="edit-buttons" @click.stop>
<view class="edit-btn-text">
<text v-if="arrow.ring === 0" :style="{ width: '100%' }"
>未上靶</text
>
<text v-if="arrow.ring !== 0">{{ arrow.ring }}</text>
<text
v-if="arrow.ring > 0"
:style="{
fontSize: '16px',
marginLeft: '2px',
}"
></text
>
</view>
<view class="edit-btn confirm-btn" @click.stop="confirmAdd(index)">
<image src="../static/arrow-edit-save.png" mode="widthFix" />
</view>
<view class="edit-btn delete-btn" @click.stop="deleteArrow(index)">
<image src="../static/arrow-edit-delete.png" mode="widthFix" />
</view>
<view <view
class="edit-btn drag-btn" v-if="arrow.x !== undefined && arrow.y !== undefined"
@touchstart.stop="startDrag($event, index)" class="point"
:style="{ transform: `scale(${1 / scale})` }"
> >
<image src="../static/arrow-edit-move.png" mode="widthFix" /> <text>{{ index + 1 }}</text>
</view> </view>
</view> </view>
<movable-view
v-if="arrow"
class="arrow-point"
direction="all"
:animation="false"
:out-of-bounds="true"
:x="arrow ? (rect.width * arrow.x) / scale : 0"
:y="arrow ? (rect.width * arrow.y) / scale : 0"
>
<view class="point" :style="{ transform: `scale(${1 / scale})` }">
</view>
<view
v-if="arrow"
class="edit-buttons"
@touchstart.stop
:style="{ transform: `scale(${1 / scale})` }"
>
<view class="edit-btn-text">
<text v-if="arrow.ring === 0" :style="{ width: '100%' }"
>未上靶</text
>
<text v-if="arrow.ring !== 0">{{ arrow.ring }}</text>
<text
v-if="arrow.ring > 0"
:style="{
fontSize: '16px',
marginLeft: '2px',
}"
></text
>
</view>
<view class="edit-btn confirm-btn" @touchstart.stop="confirmAdd">
<image src="../static/arrow-edit-save.png" mode="widthFix" />
</view>
<view class="edit-btn delete-btn" @touchstart.stop="deleteArrow">
<image src="../static/arrow-edit-delete.png" mode="widthFix" />
</view>
<view
class="edit-btn drag-btn"
@touchstart.stop="startDrag($event)"
>
<image src="../static/arrow-edit-move.png" mode="widthFix" />
</view>
</view>
</movable-view>
</movable-view> </movable-view>
</movable-area> </movable-area>
</view> </view>
@@ -209,6 +262,7 @@ onMounted(async () => {
width: 100vw; width: 100vw;
height: 100vw; height: 100vw;
overflow: hidden; overflow: hidden;
transform: translateY(-10px);
} }
.move-area { .move-area {
@@ -220,7 +274,7 @@ onMounted(async () => {
.move-view { .move-view {
width: 90vw; width: 90vw;
height: 90vw; height: 90vw;
margin: 10px 5vw; padding: 5vw;
position: relative; position: relative;
} }
@@ -230,12 +284,14 @@ onMounted(async () => {
.arrow-point { .arrow-point {
position: absolute; position: absolute;
display: flex;
justify-content: center;
align-items: center;
} }
.point { .point {
transform: translate(-50%, -50%); min-width: 12px;
width: 12px; min-height: 12px;
height: 12px;
border-radius: 50%; border-radius: 50%;
border: 1px solid #fff; border: 1px solid #fff;
color: #fff; color: #fff;
@@ -245,6 +301,7 @@ onMounted(async () => {
box-sizing: border-box; box-sizing: border-box;
background-color: #ff4444; background-color: #ff4444;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
transition: all 0.1s linear;
} }
.point > text { .point > text {
@@ -255,13 +312,14 @@ onMounted(async () => {
.edit-buttons { .edit-buttons {
position: absolute; position: absolute;
top: calc(50% - 50px); top: calc(50% - 44px);
left: calc(50% - 50px); left: calc(50% - 44px);
background: #18ff6899; background: #18ff6899;
width: 88px; width: 88px;
height: 88px; height: 88px;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
transition: all 0.1s linear;
} }
.edit-btn-text { .edit-btn-text {

View File

@@ -72,6 +72,7 @@ onMounted(async () => {
bowType.value = pointBook.bowType; bowType.value = pointBook.bowType;
distance.value = pointBook.distance; distance.value = pointBook.distance;
bowtargetType.value = pointBook.bowtargetType; bowtargetType.value = pointBook.bowtargetType;
expandIndex.value = 3;
} }
}); });
</script> </script>
@@ -183,6 +184,6 @@ onMounted(async () => {
font-size: 27px; font-size: 27px;
font-weight: 500; font-weight: 500;
margin-right: 5px; margin-right: 5px;
color: #FFF4C9; color: #fff4c9;
} }
</style> </style>