3D 硬币翻转组件(CSS 圆柱硬币抛掷动画,时长/连抛可调)
3D 硬币翻转组件:CSS preserve-3d 构建带侧面分段的正反面硬币,JS 随机抛掷 4-6 圈并判定正反面,翻转时长与自动连抛可调,自研实现零依赖零图片。
效果演示(真实可交互)
点击「抛硬币」开始
完整代码
硬币由三部分组成:正面圆面、背面圆面(rotateY(180deg) 翻转)、以及 20 个围绕圆周分布的小矩形拼成的圆柱侧面。抛掷时用一个带减速曲线的 CSS transition 把累计角度一次性推进 4-6 整圈加 0/180°,动画结束后按最终角度取模判定正反。
<div class="scene">
<div class="coin" id="coin">
<div class="face face--front"><b>¥</b><span>正面</span></div>
<div class="face face--back"><b>★</b><span>反面</span></div>
<div class="edge"><span style="--i:0"></span><span style="--i:1"></span></div>
</div>
</div>
.scene { perspective: 900px; display: flex; align-items: center; justify-content: center; height: 340px; }
.coin {
position: relative; width: 132px; height: 132px;
transform-style: preserve-3d;
}
.face {
position: absolute; inset: 0; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
backface-visibility: hidden;
}
.face--front { background: radial-gradient(circle at 35% 30%, #ffe9a8, #f5b83d 55%, #c98a16); transform: translateZ(6px); }
.face--back { background: radial-gradient(circle at 35% 30%, #e8eef8, #b9c4d6 55%, #8fa0b8); transform: rotateY(180deg) translateZ(6px); }
.edge { position: absolute; left: 50%; top: 50%; width: 0; height: 0; transform-style: preserve-3d; }
.edge span {
position: absolute; left: -11px; top: -7px; width: 22px; height: 14px;
background: linear-gradient(90deg, #e8c15a, #a87a1e);
transform: rotateY(calc(var(--i) * 18deg)) translateZ(66px); /* 20 段 × 18° */
}
let rot = 0;
function toss() {
const spins = 4 + Math.floor(Math.random() * 3); // 4-6 整圈
const side = Math.round(Math.random()); // 0 正 / 1 反
const target = rot + spins * 360 + (side ? 180 : 0);
coin.style.transition = 'transform 1200ms cubic-bezier(.23,.7,.3,1)';
coin.style.transform = 'rotateY(' + target + 'deg)';
setTimeout(() => {
const mod = ((target % 360) + 360) % 360;
result.textContent = mod < 180 ? '🎉 正面' : '🎉 反面';
rot = target;
}, 1260);
}
参数说明
| 参数 | 位置 | 说明 |
|---|---|---|
| 翻转时长 | transition 的 1200ms | 演示中 600-2200ms 可调 |
| 抛掷圈数 | spins 随机 4-6 | 圈数越多翻转越「猛」 |
| 结果判定 | target % 360 < 180 | 最终角度决定正反面 |
| 硬币厚度 | .edge span 高度 | 分段高度即硬币侧面厚度 |
| 自动连抛 | setInterval | 每 2.2s 自动抛一次 |
兼容性与性能
- 硬币为 2 个圆面 + 20 个侧面矩形,共 22 个元素,GPU 合成毫无压力
- 抛掷动画由 CSS transition 驱动,浏览器接管帧调度,省电且不掉帧
- 侧面分段数量可自由增减:段数越多越圆滑,20 段在 132px 硬币上观感已足够
- Chrome / Safari / Firefox / Edge 全支持;iOS Safari 注意保留
-webkit-backface-visibility: hidden - 结果判定基于目标角度而非动画过程,不受掉帧影响,判定永远准确
- 无障碍考量:抛掷结果以文本形式同步输出,读屏器可感知
常见问题
Q: 硬币看起来是「纸片」而不是立体硬币? 侧面分段缺失或高度太小都会导致此现象。确认 .edge span 的 translateZ 与硬币半径一致(132px 硬币为 66px),且分段数量 ≥ 12。
Q: 翻转后正反判定和视觉不一致? 判定公式 target % 360 在负角度时会出错,务必先加 360 取模(((x % 360) + 360) % 360)。此外请勿在 transition 未结束时连续抛掷,演示中已用 busy 标志互斥。
Q: 想让硬币朝上/朝下抛(绕 X 轴)怎么改? 把 rotateY 全部换成 rotateX,同时把侧面分段的分布轴从 Y 改为 X(rotateX(i × 18deg) translateZ(66px)),即可得到纵向翻转的硬币。
Q: 可以商用吗? 可以。本组件为粤喵自研原创代码,免费商用无需署名,币面文字、颜色、抛掷节奏均可自由定制。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。