update: 修复飞行特效
This commit is contained in:
@@ -84,6 +84,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
stableShotEffect: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["shot-effect-complete"]);
|
||||
@@ -98,11 +102,13 @@ const dirTimer = ref(null);
|
||||
const angle = ref(null);
|
||||
const circleColor = ref("");
|
||||
const shotEffect = ref(null);
|
||||
const pendingShotEffect = ref(null);
|
||||
const hiddenLatestKey = ref("");
|
||||
const targetShaking = ref(false);
|
||||
const targetRect = ref({ left: 0, top: 0, width: 0, height: 0 });
|
||||
const shakeTimer = ref(null);
|
||||
const instance = getCurrentInstance();
|
||||
let shotEffectRequestGeneration = 0;
|
||||
const ROUND_TIP_OFFSET_Y = -32;
|
||||
const EXPERIENCE_TIP_OFFSET_Y = -68;
|
||||
|
||||
@@ -245,7 +251,7 @@ function buildShotEffectKey(shot, index) {
|
||||
].join("-");
|
||||
}
|
||||
|
||||
function triggerShotEffect(shot, index) {
|
||||
function triggerShotEffect(shot, index, viewportMode = false) {
|
||||
const key = buildShotEffectKey(shot, index);
|
||||
clearTipTimer();
|
||||
latestOne.value = null;
|
||||
@@ -254,9 +260,31 @@ function triggerShotEffect(shot, index) {
|
||||
key,
|
||||
shot,
|
||||
token: props.shotEffectToken,
|
||||
viewportMode,
|
||||
};
|
||||
}
|
||||
|
||||
async function prepareShotEffect(shot, index) {
|
||||
const requestGeneration = ++shotEffectRequestGeneration;
|
||||
const key = buildShotEffectKey(shot, index);
|
||||
pendingShotEffect.value = { generation: requestGeneration, key };
|
||||
clearTipTimer();
|
||||
latestOne.value = null;
|
||||
|
||||
const viewportMode = props.stableShotEffect
|
||||
? await updateTargetRect()
|
||||
: false;
|
||||
|
||||
if (requestGeneration !== shotEffectRequestGeneration) {
|
||||
if (pendingShotEffect.value?.generation === requestGeneration) {
|
||||
pendingShotEffect.value = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pendingShotEffect.value = null;
|
||||
triggerShotEffect(shot, index, viewportMode);
|
||||
}
|
||||
|
||||
function completeShotEffect(key) {
|
||||
if (!shotEffect.value || shotEffect.value.key !== key) return;
|
||||
|
||||
@@ -272,7 +300,10 @@ function completeShotEffect(key) {
|
||||
}
|
||||
|
||||
function shouldHideLatestHit(index) {
|
||||
return !!hiddenLatestKey.value && index === props.scores.length - 1;
|
||||
return (
|
||||
(!!hiddenLatestKey.value || !!pendingShotEffect.value) &&
|
||||
index === props.scores.length - 1
|
||||
);
|
||||
}
|
||||
|
||||
function shakeTarget() {
|
||||
@@ -291,36 +322,53 @@ function shakeTarget() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateTargetRect() {
|
||||
nextTick(() => {
|
||||
const query = instance?.proxy
|
||||
? uni.createSelectorQuery().in(instance.proxy)
|
||||
: uni.createSelectorQuery();
|
||||
function hasValidTargetRect(rect = targetRect.value) {
|
||||
return (
|
||||
Number.isFinite(Number(rect?.left)) &&
|
||||
Number.isFinite(Number(rect?.top)) &&
|
||||
Number(rect?.width) > 0 &&
|
||||
Number(rect?.height) > 0
|
||||
);
|
||||
}
|
||||
|
||||
query
|
||||
.select(".target")
|
||||
.boundingClientRect((rect) => {
|
||||
const left = Number(rect?.left);
|
||||
const top = Number(rect?.top);
|
||||
const width = Number(rect?.width);
|
||||
const height = Number(rect?.height);
|
||||
if (
|
||||
!Number.isFinite(left) ||
|
||||
!Number.isFinite(top) ||
|
||||
!Number.isFinite(width) ||
|
||||
!Number.isFinite(height)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (width <= 0 || height <= 0) return;
|
||||
targetRect.value = { left, top, width, height };
|
||||
})
|
||||
.exec();
|
||||
async function updateTargetRect() {
|
||||
await nextTick();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let settled = false;
|
||||
const finish = (rect) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
|
||||
const isValid = hasValidTargetRect(rect);
|
||||
if (isValid) {
|
||||
targetRect.value = {
|
||||
left: Number(rect.left),
|
||||
top: Number(rect.top),
|
||||
width: Number(rect.width),
|
||||
height: Number(rect.height),
|
||||
};
|
||||
}
|
||||
resolve(isValid);
|
||||
};
|
||||
|
||||
try {
|
||||
const query = instance?.proxy
|
||||
? uni.createSelectorQuery().in(instance.proxy)
|
||||
: uni.createSelectorQuery();
|
||||
|
||||
query
|
||||
.select(".target")
|
||||
.boundingClientRect()
|
||||
.exec((result) => finish(Array.isArray(result) ? result[0] : null));
|
||||
} catch {
|
||||
finish(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleWindowResize() {
|
||||
updateTargetRect();
|
||||
void updateTargetRect();
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -329,6 +377,8 @@ watch(
|
||||
if (newVal.length - prevScores.value.length === 1) {
|
||||
showShotTip(newVal[newVal.length - 1]);
|
||||
} else if (newVal.length < prevScores.value.length) {
|
||||
shotEffectRequestGeneration += 1;
|
||||
pendingShotEffect.value = null;
|
||||
clearTipTimer();
|
||||
latestOne.value = null;
|
||||
hiddenLatestKey.value = "";
|
||||
@@ -349,7 +399,10 @@ watch(
|
||||
const latestIndex = props.scores.length - 1;
|
||||
const latestShot = props.scores[latestIndex];
|
||||
if (shouldPlayShotEffect(latestShot)) {
|
||||
triggerShotEffect(latestShot, latestIndex);
|
||||
void prepareShotEffect(latestShot, latestIndex);
|
||||
} else {
|
||||
shotEffectRequestGeneration += 1;
|
||||
pendingShotEffect.value = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -431,11 +484,13 @@ async function onReceiveMessage(message) {
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on("socket-inbox", onReceiveMessage);
|
||||
updateTargetRect();
|
||||
void updateTargetRect();
|
||||
if (uni.onWindowResize) uni.onWindowResize(handleWindowResize);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
shotEffectRequestGeneration += 1;
|
||||
pendingShotEffect.value = null;
|
||||
clearTipTimer();
|
||||
if (dirTimer.value) {
|
||||
clearTimeout(dirTimer.value);
|
||||
@@ -555,6 +610,7 @@ onBeforeUnmount(() => {
|
||||
</view>
|
||||
</block>
|
||||
<BowShotEffect
|
||||
v-if="!shotEffect || !shotEffect.viewportMode"
|
||||
:shot="shotEffect && shotEffect.shot"
|
||||
:playKey="shotEffect ? shotEffect.key : ''"
|
||||
:targetRadius="safeTargetRadius"
|
||||
@@ -567,6 +623,20 @@ onBeforeUnmount(() => {
|
||||
@complete="completeShotEffect"
|
||||
/>
|
||||
</view>
|
||||
<BowShotEffect
|
||||
v-if="shotEffect && shotEffect.viewportMode"
|
||||
:shot="shotEffect.shot"
|
||||
:playKey="shotEffect.key"
|
||||
:targetRadius="safeTargetRadius"
|
||||
:targetLeft="targetRect.left"
|
||||
:targetTop="targetRect.top"
|
||||
:targetWidth="targetRect.width"
|
||||
:targetHeight="targetRect.height"
|
||||
:hitOffsetPx="currentHitRadiusPx"
|
||||
:viewportMode="true"
|
||||
@impact="shakeTarget"
|
||||
@complete="completeShotEffect"
|
||||
/>
|
||||
<view class="footer">
|
||||
<PointSwitcher
|
||||
:onChange="(val) => (pMode = val)"
|
||||
|
||||
@@ -1330,6 +1330,7 @@ onBeforeUnmount(() => {
|
||||
:activeSector="precisionRandomBlock"
|
||||
:activeRing="precisionRandomRingArea"
|
||||
:showSectorLabels="precisionBlocks > 0"
|
||||
stable-shot-effect
|
||||
@shot-effect-complete="onShotEffectComplete"
|
||||
/>
|
||||
<!-- <view v-if="env !== 'release'" class="highlight-test-actions">
|
||||
|
||||
Reference in New Issue
Block a user