Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
love_飞影
Neos
Commits
4af2045b
Commit
4af2045b
authored
May 04, 2023
by
timel
Committed by
Chunchi Che
May 28, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add animation
parent
dcf03b35
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
472 additions
and
319 deletions
+472
-319
src/ui/Duel/Main.tsx
src/ui/Duel/Main.tsx
+0
-1
src/ui/Duel/NewPlayMat/Card/index.scss
src/ui/Duel/NewPlayMat/Card/index.scss
+4
-3
src/ui/Duel/NewPlayMat/Card/index.tsx
src/ui/Duel/NewPlayMat/Card/index.tsx
+85
-227
src/ui/Duel/NewPlayMat/Card/springs/index.ts
src/ui/Duel/NewPlayMat/Card/springs/index.ts
+4
-0
src/ui/Duel/NewPlayMat/Card/springs/toDeck.ts
src/ui/Duel/NewPlayMat/Card/springs/toDeck.ts
+64
-0
src/ui/Duel/NewPlayMat/Card/springs/toField.ts
src/ui/Duel/NewPlayMat/Card/springs/toField.ts
+126
-0
src/ui/Duel/NewPlayMat/Card/springs/toHand.ts
src/ui/Duel/NewPlayMat/Card/springs/toHand.ts
+77
-0
src/ui/Duel/NewPlayMat/Card/springs/toOutside.ts
src/ui/Duel/NewPlayMat/Card/springs/toOutside.ts
+55
-0
src/ui/Duel/NewPlayMat/Card/springs/types.ts
src/ui/Duel/NewPlayMat/Card/springs/types.ts
+16
-0
src/ui/Duel/NewPlayMat/Card/springs/utils.ts
src/ui/Duel/NewPlayMat/Card/springs/utils.ts
+11
-0
src/ui/Duel/NewPlayMat/Mat/index.scss
src/ui/Duel/NewPlayMat/Mat/index.scss
+17
-3
src/ui/Duel/NewPlayMat/Mat/index.tsx
src/ui/Duel/NewPlayMat/Mat/index.tsx
+9
-3
src/ui/Duel/NewPlayMat/utils/cssConfig.ts
src/ui/Duel/NewPlayMat/utils/cssConfig.ts
+4
-0
src/ui/Duel/Test.tsx
src/ui/Duel/Test.tsx
+0
-82
No files found.
src/ui/Duel/Main.tsx
View file @
4af2045b
...
...
@@ -14,7 +14,6 @@ import {
YesNoModal
,
}
from
"
./Message
"
;
import
Mat
from
"
./PlayMat
"
;
import
{
Test
}
from
"
./Test
"
;
import
{
Mat
as
NewMat
}
from
"
./NewPlayMat
"
;
import
{
Menu
}
from
"
./NewPlayMat/Menu
"
;
...
...
src/ui/Duel/NewPlayMat/Card/index.scss
View file @
4af2045b
section
#mat
{
.mat-card
{
position
:
absolute
;
left
:
0
;
top
:
0
;
// left: 50%
;
// top: 50%
;
--card-height
:
100px
;
height
:
var
(
--
card-height
);
aspect-ratio
:
var
(
--
card-ratio
);
...
...
@@ -12,7 +12,8 @@ section#mat {
position
:
relative
;
height
:
100%
;
width
:
100%
;
transform
:
rotateY
(
calc
(
var
(
--
ry
)
*
1deg
));
transform
:
translateZ
(
calc
(
var
(
--
z
)
*
1px
))
rotateY
(
calc
(
var
(
--
ry
)
*
1deg
));
.card-cover
,
.card-back
{
width
:
100%
;
...
...
src/ui/Duel/NewPlayMat/Card/index.tsx
View file @
4af2045b
import
React
,
{
type
CSSProperties
,
type
FC
}
from
"
react
"
;
import
classnames
from
"
classnames
"
;
import
{
CardType
,
cardStore
,
isMe
}
from
"
@/stores
"
;
import
React
,
{
useEffect
,
type
CSSProperties
,
type
FC
}
from
"
react
"
;
import
{
cardStore
,
messageStore
,
CardType
}
from
"
@/stores
"
;
import
"
./index.scss
"
;
import
{
useSnapshot
,
INTERNAL_Snapshot
as
Snapshot
}
from
"
valtio
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
watch
}
from
"
valtio/utils
"
;
import
{
useSpringRef
,
useSpring
,
animated
,
to
,
CSS
}
from
"
@react-spring/web
"
;
import
{
matConfig
}
from
"
../utils
"
;
import
{
useSpring
,
animated
,
to
}
from
"
@react-spring/web
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
useConfig
}
from
"
@/config
"
;
import
{
moveToDeck
,
moveToField
,
moveToHand
,
moveToOutside
}
from
"
./springs
"
;
import
{
ReportEnum
}
from
"
./springs/types
"
;
import
{
interactTypeToString
}
from
"
../../utils
"
;
const
NeosConfig
=
useConfig
();
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
,
OVERLAY
}
=
ygopro
.
CardZone
;
const
{
PLANE_ROTATE_X
,
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
CARD_RATIO
,
COL_GAP
,
ROW_GAP
,
HAND_MARGIN_TOP
,
HAND_CARD_HEIGHT
,
HAND_CIRCLE_CENTER_OFFSET_Y
,
DECK_OFFSET_X
,
DECK_OFFSET_Y
,
DECK_ROTATE_Z
,
}
=
matConfig
;
export
const
Card
:
FC
<
{
idx
:
number
}
>
=
React
.
memo
(({
idx
})
=>
{
const
state
=
cardStore
.
inner
[
idx
];
const
snap
=
useSnapshot
(
state
);
const
inintialCoord
=
calcCoordinate
(
state
,
false
);
const
api
=
useSpringRef
();
const
props
=
useSpring
({
ref
:
api
,
from
:
{
x
:
inintialCoord
.
translateX
,
y
:
inintialCoord
.
translateY
,
z
:
inintialCoord
.
translateZ
,
rotateX
:
inintialCoord
.
rotateX
,
rotateY
:
inintialCoord
.
rotateY
,
rotateZ
:
inintialCoord
.
rotateZ
,
height
:
inintialCoord
.
height
,
},
});
const
reloadPosition
=
(
report
:
boolean
)
=>
{
const
coord
=
calcCoordinate
(
state
,
report
);
api
.
start
({
to
:
{
x
:
coord
.
translateX
,
y
:
coord
.
translateY
,
z
:
coord
.
translateZ
,
rotateX
:
coord
.
rotateX
,
rotateY
:
coord
.
rotateY
,
rotateZ
:
coord
.
rotateZ
,
height
:
coord
.
height
,
},
});
const
[
styles
,
api
]
=
useSpring
(()
=>
({
x
:
0
,
y
:
0
,
z
:
0
,
rx
:
0
,
ry
:
0
,
rz
:
0
,
zIndex
:
0
,
height
:
0
,
}));
const
reload
=
(
zone
:
ygopro
.
CardZone
,
report
:
boolean
)
=>
{
switch
(
zone
)
{
case
MZONE
:
case
SZONE
:
case
OVERLAY
:
moveToField
({
card
:
state
,
api
,
report
});
break
;
case
HAND
:
moveToHand
({
card
:
state
,
api
,
report
});
break
;
case
DECK
:
case
EXTRA
:
moveToDeck
({
card
:
state
,
api
,
report
});
break
;
case
GRAVE
:
case
REMOVED
:
moveToOutside
({
card
:
state
,
api
,
report
});
break
;
}
};
useEffect
(()
=>
{
reload
(
state
.
zone
,
false
);
},
[]);
watch
((
get
)
=>
{
const
{
zone
,
sequence
,
controller
,
xyzMonster
}
=
get
(
state
);
reload
Position
(
true
);
reload
(
zone
,
true
);
});
// 在别的手卡更改时候,刷新这张手卡
eventBus
.
on
(
"
reload-hands
"
,
ReportEnum
.
ReloadHand
,
({
sequence
,
controller
}:
{
sequence
:
number
;
controller
:
number
})
=>
{
if
(
state
.
sequence
!==
sequence
&&
state
.
controller
===
controller
)
{
reloadPosition
(
false
);
// console.warn('reload')
reload
(
state
.
zone
,
false
);
}
}
);
...
...
@@ -83,193 +75,28 @@ export const Card: FC<{ idx: number }> = React.memo(({ idx }) => {
style=
{
{
transform
:
to
(
[
props
.
x
,
props
.
y
,
props
.
z
,
props
.
rotateX
,
props
.
rotateY
,
props
.
rotateZ
,
],
[
styles
.
x
,
styles
.
y
,
styles
.
z
,
styles
.
rx
,
styles
.
ry
,
styles
.
rz
],
(
x
,
y
,
z
,
rx
,
ry
,
rz
)
=>
`translate
3d(${x}px, ${y}px, ${z
}px) rotateX(${rx}deg) rotateZ(${rz}deg)`
`translate
(${x}px, ${y
}px) rotateX(${rx}deg) rotateZ(${rz}deg)`
),
"
--ry
"
:
props
.
rotateY
,
height
:
props
.
height
,
"
--z
"
:
styles
.
z
,
"
--ry
"
:
styles
.
ry
,
height
:
styles
.
height
,
}
as
any
as
CSSProperties
}
onClick=
{
()
=>
[
MZONE
,
SZONE
,
HAND
].
includes
(
state
.
zone
)
&&
onCardClick
(
state
)
}
>
<
div
className=
"card-img-wrap"
>
<
img
className=
"card-cover"
src=
{
getCardImgUrl
(
snap
.
code
)
}
alt=
""
/>
<
img
className=
"card-back"
src=
{
getCardImgUrl
(
0
,
true
)
}
alt=
""
/>
</
div
>
<
div
className=
"card-shadow"
/>
</
animated
.
div
>
);
});
function
calcCoordinate
(
{
zone
,
sequence
,
position
,
xyzMonster
,
controller
}:
CardType
,
report
:
boolean
)
{
const
opponent
=
!
isMe
(
controller
);
const
res
=
{
translateX
:
-
BLOCK_WIDTH
.
value
,
translateY
:
BLOCK_HEIGHT_M
.
value
+
BLOCK_HEIGHT_S
.
value
+
ROW_GAP
.
value
*
2
,
translateZ
:
0
,
rotateX
:
0
,
rotateY
:
0
,
rotateZ
:
0
,
height
:
0
,
};
let
row
=
-
1
,
col
=
-
1
;
if
([
MZONE
,
SZONE
].
includes
(
zone
))
{
row
=
zone
===
MZONE
?
(
sequence
>
4
?
2
:
opponent
?
1
:
3
)
:
opponent
?
0
:
4
;
col
=
sequence
>
4
?
(
sequence
>
5
?
3
:
1
)
:
sequence
;
if
(
opponent
)
col
=
posHelper
[
col
];
}
if
(
zone
===
OVERLAY
&&
xyzMonster
)
{
const
{
zone
,
sequence
}
=
xyzMonster
;
row
=
zone
===
MZONE
?
(
sequence
>
4
?
2
:
opponent
?
1
:
3
)
:
opponent
?
0
:
4
;
col
=
sequence
>
4
?
(
sequence
>
5
?
3
:
1
)
:
sequence
;
if
(
opponent
)
col
=
posHelper
[
col
];
}
const
isField
=
zone
===
SZONE
&&
sequence
===
5
;
if
(
isField
)
{
row
=
opponent
?
1
:
3
;
col
=
opponent
?
5
:
-
1
;
}
const
_position
=
zone
===
OVERLAY
&&
xyzMonster
?
xyzMonster
.
position
:
position
;
const
defense
=
[
ygopro
.
CardPosition
.
DEFENSE
,
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
,
ygopro
.
CardPosition
.
FACEUP_DEFENSE
,
].
includes
(
_position
??
5
);
res
.
rotateZ
=
opponent
?
180
:
0
;
res
.
rotateZ
+=
defense
?
90
:
0
;
res
.
rotateY
=
[
ygopro
.
CardPosition
.
FACEDOWN
,
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
,
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
,
].
includes
(
_position
??
5
)
?
180
:
res
.
rotateY
;
res
.
rotateY
=
[
DECK
,
EXTRA
].
includes
(
zone
)
?
180
:
0
;
zone
===
HAND
&&
(
res
.
rotateY
=
opponent
?
180
:
0
);
res
.
height
=
defense
?
BLOCK_WIDTH
.
value
:
zone
===
MZONE
?
BLOCK_HEIGHT_M
.
value
:
BLOCK_HEIGHT_S
.
value
;
const
blockPaddingX
=
(
BLOCK_WIDTH
.
value
-
res
.
height
*
CARD_RATIO
.
value
)
/
2
;
if
(
row
>
-
1
)
{
// 说明是场上的卡
res
.
translateX
=
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
*
col
+
blockPaddingX
;
res
.
translateY
=
ROW_GAP
.
value
*
row
+
BLOCK_HEIGHT_M
.
value
*
Math
.
min
(
Math
.
max
(
0
,
row
-
1
),
3
)
+
BLOCK_HEIGHT_S
.
value
*
Math
.
ceil
(
row
/
4
);
}
if
(
zone
===
HAND
)
{
// 得刷新除了这个卡以外所有的自己的手卡
if
(
report
)
{
eventBus
.
emit
(
"
reload-hands
"
,
{
controller
,
sequence
,
});
}
// 手卡会有很复杂的计算...
const
hand_circle_center_x
=
(
5
*
BLOCK_WIDTH
.
value
+
4
*
COL_GAP
.
value
)
/
2
;
const
hand_circle_center_y
=
(
3
*
BLOCK_HEIGHT_M
.
value
+
2
*
BLOCK_HEIGHT_S
.
value
+
4
*
ROW_GAP
.
value
)
*
Number
(
!
opponent
)
+
(
HAND_MARGIN_TOP
.
value
+
HAND_CARD_HEIGHT
.
value
+
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
)
*
(
opponent
?
-
1
:
1
);
const
hand_card_width
=
CARD_RATIO
.
value
*
HAND_CARD_HEIGHT
.
value
;
const
THETA
=
2
*
Math
.
atan
(
hand_card_width
/
2
/
(
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
+
HAND_CARD_HEIGHT
.
value
)
)
*
0.9
;
// 接下来计算每一张手卡
const
hands_length
=
cardStore
.
at
(
HAND
,
controller
).
length
;
const
angle
=
(
sequence
-
(
hands_length
-
1
)
/
2
)
*
THETA
;
const
r
=
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
negativeX
=
Math
.
sin
(
angle
)
*
r
-
hand_card_width
/
2
;
const
negativeY
=
Math
.
cos
(
angle
)
*
r
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
x
=
hand_circle_center_x
+
negativeX
;
const
y
=
hand_circle_center_y
-
negativeY
*
(
opponent
?
-
1
:
1
)
-
Number
(
opponent
)
*
HAND_CARD_HEIGHT
.
value
;
res
.
translateX
=
x
;
res
.
translateY
=
y
;
res
.
translateZ
=
50
;
res
.
rotateZ
=
((
angle
*
180
)
/
Math
.
PI
)
*
(
opponent
?
-
1
:
1
)
+
(
opponent
?
180
:
0
);
res
.
rotateX
=
-
PLANE_ROTATE_X
.
value
;
}
if
(
zone
===
DECK
||
zone
===
EXTRA
)
{
const
leftX
=
-
DECK_OFFSET_X
.
value
;
const
rightX
=
DECK_OFFSET_X
.
value
+
5
*
BLOCK_WIDTH
.
value
+
4
*
COL_GAP
.
value
-
CARD_RATIO
.
value
*
res
.
height
;
const
topY
=
-
DECK_OFFSET_Y
.
value
;
const
bottomY
=
DECK_OFFSET_Y
.
value
+
3
*
BLOCK_HEIGHT_M
.
value
+
2
*
BLOCK_HEIGHT_S
.
value
+
4
*
ROW_GAP
.
value
-
BLOCK_HEIGHT_S
.
value
;
res
.
translateX
=
opponent
?
leftX
:
rightX
;
res
.
translateY
=
opponent
?
topY
:
bottomY
;
if
(
zone
===
EXTRA
)
{
res
.
translateX
=
opponent
?
rightX
:
leftX
;
}
res
.
rotateZ
=
opponent
?
-
DECK_ROTATE_Z
.
value
:
180
-
DECK_ROTATE_Z
.
value
;
if
(
zone
===
EXTRA
)
{
res
.
rotateZ
=
opponent
?
DECK_ROTATE_Z
.
value
:
DECK_ROTATE_Z
.
value
;
}
res
.
translateZ
=
sequence
;
}
return
res
;
}
const
posHelper
:
Record
<
number
,
number
>
=
{
0
:
4
,
1
:
3
,
2
:
2
,
3
:
1
,
4
:
0
,
5
:
6
,
6
:
5
,
};
function
getCardImgUrl
(
code
:
number
,
back
=
false
)
{
const
ASSETS_BASE
=
import
.
meta
.
env
.
BASE_URL
==
"
/
"
...
...
@@ -280,3 +107,34 @@ function getCardImgUrl(code: number, back = false) {
}
return
NeosConfig
.
cardImgUrl
+
"
/
"
+
code
+
"
.jpg
"
;
}
const
onCardClick
=
(
card
:
CardType
)
=>
()
=>
{
// 中央弹窗展示选中卡牌信息
messageStore
.
cardModal
.
meta
=
{
id
:
card
.
code
,
text
:
card
.
text
,
data
:
card
.
data
,
};
messageStore
.
cardModal
.
interactivies
=
card
.
idleInteractivities
.
map
(
(
interactivity
)
=>
({
desc
:
interactTypeToString
(
interactivity
.
interactType
),
response
:
interactivity
.
response
,
})
);
messageStore
.
cardModal
.
counters
=
card
.
counters
;
messageStore
.
cardModal
.
isOpen
=
true
;
// 侧边栏展示超量素材信息
if
(
card
.
overlayMaterials
.
length
>
0
)
{
messageStore
.
cardListModal
.
list
=
card
.
overlayMaterials
.
map
((
overlay
)
=>
({
meta
:
{
id
:
overlay
.
code
,
text
:
overlay
.
text
,
data
:
overlay
.
data
,
},
interactivies
:
[],
}))
||
[];
messageStore
.
cardListModal
.
isOpen
=
true
;
}
};
src/ui/Duel/NewPlayMat/Card/springs/index.ts
0 → 100644
View file @
4af2045b
export
*
from
"
./toField
"
;
export
*
from
"
./toHand
"
;
export
*
from
"
./toDeck
"
;
export
*
from
"
./toOutside
"
;
src/ui/Duel/NewPlayMat/Card/springs/toDeck.ts
0 → 100644
View file @
4af2045b
import
{
isMe
,
type
CardType
}
from
"
@/stores
"
;
import
{
SpringApi
}
from
"
./types
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
PLANE_ROTATE_X
,
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
CARD_RATIO
,
COL_GAP
,
ROW_GAP
,
HAND_MARGIN_TOP
,
HAND_CARD_HEIGHT
,
HAND_CIRCLE_CENTER_OFFSET_Y
,
DECK_OFFSET_X
,
DECK_OFFSET_Y
,
DECK_ROTATE_Z
,
DECK_CARD_HEIGHT
,
}
=
matConfig
;
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
,
OVERLAY
}
=
ygopro
.
CardZone
;
export
const
moveToDeck
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
report
:
boolean
;
})
=>
{
const
{
card
,
api
,
report
}
=
props
;
// report
const
{
zone
,
sequence
,
controller
,
xyzMonster
,
position
}
=
card
;
const
rightX
=
DECK_OFFSET_X
.
value
+
2
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
const
leftX
=
-
rightX
;
const
bottomY
=
DECK_OFFSET_Y
.
value
+
2
*
BLOCK_HEIGHT_M
.
value
+
BLOCK_HEIGHT_S
.
value
+
2
*
ROW_GAP
.
value
-
BLOCK_HEIGHT_S
.
value
;
const
topY
=
-
bottomY
;
let
x
=
isMe
(
controller
)
?
rightX
:
leftX
;
let
y
=
isMe
(
controller
)
?
bottomY
:
topY
;
if
(
zone
===
EXTRA
)
{
x
=
isMe
(
controller
)
?
leftX
:
rightX
;
}
let
rz
=
isMe
(
controller
)
?
180
-
DECK_ROTATE_Z
.
value
:
-
DECK_ROTATE_Z
.
value
;
if
(
zone
===
EXTRA
)
{
rz
=
isMe
(
controller
)
?
DECK_ROTATE_Z
.
value
:
DECK_ROTATE_Z
.
value
;
}
const
z
=
sequence
;
api
.
start
({
x
,
y
,
z
,
rz
,
zIndex
:
z
,
height
:
DECK_CARD_HEIGHT
.
value
,
});
};
src/ui/Duel/NewPlayMat/Card/springs/toField.ts
0 → 100644
View file @
4af2045b
import
{
isMe
,
type
CardType
}
from
"
@/stores
"
;
import
{
SpringApi
}
from
"
./types
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
PLANE_ROTATE_X
,
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
CARD_RATIO
,
COL_GAP
,
ROW_GAP
,
HAND_MARGIN_TOP
,
HAND_CARD_HEIGHT
,
HAND_CIRCLE_CENTER_OFFSET_Y
,
DECK_OFFSET_X
,
DECK_OFFSET_Y
,
DECK_ROTATE_Z
,
}
=
matConfig
;
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
,
OVERLAY
}
=
ygopro
.
CardZone
;
export
const
moveToField
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
report
:
boolean
;
})
=>
{
const
{
card
,
api
,
report
}
=
props
;
// report
const
{
zone
,
sequence
,
controller
,
xyzMonster
,
position
,
overlayMaterials
}
=
card
;
// 根据zone计算卡片的宽度
const
cardWidth
=
zone
===
SZONE
?
BLOCK_HEIGHT_S
.
value
*
CARD_RATIO
.
value
:
BLOCK_HEIGHT_M
.
value
*
CARD_RATIO
.
value
;
const
height
=
zone
===
SZONE
?
BLOCK_HEIGHT_S
.
value
:
BLOCK_HEIGHT_M
.
value
;
// 首先计算 x 和 y
let
x
=
0
,
y
=
0
;
switch
(
zone
)
{
case
SZONE
:
{
if
(
sequence
===
5
)
{
// 场地魔法
x
=
-
(
3
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
-
(
BLOCK_WIDTH
.
value
-
cardWidth
)
/
2
);
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
}
else
{
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
2
*
(
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
)
-
(
BLOCK_HEIGHT_M
.
value
-
BLOCK_HEIGHT_S
.
value
)
/
2
;
}
break
;
}
case
MZONE
:
{
if
(
sequence
>
4
)
{
// 额外怪兽区
x
=
(
sequence
>
5
?
1
:
-
1
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
0
;
}
else
{
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
}
break
;
}
case
OVERLAY
:
{
if
(
xyzMonster
)
{
const
{
sequence
}
=
xyzMonster
;
if
(
sequence
>
4
)
{
// 额外怪兽区
x
=
(
sequence
>
5
?
1
:
-
1
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
0
;
}
else
{
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
}
}
break
;
}
}
if
(
!
isMe
(
controller
))
{
x
=
-
x
;
y
=
-
y
;
}
await
asyncStart
(
api
)({
x
,
y
,
height
,
z
:
overlayMaterials
.
length
?
200
:
120
,
ry
:
[
ygopro
.
CardPosition
.
FACEDOWN
,
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
,
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
,
].
includes
(
position
??
5
)
?
180
:
0
,
rz
:
isMe
(
controller
)
?
0
:
180
,
config
:
{
// mass: 0.5,
easing
:
easings
.
easeOutSine
,
},
});
await
asyncStart
(
api
)({
z
:
0
,
zIndex
:
overlayMaterials
.
length
?
3
:
1
,
config
:
{
easing
:
easings
.
easeInSine
,
mass
:
5
,
tension
:
300
,
// 170
friction
:
12
,
// 26
clamp
:
true
,
},
});
};
src/ui/Duel/NewPlayMat/Card/springs/toHand.ts
0 → 100644
View file @
4af2045b
import
{
isMe
,
type
CardType
,
cardStore
}
from
"
@/stores
"
;
import
{
SpringApi
,
ReportEnum
}
from
"
./types
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
PLANE_ROTATE_X
,
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
CARD_RATIO
,
COL_GAP
,
ROW_GAP
,
HAND_MARGIN_TOP
,
HAND_CARD_HEIGHT
,
HAND_CIRCLE_CENTER_OFFSET_Y
,
DECK_OFFSET_X
,
DECK_OFFSET_Y
,
DECK_ROTATE_Z
,
}
=
matConfig
;
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
,
OVERLAY
}
=
ygopro
.
CardZone
;
export
const
moveToHand
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
report
:
boolean
;
})
=>
{
const
{
card
,
api
,
report
}
=
props
;
const
{
zone
,
sequence
,
controller
}
=
card
;
// 得刷新除了这个卡以外所有的自己的手卡
if
(
report
)
{
eventBus
.
emit
(
ReportEnum
.
ReloadHand
,
{
controller
,
sequence
,
});
}
// 手卡会有很复杂的计算...
const
hand_circle_center_x
=
0
;
const
hand_circle_center_y
=
1
*
BLOCK_HEIGHT_M
.
value
+
1
*
BLOCK_HEIGHT_S
.
value
+
2
*
ROW_GAP
.
value
+
(
HAND_MARGIN_TOP
.
value
+
HAND_CARD_HEIGHT
.
value
+
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
);
const
hand_card_width
=
CARD_RATIO
.
value
*
HAND_CARD_HEIGHT
.
value
;
const
THETA
=
2
*
Math
.
atan
(
hand_card_width
/
2
/
(
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
+
HAND_CARD_HEIGHT
.
value
)
)
*
0.9
;
// 接下来计算每一张手卡
const
hands_length
=
cardStore
.
at
(
HAND
,
controller
).
length
;
const
angle
=
(
sequence
-
(
hands_length
-
1
)
/
2
)
*
THETA
;
const
r
=
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
negativeX
=
Math
.
sin
(
angle
)
*
r
;
const
negativeY
=
Math
.
cos
(
angle
)
*
r
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
x
=
hand_circle_center_x
+
negativeX
*
(
isMe
(
controller
)
?
1
:
-
1
);
const
y
=
hand_circle_center_y
-
negativeY
+
130
;
// 常量 是手动调的 这里肯定有问题 有空来修
const
_rz
=
(
angle
*
180
)
/
Math
.
PI
;
api
.
start
({
x
:
isMe
(
controller
)
?
x
:
-
x
,
y
:
isMe
(
controller
)
?
y
:
-
y
,
rz
:
isMe
(
controller
)
?
_rz
:
180
-
_rz
,
height
:
HAND_CARD_HEIGHT
.
value
,
// rx: -PLANE_ROTATE_X.value,
});
};
src/ui/Duel/NewPlayMat/Card/springs/toOutside.ts
0 → 100644
View file @
4af2045b
import
{
isMe
,
type
CardType
}
from
"
@/stores
"
;
import
{
SpringApi
}
from
"
./types
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
PLANE_ROTATE_X
,
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
CARD_RATIO
,
COL_GAP
,
ROW_GAP
,
HAND_MARGIN_TOP
,
HAND_CARD_HEIGHT
,
HAND_CIRCLE_CENTER_OFFSET_Y
,
DECK_OFFSET_X
,
DECK_OFFSET_Y
,
DECK_ROTATE_Z
,
}
=
matConfig
;
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
,
OVERLAY
}
=
ygopro
.
CardZone
;
export
const
moveToOutside
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
report
:
boolean
;
})
=>
{
const
{
card
,
api
,
report
}
=
props
;
// report
const
{
zone
,
sequence
,
controller
,
xyzMonster
,
position
,
overlayMaterials
}
=
card
;
let
x
=
0
,
y
=
0
;
if
(
zone
===
GRAVE
)
{
x
=
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
*
3
;
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
}
else
if
(
zone
===
REMOVED
)
{
x
=
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
*
2
;
}
if
(
!
isMe
(
controller
))
{
x
=
-
x
;
y
=
-
y
;
}
api
.
start
({
x
,
y
,
z
:
0
,
rz
:
isMe
(
controller
)
?
0
:
180
,
});
};
src/ui/Duel/NewPlayMat/Card/springs/types.ts
0 → 100644
View file @
4af2045b
import
{
type
SpringValue
,
type
SpringRef
}
from
"
@react-spring/web
"
;
export
type
SpringApi
=
SpringRef
<
{
x
:
number
;
y
:
number
;
z
:
number
;
rx
:
number
;
ry
:
number
;
rz
:
number
;
zIndex
:
number
;
height
:
number
;
}
>
;
export
enum
ReportEnum
{
ReloadHand
=
"
reload-hand
"
,
}
src/ui/Duel/NewPlayMat/Card/springs/utils.ts
0 → 100644
View file @
4af2045b
import
{
type
SpringRef
,
type
SpringConfig
}
from
"
@react-spring/web
"
;
export
const
asyncStart
=
<
T
extends
{}
>
(
api
:
SpringRef
<
T
>
)
=>
{
return
(
p
:
Partial
<
T
>
&
{
config
:
SpringConfig
})
=>
new
Promise
((
resolve
)
=>
{
api
.
start
({
...
p
,
onRest
:
resolve
,
});
});
};
src/ui/Duel/NewPlayMat/Mat/index.scss
View file @
4af2045b
section
#mat
{
margin-top
:
200px
;
//
margin-top: 200px;
// padding-top: 50px; // 先不管 后面调整
position
:
relative
;
#camera
{
...
...
@@ -8,12 +8,26 @@ section#mat {
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
perspective
:
var
(
--
perspective
);
//
perspective: var(--perspective);
}
#plane
{
transform
:
translateX
(
0
)
translateY
(
0
)
translateZ
(
0
)
rotateX
(
var
(
--
plane-rotate-x
));
width
:
fit-content
;
transform-style
:
preserve-3d
;
perspective
:
var
(
--
perspective
);
}
}
.mat-card-container
{
position
:
absolute
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
100%
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
transform-style
:
preserve-3d
;
}
src/ui/Duel/NewPlayMat/Mat/index.tsx
View file @
4af2045b
...
...
@@ -22,9 +22,11 @@ export const Mat: FC = () => {
>
<
Plane
>
<
Bg
/>
<
CardContainer
>
{
snap
.
map
((
cardSnap
,
i
)
=>
(
<
Card
key=
{
i
}
idx=
{
i
}
/>
))
}
</
CardContainer
>
</
Plane
>
</
section
>
);
...
...
@@ -35,3 +37,7 @@ const Plane: FC<PropsWithChildren> = ({ children }) => (
<
div
id=
"plane"
>
{
children
}
</
div
>
</
div
>
);
const
CardContainer
:
FC
<
PropsWithChildren
>
=
({
children
})
=>
(
<
div
className=
"mat-card-container"
>
{
children
}
</
div
>
);
src/ui/Duel/NewPlayMat/utils/cssConfig.ts
View file @
4af2045b
...
...
@@ -76,4 +76,8 @@ export const matConfig = {
value
:
30
,
unit
:
UNIT
.
DEG
,
},
DECK_CARD_HEIGHT
:
{
value
:
120
,
unit
:
UNIT
.
PX
,
},
};
src/ui/Duel/Test.tsx
deleted
100644 → 0
View file @
dcf03b35
import
{
cardStore
}
from
"
@/stores
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
subscribeKey
,
watch
}
from
"
valtio/utils
"
;
import
{
FC
,
memo
,
useEffect
,
useState
}
from
"
react
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
useSpring
,
SpringValue
,
animated
,
useSpringRef
,
}
from
"
@react-spring/web
"
;
export
const
Test
=
()
=>
{
const
snap
=
useSnapshot
(
cardStore
.
inner
);
return
(
<
div
style=
{
{
background
:
"
white
"
,
position
:
"
fixed
"
,
left
:
0
,
top
:
0
,
color
:
"
black
"
,
zIndex
:
9999
,
fontSize
:
12
,
}
}
>
{
snap
.
map
((
cardState
,
i
)
=>
(
<
Card
idx=
{
i
}
key=
{
i
}
show=
{
[
ygopro
.
CardZone
.
HAND
,
ygopro
.
CardZone
.
MZONE
,
ygopro
.
CardZone
.
SZONE
,
ygopro
.
CardZone
.
GRAVE
,
].
includes
(
cardState
.
zone
)
}
/>
))
}
</
div
>
);
};
export
const
Card
:
FC
<
{
idx
:
number
;
show
:
boolean
}
>
=
memo
(
({
idx
,
show
})
=>
{
const
snap
=
useSnapshot
(
cardStore
.
inner
[
idx
]);
const
api
=
useSpringRef
();
const
props
=
useSpring
({
ref
:
api
,
from
:
{
x
:
0
},
});
// subscribeKey(cardStore.inner[idx], "zone", (value) => {
// api.start({
// to: {
// x: value * 100,
// },
// });
// });
watch
((
get
)
=>
{
get
(
cardStore
.
inner
[
idx
]);
const
zone
=
get
(
cardStore
.
inner
[
idx
]).
zone
;
api
.
start
({
to
:
{
x
:
zone
*
100
,
},
});
});
return
show
?
(
<
animated
.
div
style=
{
{
transform
:
props
.
x
.
to
((
value
)
=>
`translateX(${value}px)`
),
background
:
"
white
"
,
}
}
>
<
div
>
code:
{
snap
.
code
}
</
div
>
<
div
>
{
(
Math
.
random
()
*
100
).
toFixed
(
0
)
}
</
div
>
</
animated
.
div
>
)
:
(
<></>
);
},
(
prev
,
next
)
=>
prev
.
show
===
next
.
show
// 只有 show 变化时才会重新渲染
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment