- 引入 light-animal-sprite 图像资源和 variant 属性支持 - 在动物组件中实现正常和浅色精灵图像切换功能 - 重构桌面和移动版动物组件的揭示动画逻辑 - 移除冗余的运动偏好检测和布局效果钩子 - 调整揭示动画的时间参数和平滑度曲线 - 更新资源预加载配置以包含浅色动物图像 - 修改控制组件中的投注数量调整状态管理 - 优化脚本生成精灵图像的复用性
269 lines
7.6 KiB
Markdown
269 lines
7.6 KiB
Markdown
# 结算过程金色旋转边框样式与动画
|
||
|
||
本文档保存当前 PC 端与移动端在开奖/结算过程里使用的金色边框、旋转描边和发光动画,便于后续下载、复用或对接设计标注。
|
||
|
||
## 源码位置
|
||
|
||
- PC 端挂载位置:`src/features/game/components/desktop/desktop-animal.tsx`
|
||
- 移动端挂载位置:`src/features/game/components/mobile/mobile-animal.tsx`
|
||
- 样式定义位置:`src/style/index.css`
|
||
|
||
PC 端在 `revealFrame` 存在时渲染:
|
||
|
||
```tsx
|
||
<div className="gold-reveal-glow rounded-[calc(var(--design-unit)*16)]" />
|
||
<div className="gold-reveal-static-border rounded-[calc(var(--design-unit)*16)]" />
|
||
<div className="gold-reveal-shell rounded-[calc(var(--design-unit)*16)]" />
|
||
```
|
||
|
||
移动端在 `revealFrame` 存在时渲染:
|
||
|
||
```tsx
|
||
<div className="mobile-gold-reveal-glow z-10 rounded-[calc(var(--design-unit)*7)]" />
|
||
<div className="mobile-gold-reveal-static-border z-20 rounded-[calc(var(--design-unit)*7)]" />
|
||
<div className="mobile-gold-reveal-shell z-20 rounded-[calc(var(--design-unit)*7)]" />
|
||
```
|
||
|
||
## 动画机制
|
||
|
||
- `--gold-angle`:注册为 CSS angle 自定义属性,用于驱动 `conic-gradient` 旋转。
|
||
- `rotating-gold-border`:把 `--gold-angle` 从 `0deg` 推到 `360deg`,形成边框旋转。
|
||
- `gold-border-pulse`:让外层金色光晕做轻微透明度和缩放呼吸。
|
||
- `::before`:主金色旋转边框,8 秒一圈。
|
||
- `::after`:高光扫光边框,2.1 秒一圈,并从反向角度偏移 `180deg` 开始。
|
||
- `static-border`:静态金色实体边框,保证边框厚度和底色稳定。
|
||
- `glow`:外部径向金色光晕,增强结算聚焦感。
|
||
|
||
## PC 端样式
|
||
|
||
```css
|
||
@property --gold-angle {
|
||
syntax: "<angle>";
|
||
inherits: false;
|
||
initial-value: 0deg;
|
||
}
|
||
|
||
@keyframes rotating-gold-border {
|
||
to {
|
||
--gold-angle: 360deg;
|
||
}
|
||
}
|
||
|
||
@keyframes gold-border-pulse {
|
||
0%,
|
||
100% {
|
||
opacity: 0.34;
|
||
transform: scale(0.992);
|
||
}
|
||
|
||
50% {
|
||
opacity: 0.54;
|
||
transform: scale(1.008);
|
||
}
|
||
}
|
||
|
||
.gold-reveal-shell {
|
||
--gold-angle: 0deg;
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 2.6);
|
||
border-radius: calc(var(--design-unit) * 16);
|
||
overflow: hidden;
|
||
clip-path: inset(0 round calc(var(--design-unit) * 16));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.gold-reveal-shell::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: calc(var(--design-unit) * 16);
|
||
padding: calc(var(--design-unit) * 5.6);
|
||
background: conic-gradient(
|
||
from var(--gold-angle),
|
||
#534217 10%,
|
||
#534217 20%,
|
||
#ffe226 45%,
|
||
#534217 60%,
|
||
#534217 85%,
|
||
#ffe226 95%,
|
||
#534217 100%
|
||
);
|
||
-webkit-mask:
|
||
linear-gradient(#fff 0 0) content-box,
|
||
linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
animation: rotating-gold-border 8s linear infinite;
|
||
}
|
||
|
||
.gold-reveal-shell::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 1);
|
||
border-radius: calc(var(--design-unit) * 15);
|
||
padding: calc(var(--design-unit) * 4);
|
||
background: conic-gradient(
|
||
from calc(var(--gold-angle) + 180deg),
|
||
rgba(255, 247, 210, 0) 0%,
|
||
rgba(255, 247, 210, 0) 66%,
|
||
rgba(255, 252, 232, 0.14) 71%,
|
||
rgba(255, 254, 244, 0.98) 75%,
|
||
rgba(255, 230, 130, 0.96) 79%,
|
||
rgba(255, 247, 210, 0.18) 84%,
|
||
rgba(255, 247, 210, 0) 90%,
|
||
rgba(255, 247, 210, 0) 100%
|
||
);
|
||
-webkit-mask:
|
||
linear-gradient(#fff 0 0) content-box,
|
||
linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
animation: rotating-gold-border 2.1s linear infinite;
|
||
}
|
||
|
||
.gold-reveal-static-border {
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 2.8);
|
||
border-radius: calc(var(--design-unit) * 16);
|
||
border: calc(var(--design-unit) * 7.4) solid rgba(181, 138, 40, 0.98);
|
||
box-shadow:
|
||
inset 0 0 calc(var(--design-unit) * 14) rgba(255, 241, 181, 0.38),
|
||
0 0 calc(var(--design-unit) * 18) rgba(255, 210, 102, 0.32);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.gold-reveal-glow {
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 1.4);
|
||
border-radius: calc(var(--design-unit) * 18);
|
||
background: radial-gradient(
|
||
circle at center,
|
||
rgba(255, 226, 92, 0.18) 0%,
|
||
rgba(219, 161, 42, 0.14) 42%,
|
||
rgba(127, 86, 13, 0.08) 62%,
|
||
transparent 82%
|
||
);
|
||
filter: blur(calc(var(--design-unit) * 2.8));
|
||
opacity: 0.36;
|
||
pointer-events: none;
|
||
animation: gold-border-pulse 1.9s ease-in-out infinite;
|
||
}
|
||
```
|
||
|
||
## 移动端样式
|
||
|
||
```css
|
||
.mobile-gold-reveal-shell {
|
||
--gold-angle: 0deg;
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 2.2);
|
||
border-radius: calc(var(--design-unit) * 7);
|
||
overflow: hidden;
|
||
clip-path: inset(0 round calc(var(--design-unit) * 7));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.mobile-gold-reveal-shell::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: calc(var(--design-unit) * 7);
|
||
padding: calc(var(--design-unit) * 2.5);
|
||
background: conic-gradient(
|
||
from var(--gold-angle),
|
||
#534217 10%,
|
||
#534217 20%,
|
||
#ffe226 45%,
|
||
#534217 60%,
|
||
#534217 85%,
|
||
#ffe226 95%,
|
||
#534217 100%
|
||
);
|
||
-webkit-mask:
|
||
linear-gradient(#fff 0 0) content-box,
|
||
linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
animation: rotating-gold-border 8s linear infinite;
|
||
}
|
||
|
||
.mobile-gold-reveal-shell::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 0.5);
|
||
border-radius: calc(var(--design-unit) * 6.5);
|
||
padding: calc(var(--design-unit) * 1.7);
|
||
background: conic-gradient(
|
||
from calc(var(--gold-angle) + 180deg),
|
||
rgba(255, 247, 210, 0) 0%,
|
||
rgba(255, 247, 210, 0) 66%,
|
||
rgba(255, 247, 210, 0.14) 71%,
|
||
rgba(255, 254, 244, 0.98) 75%,
|
||
rgba(255, 230, 130, 0.96) 79%,
|
||
rgba(255, 247, 210, 0.18) 84%,
|
||
rgba(255, 247, 210, 0) 90%,
|
||
rgba(255, 247, 210, 0) 100%
|
||
);
|
||
-webkit-mask:
|
||
linear-gradient(#fff 0 0) content-box,
|
||
linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
animation: rotating-gold-border 2.1s linear infinite;
|
||
}
|
||
|
||
.mobile-gold-reveal-static-border {
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 2.7);
|
||
border-radius: calc(var(--design-unit) * 7);
|
||
border: calc(var(--design-unit) * 2.8) solid rgba(181, 138, 40, 0.98);
|
||
box-shadow:
|
||
inset 0 0 calc(var(--design-unit) * 7) rgba(255, 241, 181, 0.38),
|
||
0 0 calc(var(--design-unit) * 9) rgba(255, 210, 102, 0.32);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.mobile-gold-reveal-glow {
|
||
position: absolute;
|
||
inset: calc(var(--design-unit) * 1.4);
|
||
border-radius: calc(var(--design-unit) * 8);
|
||
background: radial-gradient(
|
||
circle at center,
|
||
rgba(255, 226, 92, 0.2) 0%,
|
||
rgba(219, 161, 42, 0.16) 42%,
|
||
rgba(127, 86, 13, 0.08) 62%,
|
||
transparent 82%
|
||
);
|
||
filter: blur(calc(var(--design-unit) * 1.4));
|
||
opacity: 0.42;
|
||
pointer-events: none;
|
||
animation: gold-border-pulse 1.9s ease-in-out infinite;
|
||
}
|
||
```
|
||
|
||
## 减少动画模式
|
||
|
||
组件中已经接入 `useReducedMotion()`。当用户系统设置减少动态效果时,PC 与移动端都会把旋转和呼吸动画关闭:
|
||
|
||
```tsx
|
||
style={prefersReducedMotion ? { animation: 'none' } : undefined}
|
||
```
|
||
|
||
光晕层在减少动画模式下固定为:
|
||
|
||
```tsx
|
||
style={{
|
||
animation: 'none',
|
||
opacity: 0.36,
|
||
transform: 'scale(1)',
|
||
}}
|
||
```
|
||
|
||
## 复用注意事项
|
||
|
||
- 父容器必须是 `position: relative`,三层边框节点必须是绝对定位。
|
||
- 需要提供 `--design-unit`,当前项目用它控制 PC 与移动端等比缩放。
|
||
- `@property --gold-angle` 是旋转效果的关键;如果目标浏览器不支持 `@property`,可改用 `transform: rotate()` 方案,但需要额外包一层元素。
|
||
- 移动端额外叠加了 `animalBorderImage` 图片边框,CSS 金色旋转边框位于图片边框下方,增强结算选中效果。
|
||
|