Commit 67516e9a authored by timel's avatar timel

feat: ui block

parent 860d5508
...@@ -67,6 +67,38 @@ section#mat { ...@@ -67,6 +67,38 @@ section#mat {
} }
} }
} }
// 下面应该和moveToOutside、moveToGround对应
.bg-other-blocks {
&.op {
transform: rotate(180deg);
}
position: absolute;
--height: var(--card-height-o);
--width: calc(var(--height) * var(--card-ratio));
--left: calc(
var(--col-gap) * 2 + var(--block-width) * 2.5 +
var(--block-outside-offset-x) + var(--width) / 2
);
--top: calc(
var(--row-gap) + var(--block-height-m) +
(var(--block-height-m) - var(--height)) / 2
);
.block {
position: absolute;
transform: translate(-50%, -50%);
height: var(--height);
width: var(--width);
top: var(--top);
left: var(--left);
}
.field {
left: calc(-1 * var(--left));
}
.banish {
top: calc(var(--top) - var(--row-gap) - var(--height));
}
}
} }
// 被禁用的样式 // 被禁用的样式
......
...@@ -5,12 +5,19 @@ import { type INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio"; ...@@ -5,12 +5,19 @@ import { type INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio";
import { sendSelectPlaceResponse, ygopro } from "@/api"; import { sendSelectPlaceResponse, ygopro } from "@/api";
import { import {
BlockState, type BlockState,
cardStore, cardStore,
type PlaceInteractivity, type PlaceInteractivity,
placeStore, placeStore,
} from "@/stores"; } from "@/stores";
const BgBlock: React.FC<React.HTMLProps<HTMLDivElement>> = (props) => (
<div {...props} className={classnames("block", props.className)}>
{<DecoTriangles />}
{<DisabledCross />}
</div>
);
const BgExtraRow: React.FC<{ const BgExtraRow: React.FC<{
meSnap: Snapshot<BlockState[]>; meSnap: Snapshot<BlockState[]>;
opSnap: Snapshot<BlockState[]>; opSnap: Snapshot<BlockState[]>;
...@@ -18,9 +25,9 @@ const BgExtraRow: React.FC<{ ...@@ -18,9 +25,9 @@ const BgExtraRow: React.FC<{
return ( return (
<div className={classnames("bg-row")}> <div className={classnames("bg-row")}>
{Array.from({ length: 2 }).map((_, i) => ( {Array.from({ length: 2 }).map((_, i) => (
<div <BgBlock
key={i} key={i}
className={classnames("block", "extra", { className={classnames("extra", {
highlight: !!meSnap[i].interactivity || !!opSnap[i].interactivity, highlight: !!meSnap[i].interactivity || !!opSnap[i].interactivity,
disabled: meSnap[i].disabled || opSnap[i].disabled, disabled: meSnap[i].disabled || opSnap[i].disabled,
})} })}
...@@ -28,10 +35,7 @@ const BgExtraRow: React.FC<{ ...@@ -28,10 +35,7 @@ const BgExtraRow: React.FC<{
onBlockClick(meSnap[i].interactivity); onBlockClick(meSnap[i].interactivity);
onBlockClick(opSnap[i].interactivity); onBlockClick(opSnap[i].interactivity);
}} }}
> />
{<DecoTriangles />}
{<DisabledCross />}
</div>
))} ))}
</div> </div>
); );
...@@ -44,22 +48,27 @@ const BgRow: React.FC<{ ...@@ -44,22 +48,27 @@ const BgRow: React.FC<{
}> = ({ isSzone = false, opponent = false, snap }) => ( }> = ({ isSzone = false, opponent = false, snap }) => (
<div className={classnames("bg-row", { opponent })}> <div className={classnames("bg-row", { opponent })}>
{Array.from({ length: 5 }).map((_, i) => ( {Array.from({ length: 5 }).map((_, i) => (
<div <BgBlock
key={i} key={i}
className={classnames("block", { className={classnames({
szone: isSzone, szone: isSzone,
highlight: !!snap[i].interactivity, highlight: !!snap[i].interactivity,
disabled: snap[i].disabled, disabled: snap[i].disabled,
})} })}
onClick={() => onBlockClick(snap[i].interactivity)} onClick={() => onBlockClick(snap[i].interactivity)}
> />
{<DecoTriangles />}
{<DisabledCross />}
</div>
))} ))}
</div> </div>
); );
const BgOtherBlocks: React.FC<{ className?: string }> = ({ className }) => (
<div className={classnames("bg-other-blocks", className)}>
<BgBlock className="banish" />
<BgBlock className="graveyard" />
<BgBlock className="field" />
</div>
);
export const Bg: React.FC = () => { export const Bg: React.FC = () => {
const snap = useSnapshot(placeStore.inner); const snap = useSnapshot(placeStore.inner);
return ( return (
...@@ -72,6 +81,8 @@ export const Bg: React.FC = () => { ...@@ -72,6 +81,8 @@ export const Bg: React.FC = () => {
/> />
<BgRow snap={snap[ygopro.CardZone.MZONE].me} /> <BgRow snap={snap[ygopro.CardZone.MZONE].me} />
<BgRow snap={snap[ygopro.CardZone.SZONE].me} isSzone /> <BgRow snap={snap[ygopro.CardZone.SZONE].me} isSzone />
<BgOtherBlocks className="me" />
<BgOtherBlocks className="op" />
</div> </div>
); );
}; };
......
...@@ -57,6 +57,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -57,6 +57,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
focusDisplay: "none", focusDisplay: "none",
focusOpacity: 1, focusOpacity: 1,
subZ: 0, subZ: 0,
opacity: 1,
} satisfies SpringApiProps) } satisfies SpringApiProps)
); );
...@@ -310,6 +311,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -310,6 +311,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
"--focus-scale": styles.focusScale, "--focus-scale": styles.focusScale,
"--focus-display": styles.focusDisplay, "--focus-display": styles.focusDisplay,
"--focus-opacity": styles.focusOpacity, "--focus-opacity": styles.focusOpacity,
opacity: styles.opacity,
} as any as CSSProperties } as any as CSSProperties
} }
onClick={onClick} onClick={onClick}
......
...@@ -13,6 +13,8 @@ const { ...@@ -13,6 +13,8 @@ const {
CARD_RATIO, CARD_RATIO,
COL_GAP, COL_GAP,
ROW_GAP, ROW_GAP,
BLOCK_OUTSIDE_OFFSET_X,
CARD_HEIGHT_O,
} = matConfig; } = matConfig;
const { MZONE, SZONE, TZONE } = ygopro.CardZone; const { MZONE, SZONE, TZONE } = ygopro.CardZone;
...@@ -36,9 +38,15 @@ export const moveToGround: MoveFunc = async (props) => { ...@@ -36,9 +38,15 @@ export const moveToGround: MoveFunc = async (props) => {
switch (zone) { switch (zone) {
case SZONE: { case SZONE: {
if (sequence === 5) { if (sequence === 5) {
height = CARD_HEIGHT_O;
// 场地魔法 // 场地魔法
x = -(3 * (BLOCK_WIDTH + COL_GAP) - (BLOCK_WIDTH - cardWidth) / 2); x = -(
y = BLOCK_HEIGHT_M + ROW_GAP; BLOCK_WIDTH * 2.5 +
COL_GAP * 2 +
BLOCK_OUTSIDE_OFFSET_X +
CARD_HEIGHT_O * CARD_RATIO * 0.5
);
y = ROW_GAP + BLOCK_HEIGHT_M + (BLOCK_HEIGHT_M - CARD_HEIGHT_O) / 2;
} else { } else {
x = (sequence - 2) * (BLOCK_WIDTH + COL_GAP); x = (sequence - 2) * (BLOCK_WIDTH + COL_GAP);
y = y =
......
...@@ -4,18 +4,30 @@ import { isMe } from "@/stores"; ...@@ -4,18 +4,30 @@ import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared"; import { matConfig } from "@/ui/Shared";
import { asyncStart, type MoveFunc } from "./utils"; import { asyncStart, type MoveFunc } from "./utils";
const { BLOCK_WIDTH, BLOCK_HEIGHT_M, BLOCK_HEIGHT_S, COL_GAP, ROW_GAP } = const {
matConfig; BLOCK_WIDTH,
BLOCK_HEIGHT_M,
BLOCK_HEIGHT_S,
COL_GAP,
ROW_GAP,
CARD_HEIGHT_O,
BLOCK_OUTSIDE_OFFSET_X,
CARD_RATIO,
} = matConfig;
const { GRAVE } = ygopro.CardZone; const { REMOVED } = ygopro.CardZone;
export const moveToOutside: MoveFunc = async (props) => { export const moveToOutside: MoveFunc = async (props) => {
const { card, api } = props; const { card, api } = props;
// report
const { zone, controller, position, sequence } = card.location; const { zone, controller, position, sequence } = card.location;
let x = (BLOCK_WIDTH + COL_GAP) * 3, let x =
y = zone === GRAVE ? BLOCK_HEIGHT_M + ROW_GAP : 0; BLOCK_WIDTH * 2.5 +
COL_GAP * 2 +
BLOCK_OUTSIDE_OFFSET_X +
CARD_HEIGHT_O * CARD_RATIO * 0.5,
y = ROW_GAP + BLOCK_HEIGHT_M + (BLOCK_HEIGHT_M - CARD_HEIGHT_O) / 2;
if (zone === REMOVED) y -= ROW_GAP + CARD_HEIGHT_O;
if (!isMe(controller)) { if (!isMe(controller)) {
x = -x; x = -x;
y = -y; y = -y;
...@@ -24,7 +36,7 @@ export const moveToOutside: MoveFunc = async (props) => { ...@@ -24,7 +36,7 @@ export const moveToOutside: MoveFunc = async (props) => {
x, x,
y, y,
z: 0, z: 0,
height: BLOCK_HEIGHT_S, height: CARD_HEIGHT_O,
rz: isMe(controller) ? 0 : 180, rz: isMe(controller) ? 0 : 180,
ry: [ygopro.CardPosition.FACEDOWN].includes(position) ? 180 : 0, ry: [ygopro.CardPosition.FACEDOWN].includes(position) ? 180 : 0,
subZ: 100, subZ: 100,
......
...@@ -4,5 +4,7 @@ export const moveToToken: MoveFunc = async (props) => { ...@@ -4,5 +4,7 @@ export const moveToToken: MoveFunc = async (props) => {
const { api } = props; const { api } = props;
await asyncStart(api)({ await asyncStart(api)({
height: 0, height: 0,
opacity: 0,
}); });
api.set({ opacity: 1 });
}; };
...@@ -9,6 +9,7 @@ export interface SpringApiProps { ...@@ -9,6 +9,7 @@ export interface SpringApiProps {
rz: number; rz: number;
zIndex: number; zIndex: number;
height: number; height: number;
opacity: number;
// >>> focus // >>> focus
focusScale: number; focusScale: number;
focusDisplay: string; focusDisplay: string;
......
...@@ -22,7 +22,7 @@ enum UNIT { ...@@ -22,7 +22,7 @@ enum UNIT {
NONE = "", NONE = "",
} }
const matConfigWithUnit: CSSConfig = { const matConfigWithUnit = {
PERSPECTIVE: [1500, UNIT.PX], PERSPECTIVE: [1500, UNIT.PX],
PLANE_ROTATE_X: [0, UNIT.DEG], PLANE_ROTATE_X: [0, UNIT.DEG],
BLOCK_WIDTH: [120, UNIT.PX], BLOCK_WIDTH: [120, UNIT.PX],
...@@ -38,12 +38,18 @@ const matConfigWithUnit: CSSConfig = { ...@@ -38,12 +38,18 @@ const matConfigWithUnit: CSSConfig = {
DECK_OFFSET_Y: [80, UNIT.PX], DECK_OFFSET_Y: [80, UNIT.PX],
DECK_ROTATE_Z: [30, UNIT.DEG], DECK_ROTATE_Z: [30, UNIT.DEG],
DECK_CARD_HEIGHT: [120, UNIT.PX], DECK_CARD_HEIGHT: [120, UNIT.PX],
}; CARD_HEIGHT_O: [100, UNIT.PX], // 场地魔法/墓地/除外的卡片高度
BLOCK_OUTSIDE_OFFSET_X: [15, UNIT.PX],
} satisfies CSSConfig;
export const matConfig = Object.keys(matConfigWithUnit).reduce( export const matConfig = Object.keys(matConfigWithUnit).reduce(
(prev, key) => ({ ...prev, [key]: matConfigWithUnit[key][0] }), (prev, key) => ({
...prev,
// @ts-ignore
[key]: matConfigWithUnit[key][0],
}),
{} as Record<string, number> {} as Record<string, number>
); ) as Record<keyof typeof matConfigWithUnit, number>;
toCssProperties(matConfigWithUnit).forEach(([k, v]) => { toCssProperties(matConfigWithUnit).forEach(([k, v]) => {
document.body.style.setProperty(k, v); document.body.style.setProperty(k, v);
......
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