Commit c4365854 authored by timel's avatar timel

feat: 抽离Dropdown,提升性能

parent 2805ab54
Pipeline #25709 failed with stages
in 14 minutes and 20 seconds
...@@ -14,8 +14,8 @@ const getWhom = (controller: number): "me" | "op" => ...@@ -14,8 +14,8 @@ const getWhom = (controller: number): "me" | "op" =>
isMe(controller) ? "me" : "op"; isMe(controller) ? "me" : "op";
/** /**
* 根据自己的先后手判断是否是自己 * 根据 controller 判断是否是自己,
* 原本名字叫judgeSelf * 入参可以是 currentPlayer 或者 card.location.controller
*/ */
export const isMe = (controller: number): boolean => { export const isMe = (controller: number): boolean => {
switch (matStore.selfType) { switch (matStore.selfType) {
......
...@@ -18,7 +18,7 @@ import { ...@@ -18,7 +18,7 @@ import {
SortCardModal, SortCardModal,
YesNoModal, YesNoModal,
} from "./Message"; } from "./Message";
import { LifeBar, Mat, Menu, Underlying } from "./PlayMat"; import { LifeBar, Mat, Menu, Underlying, CardDropdown } from "./PlayMat";
import { ChatBox } from "./PlayMat/ChatBox"; import { ChatBox } from "./PlayMat/ChatBox";
export const Component: React.FC = () => { export const Component: React.FC = () => {
...@@ -59,6 +59,7 @@ export const Component: React.FC = () => { ...@@ -59,6 +59,7 @@ export const Component: React.FC = () => {
<SimpleSelectCardsModal /> <SimpleSelectCardsModal />
<EndModal /> <EndModal />
<ChatBox /> <ChatBox />
<CardDropdown />
</> </>
); );
}; };
......
import { animated, to, useSpring } from "@react-spring/web"; import { animated, to, useSpring } from "@react-spring/web";
import { Dropdown, type MenuProps } from "antd";
import classnames from "classnames"; import classnames from "classnames";
import React, { type CSSProperties, useEffect, useRef, useState } from "react"; import React, { type CSSProperties, useEffect, useRef, useState } from "react";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
...@@ -12,9 +11,20 @@ import { ...@@ -12,9 +11,20 @@ import {
ygopro, ygopro,
} from "@/api"; } from "@/api";
import { eventbus, Task } from "@/infra"; import { eventbus, Task } from "@/infra";
import { cardStore, CardType, Interactivity, InteractType } from "@/stores"; import {
cardStore,
CardType,
Interactivity,
InteractType,
isMe,
} from "@/stores";
import { showCardModal as displayCardModal } from "@/ui/Duel/Message/CardModal"; import { showCardModal as displayCardModal } from "@/ui/Duel/Message/CardModal";
import { YgoCard } from "@/ui/Shared"; import {
showCardDropdown,
hideCardDropdown,
type DropdownItem,
} from "@/ui/Duel/PlayMat/Dropdown";
import { YgoCard, matConfig } from "@/ui/Shared";
import { import {
displayCardListModal, displayCardListModal,
...@@ -34,6 +44,8 @@ import type { SpringApiProps } from "./springs/types"; ...@@ -34,6 +44,8 @@ import type { SpringApiProps } from "./springs/types";
const { HAND, GRAVE, REMOVED, EXTRA, MZONE, SZONE, TZONE } = ygopro.CardZone; const { HAND, GRAVE, REMOVED, EXTRA, MZONE, SZONE, TZONE } = ygopro.CardZone;
const { CARD_RATIO } = matConfig;
export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
const card = cardStore.inner[idx]; const card = cardStore.inner[idx];
const snap = useSnapshot(card); const snap = useSnapshot(card);
...@@ -47,6 +59,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -47,6 +59,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
rx: 0, rx: 0,
ry: 0, ry: 0,
rz: 0, rz: 0,
rd: 0, // 围绕对角线的旋转
zIndex: 0, zIndex: 0,
height: 0, height: 0,
focusScale: 1, focusScale: 1,
...@@ -113,13 +126,6 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -113,13 +126,6 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
); );
}, [idleInteractivities]); }, [idleInteractivities]);
const [dropdownMenu, setDropdownMenu] = useState({
items: [] as DropdownItem[],
});
// 是否禁用下拉菜单
const [dropdownMenuDisabled, setDropdownMenuDisabled] = useState(false);
// 发动效果 // 发动效果
// 1. 下拉菜单里面选择[召唤 / 特殊召唤 /.../效果发动] // 1. 下拉菜单里面选择[召唤 / 特殊召唤 /.../效果发动]
// 2. 如果是非效果发动,那么直接选择哪张卡(单张卡直接选择那张) // 2. 如果是非效果发动,那么直接选择哪张卡(单张卡直接选择那张)
...@@ -136,10 +142,8 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -136,10 +142,8 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
}); });
if (!map.size) { if (!map.size) {
setDropdownMenuDisabled(true); hideCardDropdown();
return; return;
} else {
setDropdownMenuDisabled(false);
} }
const actions = [...map.entries()]; const actions = [...map.entries()];
const nonEffectActions = actions.filter( const nonEffectActions = actions.filter(
...@@ -228,12 +232,16 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -228,12 +232,16 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
); );
}, },
}; };
setDropdownMenu({ const current = api.current[0].get();
showCardDropdown({
items: [...nonEffectItem, ...(hasEffect ? [effectItem] : [])], items: [...nonEffectItem, ...(hasEffect ? [effectItem] : [])],
x: current.x,
y: current.y - current.height / 2 - 10, // 10px 的 gap
}); });
}; };
const onClick = () => { const onClick = (e: any) => {
e.stopPropagation();
const onCardClick = (card: CardType) => { const onCardClick = (card: CardType) => {
// 中央弹窗展示选中卡牌信息 // 中央弹窗展示选中卡牌信息
// TODO: 同一张卡片,是否重复点击会关闭CardModal? // TODO: 同一张卡片,是否重复点击会关闭CardModal?
...@@ -271,6 +279,15 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -271,6 +279,15 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
onFieldClick(card); onFieldClick(card);
} }
}; };
/** 鼠标hover在手卡上的效果 不确定 先放着 */
const onMouse = (isEnter: boolean) => () => {
// if (isMe(card.location.controller) && card.location.zone === HAND) {
// api.start({
// rd: isEnter ? 15 : 0,
// });
// }
};
// <<< 效果 <<< // <<< 效果 <<<
return ( return (
...@@ -279,9 +296,9 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -279,9 +296,9 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
style={ style={
{ {
transform: to( transform: to(
[spring.x, spring.y, spring.z, spring.rx, spring.ry, spring.rz], [spring.x, spring.y, spring.rx, spring.rz, spring.rd],
(x, y, z, rx, ry, rz) => (x, y, rx, rz, rd) =>
`translate(${x}px, ${y}px) rotateX(${rx}deg) rotateZ(${rz}deg)`, `translate(${x}px, ${y}px) rotateX(${rx}deg) rotateZ(${rz}deg) rotate3d(${CARD_RATIO}, 1, 0, ${rd}deg)`,
), ),
"--z": spring.z, "--z": spring.z,
"--sub-z": spring.subZ.to([0, 50, 100], [0, 200, 0]), // 中间高,两边低 "--sub-z": spring.subZ.to([0, 50, 100], [0, 200, 0]), // 中间高,两边低
...@@ -295,31 +312,23 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -295,31 +312,23 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
} as any as CSSProperties } as any as CSSProperties
} }
onClick={onClick} onClick={onClick}
onMouseEnter={onMouse(true)}
onMouseLeave={onMouse(false)}
> >
<div className={styles.focus} /> <div className={styles.focus} />
<div className={styles.shadow} /> <div className={styles.shadow} />
<Dropdown <div
menu={dropdownMenu} className={classnames(styles["img-wrap"], {
placement="top" [styles.focusing]: classFocus,
overlayClassName={classnames(styles.dropdown, {
[styles["dropdown-disabled"]]: dropdownMenuDisabled,
})} })}
arrow
trigger={["click"]}
> >
<div <YgoCard
className={classnames(styles["img-wrap"], { className={styles.cover}
[styles.focusing]: classFocus, // cardName={snap.meta.text.name}
})} code={snap.code === 0 ? snap.meta.id : snap.code}
> />
<YgoCard <YgoCard className={styles.back} isBack />
className={styles.cover} </div>
// cardName={snap.meta.text.name}
code={snap.code === 0 ? snap.meta.id : snap.code}
/>
<YgoCard className={styles.back} isBack />
</div>
</Dropdown>
{snap.selected ? <div className={styles.streamer} /> : <></>} {snap.selected ? <div className={styles.streamer} /> : <></>}
</animated.div> </animated.div>
); );
...@@ -332,10 +341,6 @@ interface Interactivy { ...@@ -332,10 +341,6 @@ interface Interactivy {
effectCode: number | undefined; effectCode: number | undefined;
} }
type DropdownItem = NonNullable<MenuProps["items"]>[number] & {
onClick: () => void;
};
const handleEffectActivation = ( const handleEffectActivation = (
effectInteractivies: Interactivy[], effectInteractivies: Interactivy[],
meta?: CardMeta, meta?: CardMeta,
......
...@@ -10,6 +10,7 @@ export interface SpringApiProps { ...@@ -10,6 +10,7 @@ export interface SpringApiProps {
rx: number; rx: number;
ry: number; ry: number;
rz: number; rz: number;
rd: number, // 围绕对角线的旋转
zIndex: number; zIndex: number;
height: number; height: number;
opacity: number; opacity: number;
......
import { Dropdown, type MenuProps } from "antd";
import { proxy, useSnapshot, ref } from "valtio";
export type DropdownItem = NonNullable<MenuProps["items"]>[number] & {
onClick: () => void;
};
const defaultProps = {
isOpen: false,
menu: {
items: ref([]) as DropdownItem[],
},
x: 0,
y: 0,
};
const localStore = proxy(defaultProps);
/** 想让下拉菜单全局唯一 */
export const CardDropdown: React.FC = () => {
const { isOpen, menu, x, y } = useSnapshot(localStore);
return (
<Dropdown
open
menu={menu as any}
overlayStyle={{
position: "fixed",
left: "50%",
top: "50%",
transform: `translate(-50%, -100%) translate3d(${x}px, ${y}px, 0)`,
opacity: isOpen ? 1 : 0, // 不能直接用在open属性上,否则会造成渲染不及时的问题
}}
placement="top"
autoAdjustOverflow={false}
arrow
>
<div style={{ width: 0, height: 0 }} />
</Dropdown>
);
};
export function showCardDropdown(props: {
items: DropdownItem[];
x: number;
y: number;
}) {
localStore.x = props.x;
localStore.y = props.y;
localStore.menu = {
items: props.items.map((x) => {
const tmpOnClick = x.onClick;
x.onClick = () => {
tmpOnClick();
localStore.isOpen = false;
};
return x;
}),
};
localStore.isOpen = true;
}
export function hideCardDropdown() {
localStore.isOpen = false;
}
// 全局点击收起
document.addEventListener("click", () => {
localStore.isOpen = false;
});
...@@ -2,3 +2,4 @@ export * from "./LifeBar"; ...@@ -2,3 +2,4 @@ export * from "./LifeBar";
export * from "./Mat"; export * from "./Mat";
export * from "./Menu"; export * from "./Menu";
export * from "./Underlying"; export * from "./Underlying";
export * from "./Dropdown";
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