html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="./css/style.css"> <title>抛硬币</title> </head> <body> <button class="tip-button"> <span class="tip-button__text">遇事不决,抛一下</span> <div class="coin-wrapper"> <div class="coin" style=" --coin-y-multiplier: 0; --coin-x-multiplier: 0; --coin-scale-multiplier: 0; --coin-rotation-multiplier: 0; --front-scale-multiplier: 1; --front-y-multiplier: -4.898587196589413e-16; --middle-scale-multiplier: 1; --middle-y-multiplier: 6.123233995736766e-17; --back-scale-multiplier: 0; --back-y-multiplier: 3.6739403974420594e-16; --shine-opacity-multiplier: 0.4; --shine-bg-multiplier: 50%; opacity: 1; "> <div class="coin__middle"></div> <div class="coin__back"></div> <div class="coin__front"></div> </div> </div> </button> <script src="./js/script.js"></script> </body> </html>
css
.tip-button { background: none; border: 0; /* border-radius: 0.25rem 0.25rem 0 0; */ cursor: pointer; font-family: "Quicksand", sans-serif; font-size: 0.75rem; font-weight: 600; height: 2.6rem; margin-bottom: -4rem; outline: 0; position: relative; top: 0; transform-origin: 0% 100%; transition: transform 50ms ease-in-out; width: 9.5rem; -webkit-tap-highlight-color: transparent; } /* 按钮滚动 */ .tip-button:active { transform: rotate(4deg); } .tip-button.clicked { animation: 150ms ease-in-out 1 shake; pointer-events: none; } .tip-button.clicked .tip-button__text { opacity: 0; transition: opacity 100ms linear 200ms; } .tip-button.clicked::before { height: 0.5rem; width: 60%; } .tip-button.clicked .coin { transition: margin-bottom 1s linear 200ms; margin-bottom: 0; } .tip-button.shrink-landing::before { transition: width 200ms ease-in; width: 0; } .tip-button.coin-landed::after { opacity: 1; transform: scale(1); transform-origin: 50% 100%; } .tip-button.coin-landed .coin-wrapper { background: radial-gradient(circle at 35% 97%, rgba(3, 16, 50, 0.4) 0.04rem, transparent 0.04rem), radial-gradient(circle at 45% 92%, rgba(3, 16, 50, 0.4) 0.04rem, transparent 0.02rem), radial-gradient(circle at 55% 98%, rgba(3, 16, 50, 0.4) 0.04rem, transparent 0.04rem), radial-gradient(circle at 65% 96%, rgba(3, 16, 50, 0.4) 0.06rem, transparent 0.06rem); background-position: center bottom; background-size: 100%; bottom: -1rem; opacity: 0; transform: scale(2) translateY(-10px); } .tip-button__text { color: #fff; margin-right: 1.8rem; opacity: 1; position: relative; transition: opacity 100ms linear 500ms; z-index: 3; } .tip-button::before { background: #031032; border-radius: 0.25rem; bottom: 0; content: ""; display: block; height: 100%; left: 50%; position: absolute; transform: translateX(-50%); transition: height 250ms ease-in-out 400ms, width 250ms ease-in-out 300ms; width: 100%; z-index: 2; } .tip-button::after { bottom: -1rem; color: #031032; content: attr(content); height: 110%; left: 0; opacity: 0; position: absolute; pointer-events: none; text-align: center; transform: scale(0); transform-origin: 50% 20%; transition: transform 200ms cubic-bezier(0, 0, 0.35, 1.43); width: 100%; z-index: 1; } .coin-wrapper { background: none; bottom: 0; height: 18rem; left: 0; opacity: 1; overflow: hidden; pointer-events: none; position: absolute; transform: none; transform-origin: 50% 100%; transition: opacity 200ms linear 100ms, transform 300ms ease-out; width: 100%; } .coin { --front-y-multiplier: 0; --back-y-multiplier: 0; --coin-y-multiplier: 0; --coin-x-multiplier: 0; --coin-scale-multiplier: 0; --coin-rotation-multiplier: 0; --shine-opacity-multiplier: 0.4; --shine-bg-multiplier: 50%; bottom: calc(var(--coin-y-multiplier) * 1rem - 3.5rem); height: 3.5rem; margin-bottom: 3.05rem; position: absolute; right: calc(var(--coin-x-multiplier) * 34% + 16%); transform: translateX(50%) scale(calc(0.4 + var(--coin-scale-multiplier))) rotate(calc(var(--coin-rotation-multiplier) * -1deg)); transition: opacity 100ms linear 200ms; width: 3.5rem; z-index: 3; } .coin__front, .coin__middle, .coin__back, .coin::before, .coin__front::after, .coin__back::after { border-radius: 50%; box-sizing: border-box; height: 100%; left: 0; position: absolute; width: 100%; z-index: 3; } .coin__front { background: radial-gradient(circle at 50% 50%, transparent 50%, rgba(115, 124, 153, 0.4) 54%, #c2cadf 54%), linear-gradient(210deg, #8590b3 32%, transparent 32%), linear-gradient(150deg, #8590b3 32%, transparent 32%), linear-gradient(to right, #8590b3 22%, transparent 22%, transparent 78%, #8590b3 78%), linear-gradient(to bottom, #fcfaf9 44%, transparent 44%, transparent 65%, #fcfaf9 65%, #fcfaf9 71%, #8590b3 71%), linear-gradient(to right, transparent 28%, #fcfaf9 28%, #fcfaf9 34%, #8590b3 34%, #8590b3 40%, #fcfaf9 40%, #fcfaf9 47%, #8590b3 47%, #8590b3 53%, #fcfaf9 53%, #fcfaf9 60%, #8590b3 60%, #8590b3 66%, #fcfaf9 66%, #fcfaf9 72%, transparent 72%); background-color: #8590b3; background-size: 100% 100%; transform: translateY(calc(var(--front-y-multiplier) * 0.3181818182rem / 2)) scaleY(var(--front-scale-multiplier)); } .coin__front::after { background: rgba(0, 0, 0, 0.2); content: ""; opacity: var(--front-y-multiplier); } .coin__middle { background: #737c99; transform: translateY(calc(var(--middle-y-multiplier) * 0.3181818182rem / 2)) scaleY(var(--middle-scale-multiplier)); } .coin__back { background: radial-gradient(circle at 50% 50%, transparent 50%, rgba(115, 124, 153, 0.4) 54%, #c2cadf 54%), radial-gradient(circle at 50% 40%, #fcfaf9 23%, transparent 23%), radial-gradient(circle at 50% 100%, #fcfaf9 35%, transparent 35%); background-color: #8590b3; background-size: 100% 100%; transform: translateY(calc(var(--back-y-multiplier) * 0.3181818182rem / 2)) scaleY(var(--back-scale-multiplier)); } .coin__back::after { background: rgba(0, 0, 0, 0.2); content: ""; opacity: var(--back-y-multiplier); } .coin::before { background: radial-gradient(circle at 25% 65%, transparent 50%, rgba(255, 255, 255, 0.9) 90%), linear-gradient(55deg, transparent calc(var(--shine-bg-multiplier) + 0%), #e9f4ff calc(var(--shine-bg-multiplier) + 0%), transparent calc(var(--shine-bg-multiplier) + 50%)); content: ""; opacity: var(--shine-opacity-multiplier); transform: translateY(calc(var(--middle-y-multiplier) * 0.3181818182rem / -2)) scaleY(var(--middle-scale-multiplier)) rotate(calc(var(--coin-rotation-multiplier) * 1deg)); z-index: 10; } .coin::after { background: #737c99; content: ""; height: 0.3181818182rem; left: 0; position: absolute; top: 50%; transform: translateY(-50%); width: 100%; z-index: 2; } @keyframes shake { 0% { transform: rotate(4deg); } 66% { transform: rotate(-4deg); } 100% { transform: rotate(); } } /********* BODY STYLES *********/ html, body { height: 100%; } body { align-items: center; background: #f4f7ff; display: flex; justify-content: center; -webkit-font-smoothing: antialiased; }
js
const tipButtons = document.querySelectorAll(".tip-button"); // Loop through all buttons (allows for multiple buttons on page) //循环浏览所有按钮(允许页面上有多个按钮) tipButtons.forEach((button) => { let coin = button.querySelector(".coin"); //数字越大,动画越慢 coin.maxMoveLoopCount = 90; // 为button绑定click事件 button.addEventListener("click", () => { const f1 = () => parseInt(Math.random() * 5) + 1; // 等概率发生器 const f2 = () => { let c = 0; do { c = f1(); } while (c === 3); // 1、2 => 0 4、5 => 1 return c > 3 ? true : false; }; const flag = f2(); if (flag) { button.setAttribute("content", "正面"); } else { button.setAttribute("content", "反面"); } // 如果正在被点击 if (button.clicked) return; // 新增clicked的class button.classList.add("clicked"); // Wait to start flipping th coin because of the button tilt animation setTimeout(() => { // 定义随机反转的速度 coin.sideRotationCount = Math.floor(Math.random() * 5) * 90; // 最大翻转角度 coin.maxFlipAngle = (Math.floor(Math.random() * 4) + 3) * Math.PI; button.clicked = true; flipCoin(); }, 50); }); const flipCoin = () => { coin.moveLoopCount = 0; // 移动循环计数 flipCoinLoop(); }; const flipCoinLoop = () => { coin.moveLoopCount++; let percentageCompleted = coin.moveLoopCount / coin.maxMoveLoopCount; // 获取投掷角度 coin.angle = -coin.maxFlipAngle * Math.pow(percentageCompleted - 1, 2) + coin.maxFlipAngle; // Calculate the scale and position of the coin moving through the air // 计算硬币在空中移动的比例和位置 coin.style.setProperty( "--coin-y-multiplier", -11 * Math.pow(percentageCompleted * 2 - 1, 4) + 11 ); coin.style.setProperty("--coin-x-multiplier", percentageCompleted); coin.style.setProperty( "--coin-scale-multiplier", percentageCompleted * 0.6 ); coin.style.setProperty( "--coin-rotation-multiplier", percentageCompleted * coin.sideRotationCount ); // Calculate the scale and position values for the different coin faces // The math uses sin/cos wave functions to similate the circular motion of 3D spin //计算不同硬币表面的比例和位置值 //数学使用sin/cos波函数来模拟三维自旋的圆周运动 // 前面样式 coin.style.setProperty( "--front-scale-multiplier", Math.max(Math.cos(coin.angle), 0) ); // console.log("===== : 前面", Math.sin(coin.angle)); coin.style.setProperty("--front-y-multiplier", Math.sin(coin.angle)); // 中间样式 coin.style.setProperty( "--middle-scale-multiplier", Math.abs(Math.cos(coin.angle), 0) ); coin.style.setProperty( "--middle-y-multiplier", Math.cos((coin.angle + Math.PI / 2) % Math.PI) ); // 背面样式 coin.style.setProperty( "--back-scale-multiplier", Math.max(Math.cos(coin.angle - Math.PI), 0) ); // console.log("===== : 背面", Math.sin(coin.angle - Math.PI)); coin.style.setProperty( "--back-y-multiplier", Math.sin(coin.angle - Math.PI) ); // 计算光照样式 coin.style.setProperty( "--shine-opacity-multiplier", 4 * Math.sin((coin.angle + Math.PI / 2) % Math.PI) - 3.2 ); coin.style.setProperty( "--shine-bg-multiplier", -40 * (Math.cos((coin.angle + Math.PI / 2) % Math.PI) - 0.5) + "%" ); // Repeat animation loop if (coin.moveLoopCount < coin.maxMoveLoopCount) { if (coin.moveLoopCount === coin.maxMoveLoopCount - 6) button.classList.add("shrink-landing"); window.requestAnimationFrame(flipCoinLoop); } else { button.classList.add("coin-landed"); coin.style.setProperty("opacity", 0); setTimeout(() => { button.classList.remove("clicked", "shrink-landing", "coin-landed"); setTimeout(() => { resetCoin(); }, 300); }, 1500); } }; // 重置按钮 const resetCoin = () => { coin.style.setProperty("--coin-x-multiplier", 0); coin.style.setProperty("--coin-scale-multiplier", 0); coin.style.setProperty("--coin-rotation-multiplier", 0); coin.style.setProperty("--shine-opacity-multiplier", 0.4); coin.style.setProperty("--shine-bg-multiplier", "50%"); coin.style.setProperty("opacity", 1); // Delay to give the reset animation some time before you can click again setTimeout(() => { button.clicked = false; }, 300); }; });