Commit c964f920 authored by timel's avatar timel

feat: 调节动画

parent 24a7c1b4
...@@ -54,6 +54,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -54,6 +54,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
focusOpacity: 1, focusOpacity: 1,
subZ: 0, subZ: 0,
opacity: 1, opacity: 1,
config: { mass: 0.8, tension: 800, friction: 30 },
}) satisfies SpringApiProps, }) satisfies SpringApiProps,
); );
......
...@@ -20,6 +20,7 @@ export const attack: AttackFunc = async (props) => { ...@@ -20,6 +20,7 @@ export const attack: AttackFunc = async (props) => {
if (options?.directAttack) { if (options?.directAttack) {
// 直接攻击 // 直接攻击
y = BLOCK_HEIGHT_M + BLOCK_HEIGHT_S; y = BLOCK_HEIGHT_M + BLOCK_HEIGHT_S;
x = 0;
if (isMe(card.location.controller)) { if (isMe(card.location.controller)) {
y = -y; y = -y;
...@@ -40,30 +41,31 @@ export const attack: AttackFunc = async (props) => { ...@@ -40,30 +41,31 @@ export const attack: AttackFunc = async (props) => {
x = -x; x = -x;
y = -y; y = -y;
} }
rz += -Math.atan((x - current.x) / (y - current.y)) / (Math.PI / 180);
} else { } else {
console.error(`<Spring/Attack>directAttack is false and target is null.`); console.error(`<Spring/Attack>directAttack is false and target is null.`);
return; return;
} }
rz += -Math.atan((x - current.x) / (y - current.y)) / (Math.PI / 180);
// 先浮空 // 先浮空
await asyncStart(api)({ await asyncStart(api)({
z: 200, z: 200,
}); });
// 后撤半个卡位,并调整倾斜角 // 后撤半个卡位,并调整倾斜角
await asyncStart(api)({ // await asyncStart(api)({
y: // y:
current.y + // current.y +
(BLOCK_HEIGHT_M / 2) * (isMe(card.location.controller) ? 1 : -1), // (BLOCK_HEIGHT_M / 2) * (isMe(card.location.controller) ? 1 : -1),
rz, // rz,
}); // });
// 加速前冲 // 加速前冲
await asyncStart(api)({ await asyncStart(api)({
x, x,
y, y,
rz,
config: { config: {
easing: easings.easeInOutSine, easing: easings.easeInSine,
}, },
}); });
// 减速归位 // 减速归位
...@@ -72,8 +74,5 @@ export const attack: AttackFunc = async (props) => { ...@@ -72,8 +74,5 @@ export const attack: AttackFunc = async (props) => {
y: current.y, y: current.y,
z: current.z, z: current.z,
rz: current.rz, rz: current.rz,
config: {
easing: easings.easeInOutQuad,
},
}); });
}; };
...@@ -3,6 +3,7 @@ import { type CardType, matStore } from "@/stores"; ...@@ -3,6 +3,7 @@ import { type CardType, matStore } from "@/stores";
import type { SpringApi } from "./types"; import type { SpringApi } from "./types";
import { asyncStart } from "./utils"; import { asyncStart } from "./utils";
import { sleep } from "@/infra";
/** 发动效果的动画 */ /** 发动效果的动画 */
export const focus = async (props: { card: CardType; api: SpringApi }) => { export const focus = async (props: { card: CardType; api: SpringApi }) => {
...@@ -19,6 +20,7 @@ export const focus = async (props: { card: CardType; api: SpringApi }) => { ...@@ -19,6 +20,7 @@ export const focus = async (props: { card: CardType; api: SpringApi }) => {
// rz: 0, // rz: 0,
z: current.z + 50, z: current.z + 50,
}); });
await sleep(500); // 稍微停一下,让人看清
await asyncStart(api)(current); await asyncStart(api)(current);
} else { } else {
await asyncStart(api)({ await asyncStart(api)({
......
...@@ -98,32 +98,24 @@ export const moveToGround: MoveFunc = async (props) => { ...@@ -98,32 +98,24 @@ export const moveToGround: MoveFunc = async (props) => {
rz, rz,
height: 0, height: 0,
}); });
} else {
await asyncStart(api)({
x,
y,
height,
z: is_overlay ? 120 : 200,
ry,
rz,
config: {
tension: 250,
clamp: true,
easing: easings.easeOutSine,
},
});
} }
// 先稍微浮起,然后降落,避免卡穿模
asyncStart(api)({
z: height / 2
}).then(() => asyncStart(api)({
z: 0
}))
await asyncStart(api)({ await asyncStart(api)({
height, height,
x,
y,
z: 0, z: 0,
subZ: isToken ? 100 : 0, subZ: isToken ? 100 : 0,
ry,
rz,
zIndex: is_overlay ? 1 : 3, zIndex: is_overlay ? 1 : 3,
config: {
easing: easings.easeInQuad,
duration: 200,
clamp: true,
},
}); });
if (isToken) api.set({ subZ: 0 }); if (isToken) api.set({ subZ: 0 });
}; };
...@@ -22,16 +22,27 @@ export const moveToOutside: MoveFunc = async (props) => { ...@@ -22,16 +22,27 @@ export const moveToOutside: MoveFunc = async (props) => {
const { zone, controller, position, sequence } = card.location; const { zone, controller, position, sequence } = card.location;
let x = let x =
BLOCK_WIDTH * 2.5 + BLOCK_WIDTH * 2.5 +
COL_GAP * 2 + COL_GAP * 2 +
BLOCK_OUTSIDE_OFFSET_X + BLOCK_OUTSIDE_OFFSET_X +
CARD_HEIGHT_O * CARD_RATIO * 0.5, CARD_HEIGHT_O * CARD_RATIO * 0.5,
y = ROW_GAP + BLOCK_HEIGHT_M + (BLOCK_HEIGHT_M - CARD_HEIGHT_O) / 2; y = ROW_GAP + BLOCK_HEIGHT_M + (BLOCK_HEIGHT_M - CARD_HEIGHT_O) / 2;
if (zone === REMOVED) y -= ROW_GAP + CARD_HEIGHT_O; if (zone === REMOVED) y -= ROW_GAP + CARD_HEIGHT_O;
if (!isMe(controller)) { if (!isMe(controller)) {
x = -x; x = -x;
y = -y; y = -y;
} }
// 先稍微浮起,然后降落,避免卡穿模
asyncStart(api)({
z: CARD_HEIGHT_O / 2
}).then(() => asyncStart(api)({
z: 0,
config: {
clamp: false
}
}))
await asyncStart(api)({ await asyncStart(api)({
x, x,
y, y,
...@@ -41,9 +52,6 @@ export const moveToOutside: MoveFunc = async (props) => { ...@@ -41,9 +52,6 @@ export const moveToOutside: MoveFunc = async (props) => {
ry: [ygopro.CardPosition.FACEDOWN].includes(position) ? 180 : 0, ry: [ygopro.CardPosition.FACEDOWN].includes(position) ? 180 : 0,
subZ: 100, subZ: 100,
zIndex: sequence, zIndex: sequence,
config: {
tension: 140,
},
}); });
api.set({ subZ: 0 }); api.set({ subZ: 0 });
}; };
import type { SpringRef } from "@react-spring/web"; import type { SpringRef, SpringConfig } from "@react-spring/web";
import type { ygopro } from "@/api"; import type { ygopro } from "@/api";
import type { CardType } from "@/stores"; import type { CardType } from "@/stores";
...@@ -20,6 +20,8 @@ export interface SpringApiProps { ...@@ -20,6 +20,8 @@ export interface SpringApiProps {
// <<< focus // <<< focus
subZ: number; // 0 -> 100,这是为了让卡片移动过程中,稍微上浮一些,避免一些奇怪的遮挡问题 subZ: number; // 0 -> 100,这是为了让卡片移动过程中,稍微上浮一些,避免一些奇怪的遮挡问题
config?: SpringConfig
} }
export type SpringApi = SpringRef<SpringApiProps>; export type SpringApi = SpringRef<SpringApiProps>;
...@@ -37,7 +39,7 @@ export type MoveFunc = OptionsToFunc<MoveOptions>; ...@@ -37,7 +39,7 @@ export type MoveFunc = OptionsToFunc<MoveOptions>;
export type AttackOptions = export type AttackOptions =
| { | {
directAttack: true; directAttack: true;
} }
| { directAttack: false; target: ygopro.CardLocation }; | { directAttack: false; target: ygopro.CardLocation };
export type AttackFunc = OptionsToFunc<AttackOptions>; export type AttackFunc = OptionsToFunc<AttackOptions>;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment