From 3c45ef78ac2b8633630ca1b8882ddcd8a71af63f Mon Sep 17 00:00:00 2001 From: zhangyibo95 <690096405@qq.com> Date: Tue, 4 Aug 2026 16:18:30 +0800 Subject: [PATCH] =?UTF-8?q?update:=20=E4=BF=AE=E5=A4=8D=E9=A3=9E=E8=A1=8C?= =?UTF-8?q?=E7=89=B9=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BowShotEffect.vue | 99 +++++++++++-- src/components/BowTarget.vue | 140 ++++++++++++++---- src/pages/melee-battle.vue | 2 +- src/pages/practise-one.vue | 1 + .../team-battle/components/BowTarget.vue | 138 +++++++++++++---- src/pages/team-battle/index.vue | 1 + src/pages/training/components/BowTarget.vue | 128 ++++++++++++---- src/pages/training/practise-one.vue | 1 + 8 files changed, 406 insertions(+), 104 deletions(-) diff --git a/src/components/BowShotEffect.vue b/src/components/BowShotEffect.vue index 058f5d5..171c9a3 100644 --- a/src/components/BowShotEffect.vue +++ b/src/components/BowShotEffect.vue @@ -34,6 +34,10 @@ const props = defineProps({ type: Number, default: 0, }, + viewportMode: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(["complete", "impact"]); @@ -43,6 +47,7 @@ const activePlayKey = ref(""); const animationKey = ref(""); const impactEmitted = ref(false); const activeShot = ref(null); +const activeLayout = ref(null); let timers = []; const isActive = computed(() => phase.value !== "idle"); @@ -93,6 +98,17 @@ function hasShotPoint(shot) { } const effectiveShot = computed(() => activeShot.value || props.shot); +const effectiveTargetSize = computed( + () => activeLayout.value?.target || safeTargetSize.value +); +const effectiveWindowSize = computed( + () => activeLayout.value?.window || getWindowSize() +); +const isViewportMode = computed(() => + activeLayout.value + ? activeLayout.value.viewportMode + : props.viewportMode === true +); const shotPoint = computed(() => { const x = Number(effectiveShot.value?.x); @@ -137,8 +153,40 @@ const hitPercent = computed(() => { }); const flightPath = computed(() => { - const size = safeTargetSize.value; - const windowSize = getWindowSize(); + const size = effectiveTargetSize.value; + const windowSize = effectiveWindowSize.value; + + if ( + isViewportMode.value && + size.width > 0 && + size.height > 0 && + windowSize.width > 0 && + windowSize.height > 0 + ) { + const startX = windowSize.width * 0.5; + const endX = + size.left + + size.width * (hitPercent.value.left / 100) + + hitOffset.value.x; + const endY = + size.top + + size.height * (hitPercent.value.top / 100) + + hitOffset.value.y; + // 常规情况下从视口底部进入;靶面局部超出视口时仍保证起点在命中点下方。 + const startY = Math.max(windowSize.height + 16, endY + 80); + const dx = endX - startX; + const dy = endY - startY; + + return { + startX, + startY, + endX, + endY, + translateX: dx, + translateY: dy, + angle: Math.atan2(dx, -dy) * (180 / Math.PI), + }; + } const hasScreenCoordinates = size.width > 0 && @@ -161,6 +209,8 @@ const flightPath = computed(() => { return { startX, startY, + endX, + endY, translateX: dx, translateY: dy, angle: Math.atan2(dx, -dy) * (180 / Math.PI), @@ -178,10 +228,19 @@ function formatTargetPosition(percent, offset) { return pxOffset ? `calc(${percent}%${pxOffset})` : `${percent}%`; } -const crackStyle = computed(() => ({ - left: formatTargetPosition(hitPercent.value.left, hitOffset.value.x), - top: formatTargetPosition(hitPercent.value.top, hitOffset.value.y), -})); +const crackStyle = computed(() => { + if (isViewportMode.value) { + return { + left: `${flightPath.value.endX}px`, + top: `${flightPath.value.endY}px`, + }; + } + + return { + left: formatTargetPosition(hitPercent.value.left, hitOffset.value.x), + top: formatTargetPosition(hitPercent.value.top, hitOffset.value.y), + }; +}); function getTargetTranslate(percent) { const absPercent = Math.abs(percent); @@ -190,19 +249,21 @@ function getTargetTranslate(percent) { } const arrowMoveStyle = computed(() => { - const size = safeTargetSize.value; + const size = effectiveTargetSize.value; const path = flightPath.value; let x = getTargetTranslate(hitPercent.value.left - 50); let y = getTargetTranslate(hitPercent.value.top - 114); - if (size.width && size.height) { + if (isViewportMode.value || (size.width && size.height)) { x = `${path.translateX}px`; y = `${path.translateY}px`; } return { - "--shot-start-x": size.width ? `${path.startX}px` : "50%", - "--shot-start-y": size.height ? `${path.startY}px` : "114%", + "--shot-start-x": + isViewportMode.value || size.width ? `${path.startX}px` : "50%", + "--shot-start-y": + isViewportMode.value || size.height ? `${path.startY}px` : "114%", "--shot-tx": x, "--shot-ty": y, "--shot-angle": `${path.angle}deg`, @@ -231,6 +292,7 @@ function finish(playKey) { phase.value = "idle"; activePlayKey.value = ""; activeShot.value = null; + activeLayout.value = null; emit("complete", playKey); } @@ -244,6 +306,11 @@ function play() { animationKey.value = `${props.playKey}`; impactEmitted.value = false; activeShot.value = { ...props.shot }; + activeLayout.value = { + target: { ...safeTargetSize.value }, + window: getWindowSize(), + viewportMode: props.viewportMode === true, + }; phase.value = "playing"; queueTimer(() => { @@ -278,7 +345,11 @@ onBeforeUnmount(() => {