update:修复靶纸贴边问题
This commit is contained in:
@@ -34,6 +34,18 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
targetRadius: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
hitRadiusPx: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
zoomHitRadiusPx: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
});
|
||||
|
||||
const pMode = ref(true);
|
||||
@@ -80,13 +92,79 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
function calcRealX(num, offset = 3.4) {
|
||||
const len = 20.4 + num;
|
||||
return `calc(${(len / 40.8) * 100 - offset / 2}%)`;
|
||||
const safeTargetRadius = computed(() => {
|
||||
const radius = Number(props.targetRadius);
|
||||
return Number.isFinite(radius) && radius > 0 ? radius : 20;
|
||||
});
|
||||
|
||||
const currentHitRadiusPx = computed(() => {
|
||||
const radius = Number(
|
||||
pMode.value ? props.zoomHitRadiusPx : props.hitRadiusPx
|
||||
);
|
||||
return Number.isFinite(radius) && radius >= 0 ? radius : 0;
|
||||
});
|
||||
|
||||
function getShotPoint(shot, fallbackCenter = false) {
|
||||
const x = Number(shot?.x);
|
||||
const y = Number(shot?.y);
|
||||
if (Number.isFinite(x) && Number.isFinite(y)) return { x, y };
|
||||
return fallbackCenter ? { x: 0, y: 0 } : null;
|
||||
}
|
||||
function calcRealY(num, offset = 3.4) {
|
||||
const len = num < 0 ? Math.abs(num) + 20.4 : 20.4 - num;
|
||||
return `calc(${(len / 40.8) * 100 - offset / 2}%)`;
|
||||
|
||||
function getPointDirection(point) {
|
||||
if (!point) return null;
|
||||
const distance = Math.sqrt(point.x * point.x + point.y * point.y);
|
||||
if (distance === 0) return null;
|
||||
|
||||
return {
|
||||
x: point.x / distance,
|
||||
y: point.y / distance,
|
||||
};
|
||||
}
|
||||
|
||||
function formatPxOffset(value) {
|
||||
if (!value) return "";
|
||||
const operator = value > 0 ? "+" : "-";
|
||||
return ` ${operator} ${Math.abs(value)}px`;
|
||||
}
|
||||
|
||||
function formatTargetPosition(percent, offset) {
|
||||
const pxOffset = formatPxOffset(offset);
|
||||
return pxOffset ? `calc(${percent}%${pxOffset})` : `${percent}%`;
|
||||
}
|
||||
|
||||
function getTargetPositionStyle(point, offsetPx = 0) {
|
||||
if (!point) return { display: "none" };
|
||||
|
||||
const radius = safeTargetRadius.value;
|
||||
const diameter = radius * 2;
|
||||
const direction = getPointDirection(point);
|
||||
const xOffset = direction ? direction.x * offsetPx : 0;
|
||||
const yOffset = direction ? -direction.y * offsetPx : 0;
|
||||
const leftPercent = ((point.x + radius) / diameter) * 100;
|
||||
const topPercent = ((radius - point.y) / diameter) * 100;
|
||||
|
||||
return {
|
||||
left: formatTargetPosition(leftPercent, xOffset),
|
||||
top: formatTargetPosition(topPercent, yOffset),
|
||||
transform: "translate(-50%, -50%)",
|
||||
};
|
||||
}
|
||||
|
||||
function getHitStyle(shot) {
|
||||
const radius = currentHitRadiusPx.value;
|
||||
const point = getShotPoint(shot);
|
||||
|
||||
return {
|
||||
...getTargetPositionStyle(point, radius),
|
||||
width: `${radius * 2}px`,
|
||||
height: `${radius * 2}px`,
|
||||
};
|
||||
}
|
||||
|
||||
function getTipStyle(shot) {
|
||||
const point = getShotPoint(shot, true);
|
||||
return getTargetPositionStyle(point, shot?.ring ? currentHitRadiusPx.value : 0);
|
||||
}
|
||||
const simulShoot = async () => {
|
||||
if (device.value.deviceId) await simulShootAPI(device.value.deviceId);
|
||||
@@ -169,20 +247,14 @@ onBeforeUnmount(() => {
|
||||
<view
|
||||
v-if="latestOne && latestOne.ring && user.id === latestOne.playerId"
|
||||
class="e-value fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(latestOne.ring ? latestOne.x : 0, 20),
|
||||
top: calcRealY(latestOne.ring ? latestOne.y : 0, 40),
|
||||
}"
|
||||
:style="getTipStyle(latestOne)"
|
||||
>
|
||||
经验 +1
|
||||
</view>
|
||||
<view
|
||||
v-if="latestOne"
|
||||
class="round-tip fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(latestOne.ring ? latestOne.x : 0, 28),
|
||||
top: calcRealY(latestOne.ring ? latestOne.y : 0, 28),
|
||||
}"
|
||||
:style="getTipStyle(latestOne)"
|
||||
>{{ latestOne.ringX ? "X" : latestOne.ring || "未上靶"
|
||||
}}<text v-if="latestOne.ring">环</text>
|
||||
</view>
|
||||
@@ -193,20 +265,14 @@ onBeforeUnmount(() => {
|
||||
user.id === bluelatestOne.playerId
|
||||
"
|
||||
class="e-value fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(bluelatestOne.ring ? bluelatestOne.x : 0, 20),
|
||||
top: calcRealY(bluelatestOne.ring ? bluelatestOne.y : 0, 40),
|
||||
}"
|
||||
:style="getTipStyle(bluelatestOne)"
|
||||
>
|
||||
经验 +1
|
||||
</view>
|
||||
<view
|
||||
v-if="bluelatestOne"
|
||||
class="round-tip fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(bluelatestOne.ring ? bluelatestOne.x : 0, 28),
|
||||
top: calcRealY(bluelatestOne.ring ? bluelatestOne.y : 0, 28),
|
||||
}"
|
||||
:style="getTipStyle(bluelatestOne)"
|
||||
>{{ bluelatestOne.ringX ? "X" : bluelatestOne.ring || "未上靶"
|
||||
}}<text v-if="bluelatestOne.ring">环</text></view
|
||||
>
|
||||
@@ -217,8 +283,7 @@ onBeforeUnmount(() => {
|
||||
index === scores.length - 1 && latestOne ? 'pump-in' : ''
|
||||
}`"
|
||||
:style="{
|
||||
left: calcRealX(bow.x, pMode ? '3.4' : '2'),
|
||||
top: calcRealY(bow.y, pMode ? '3.4' : '2'),
|
||||
...getHitStyle(bow),
|
||||
backgroundColor: mode === 'solo' ? '#00bf04' : '#FF0000',
|
||||
}"
|
||||
><text v-if="pMode">{{ index + 1 }}</text></view
|
||||
@@ -231,8 +296,7 @@ onBeforeUnmount(() => {
|
||||
index === blueScores.length - 1 && bluelatestOne ? 'pump-in' : ''
|
||||
}`"
|
||||
:style="{
|
||||
left: calcRealX(bow.x, pMode ? '3.4' : '2'),
|
||||
top: calcRealY(bow.y, pMode ? '3.4' : '2'),
|
||||
...getHitStyle(bow),
|
||||
backgroundColor: '#1840FF',
|
||||
}"
|
||||
>
|
||||
@@ -302,21 +366,11 @@ onBeforeUnmount(() => {
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.s-point {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
min-width: 4px;
|
||||
min-height: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.b-point {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
min-height: 10px;
|
||||
border: 1px solid #fff;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -332,6 +386,19 @@ onBeforeUnmount(() => {
|
||||
transform: translate(-50%, -50%);*/
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
@keyframes target-pump-in {
|
||||
from {
|
||||
transform: translate(-50%, -50%) scale(2);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
.hit.pump-in {
|
||||
animation: target-pump-in 0.3s ease-out forwards;
|
||||
transform-origin: center center;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -38,6 +38,18 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
targetRadius: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
hitRadiusPx: {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
zoomHitRadiusPx: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
});
|
||||
|
||||
const pMode = ref(true);
|
||||
@@ -75,13 +87,79 @@ watch(
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function calcRealX(num, offset = 3.4) {
|
||||
const len = 20.4 + num;
|
||||
return `calc(${(len / 40.8) * 100 - offset / 2}%)`;
|
||||
const safeTargetRadius = computed(() => {
|
||||
const radius = Number(props.targetRadius);
|
||||
return Number.isFinite(radius) && radius > 0 ? radius : 20;
|
||||
});
|
||||
|
||||
const currentHitRadiusPx = computed(() => {
|
||||
const radius = Number(
|
||||
pMode.value ? props.zoomHitRadiusPx : props.hitRadiusPx
|
||||
);
|
||||
return Number.isFinite(radius) && radius >= 0 ? radius : 0;
|
||||
});
|
||||
|
||||
function getShotPoint(shot, fallbackCenter = false) {
|
||||
const x = Number(shot?.x);
|
||||
const y = Number(shot?.y);
|
||||
if (Number.isFinite(x) && Number.isFinite(y)) return { x, y };
|
||||
return fallbackCenter ? { x: 0, y: 0 } : null;
|
||||
}
|
||||
function calcRealY(num, offset = 3.4) {
|
||||
const len = num < 0 ? Math.abs(num) + 20.4 : 20.4 - num;
|
||||
return `calc(${(len / 40.8) * 100 - offset / 2}%)`;
|
||||
|
||||
function getPointDirection(point) {
|
||||
if (!point) return null;
|
||||
const distance = Math.sqrt(point.x * point.x + point.y * point.y);
|
||||
if (distance === 0) return null;
|
||||
|
||||
return {
|
||||
x: point.x / distance,
|
||||
y: point.y / distance,
|
||||
};
|
||||
}
|
||||
|
||||
function formatPxOffset(value) {
|
||||
if (!value) return "";
|
||||
const operator = value > 0 ? "+" : "-";
|
||||
return ` ${operator} ${Math.abs(value)}px`;
|
||||
}
|
||||
|
||||
function formatTargetPosition(percent, offset) {
|
||||
const pxOffset = formatPxOffset(offset);
|
||||
return pxOffset ? `calc(${percent}%${pxOffset})` : `${percent}%`;
|
||||
}
|
||||
|
||||
function getTargetPositionStyle(point, offsetPx = 0) {
|
||||
if (!point) return { display: "none" };
|
||||
|
||||
const radius = safeTargetRadius.value;
|
||||
const diameter = radius * 2;
|
||||
const direction = getPointDirection(point);
|
||||
const xOffset = direction ? direction.x * offsetPx : 0;
|
||||
const yOffset = direction ? -direction.y * offsetPx : 0;
|
||||
const leftPercent = ((point.x + radius) / diameter) * 100;
|
||||
const topPercent = ((radius - point.y) / diameter) * 100;
|
||||
|
||||
return {
|
||||
left: formatTargetPosition(leftPercent, xOffset),
|
||||
top: formatTargetPosition(topPercent, yOffset),
|
||||
transform: "translate(-50%, -50%)",
|
||||
};
|
||||
}
|
||||
|
||||
function getHitStyle(shot) {
|
||||
const radius = currentHitRadiusPx.value;
|
||||
const point = getShotPoint(shot);
|
||||
|
||||
return {
|
||||
...getTargetPositionStyle(point, radius),
|
||||
width: `${radius * 2}px`,
|
||||
height: `${radius * 2}px`,
|
||||
};
|
||||
}
|
||||
|
||||
function getTipStyle(shot) {
|
||||
const point = getShotPoint(shot, true);
|
||||
return getTargetPositionStyle(point, shot?.ring ? currentHitRadiusPx.value : 0);
|
||||
}
|
||||
const simulShoot = async () => {
|
||||
if (device.value.deviceId) await simulShootAPI(device.value.deviceId);
|
||||
@@ -164,20 +242,14 @@ onBeforeUnmount(() => {
|
||||
<view
|
||||
v-if="latestOne && latestOne.ring && user.id === latestOne.playerId"
|
||||
class="e-value fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(latestOne.ring ? latestOne.x : 0, 20),
|
||||
top: calcRealY(latestOne.ring ? latestOne.y : 0, 40),
|
||||
}"
|
||||
:style="getTipStyle(latestOne)"
|
||||
>
|
||||
经验 +1
|
||||
</view>
|
||||
<view
|
||||
v-if="latestOne"
|
||||
class="round-tip fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(latestOne.ring ? latestOne.x : 0, 28),
|
||||
top: calcRealY(latestOne.ring ? latestOne.y : 0, 28),
|
||||
}"
|
||||
:style="getTipStyle(latestOne)"
|
||||
>{{ latestOne.ringX ? "X" : latestOne.ring || "未上靶"
|
||||
}}<text v-if="latestOne.ring">环</text>
|
||||
</view>
|
||||
@@ -188,20 +260,14 @@ onBeforeUnmount(() => {
|
||||
user.id === bluelatestOne.playerId
|
||||
"
|
||||
class="e-value fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(bluelatestOne.ring ? bluelatestOne.x : 0, 20),
|
||||
top: calcRealY(bluelatestOne.ring ? bluelatestOne.y : 0, 40),
|
||||
}"
|
||||
:style="getTipStyle(bluelatestOne)"
|
||||
>
|
||||
经验 +1
|
||||
</view>
|
||||
<view
|
||||
v-if="bluelatestOne"
|
||||
class="round-tip fade-in-out"
|
||||
:style="{
|
||||
left: calcRealX(bluelatestOne.ring ? bluelatestOne.x : 0, 28),
|
||||
top: calcRealY(bluelatestOne.ring ? bluelatestOne.y : 0, 28),
|
||||
}"
|
||||
:style="getTipStyle(bluelatestOne)"
|
||||
>{{ bluelatestOne.ringX ? "X" : bluelatestOne.ring || "未上靶"
|
||||
}}<text v-if="bluelatestOne.ring">环</text></view
|
||||
>
|
||||
@@ -212,8 +278,7 @@ onBeforeUnmount(() => {
|
||||
index === scores.length - 1 && latestOne ? 'pump-in' : ''
|
||||
}`"
|
||||
:style="{
|
||||
left: calcRealX(bow.x, pMode ? '3.4' : '2'),
|
||||
top: calcRealY(bow.y, pMode ? '3.4' : '2'),
|
||||
...getHitStyle(bow),
|
||||
backgroundColor: mode === 'solo' ? '#00bf04' : '#FF0000',
|
||||
}"
|
||||
><text v-if="pMode">{{ index + 1 }}</text></view
|
||||
@@ -226,8 +291,7 @@ onBeforeUnmount(() => {
|
||||
index === blueScores.length - 1 && bluelatestOne ? 'pump-in' : ''
|
||||
}`"
|
||||
:style="{
|
||||
left: calcRealX(bow.x, pMode ? '3.4' : '2'),
|
||||
top: calcRealY(bow.y, pMode ? '3.4' : '2'),
|
||||
...getHitStyle(bow),
|
||||
backgroundColor: '#1840FF',
|
||||
}"
|
||||
>
|
||||
@@ -297,21 +361,11 @@ onBeforeUnmount(() => {
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.s-point {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
min-width: 4px;
|
||||
min-height: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.b-point {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
min-height: 10px;
|
||||
border: 1px solid #fff;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -327,6 +381,19 @@ onBeforeUnmount(() => {
|
||||
transform: translate(-50%, -50%);*/
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
@keyframes target-pump-in {
|
||||
from {
|
||||
transform: translate(-50%, -50%) scale(2);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
.hit.pump-in {
|
||||
animation: target-pump-in 0.3s ease-out forwards;
|
||||
transform-origin: center center;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user