使用html+javascript實(shí)現(xiàn)金幣落袋效果實(shí)例代碼
如圖:

金幣飛入頂部金幣欄的動(dòng)效,支持多金幣同時(shí)飛、帶拋物線 + 3D 旋轉(zhuǎn) + 落地反饋
要讓金幣落袋效果更逼真,我們可以從物理軌跡(拋物線 / 貝塞爾曲線)、細(xì)節(jié)反饋、音效與特效、性能優(yōu)化這幾個(gè)維度入手,把 “簡單的直線飛” 升級成 “像游戲里一樣的真實(shí)感動(dòng)畫”
- 拋物線軌跡:金幣飛出時(shí)先向上 “拋” 一下,再落向目標(biāo),模擬重力和初速度。
- 隨機(jī)偏移:每個(gè)金幣的起點(diǎn) / 落點(diǎn)有微小偏差,避免 “復(fù)制粘貼式” 的僵硬感。
- 初速度與加速度:前半段快,后半段減速,模擬空氣阻力。
- 3D 旋轉(zhuǎn) + 透視:金幣在飛行中繞不同軸旋轉(zhuǎn),配合大小變化模擬近大遠(yuǎn)小。
- 光影變化:飛行時(shí)的亮度 / 透明度隨角度變化,模擬光照。
- 尾跡 / 拖影:短暫的淡金色尾跡,強(qiáng)化速度感。
- 落地反饋:金幣到達(dá)目標(biāo)時(shí),錢袋 / 數(shù)字有輕微的 “抖動(dòng) + 放大” 效果,增強(qiáng)交互反饋。
音效與震動(dòng)
- 金幣飛行的 “咻” 聲、落地的 “叮” 聲。
- 移動(dòng)端可以加輕微震動(dòng),強(qiáng)化 “落袋成功” 的爽感。
性能與體驗(yàn)
- 金幣復(fù)用池:避免頻繁創(chuàng)建 / 銷毀 DOM,提升流暢度。
- 限制最大同時(shí)動(dòng)畫數(shù)量,防止卡頓。
- 響應(yīng)式適配,不同屏幕下軌跡不跑偏。
核心代碼:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>超逼真金幣落袋效果</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background: #1a1a4e;
overflow: hidden;
user-select: none;
position: relative;
}
/* 頂部金幣UI */
.top-bar {
position: fixed;
top: 20px;
left: 0;
width: 100%;
display: flex;
align-items: center;
gap: 20px;
padding: 0 20px;
z-index: 10;
}
.coin-box {
display: flex;
align-items: center;
background: rgba(255,255,255,0.1);
border-radius: 999px;
padding: 6px 12px;
gap: 8px;
transition: transform 0.1s ease;
}
.coin-box:active {
transform: scale(0.95);
}
.coin-icon {
width: 36px;
height: 36px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ffdf4f, #ffb700);
box-shadow: 0 0 8px #ffd000;
}
.coin-count {
color: white;
font-size: 28px;
font-weight: bold;
font-family: sans-serif;
transition: transform 0.15s ease;
}
/* 飛行動(dòng)畫金幣 */
.flying-coin {
position: fixed;
width: 24px;
height: 24px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ffdf4f, #ffb700);
box-shadow: 0 0 10px #ffd000;
pointer-events: none;
z-index: 999;
/* 3D旋轉(zhuǎn)關(guān)鍵屬性 */
transform-style: preserve-3d;
perspective: 500px;
}
/* 點(diǎn)擊區(qū)域 */
.click-area {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, rgba(255,223,79,0.3), rgba(255,183,0,0.3));
cursor: pointer;
border: 2px dashed rgba(255,255,255,0.5);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 14px;
}
</style>
</head>
<body>
<!-- 頂部金幣UI -->
<div class="top-bar">
<div class="coin-box" id="coinBox">
<div class="coin-icon"></div>
<span class="coin-count" id="coinCount">844</span>
</div>
</div>
<!-- 點(diǎn)擊區(qū)域 -->
<div class="click-area" id="clickArea">點(diǎn)擊生成金幣</div>
<script>
const clickArea = document.getElementById('clickArea');
const coinCountEl = document.getElementById('coinCount');
const coinBoxEl = document.getElementById('coinBox');
let coinCount = 844;
let targetRect;
// 金幣對象池,復(fù)用DOM,提升性能
const coinPool = [];
const maxCoins = 20; // 最大同時(shí)動(dòng)畫數(shù)量
// 更新目標(biāo)位置
function updateTargetRect() {
const coinIcon = document.querySelector('.coin-icon');
targetRect = coinIcon.getBoundingClientRect();
}
window.addEventListener('load', updateTargetRect);
window.addEventListener('resize', updateTargetRect);
// 點(diǎn)擊生成金幣(一次生成多個(gè),更有真實(shí)感)
clickArea.addEventListener('click', (e) => {
// 一次生成3-5個(gè)金幣,隨機(jī)數(shù)量
const count = Math.floor(Math.random() * 3) + 3;
for (let i = 0; i < count; i++) {
// 隨機(jī)延遲,避免同時(shí)出發(fā)
setTimeout(() => {
// 給每個(gè)金幣加隨機(jī)起點(diǎn)偏移,避免完全一樣
const offsetX = (Math.random() - 0.5) * 60;
const offsetY = (Math.random() - 0.5) * 60;
createFlyingCoin(e.clientX + offsetX, e.clientY + offsetY);
}, i * 50);
}
coinCount += count;
});
// 從對象池獲取金幣
function getCoinFromPool() {
if (coinPool.length > 0) {
return coinPool.pop();
}
// 池子里沒有就新建
const coin = document.createElement('div');
coin.className = 'flying-coin';
return coin;
}
// 回收金幣到對象池
function recycleCoin(coin) {
if (coinPool.length < maxCoins) {
coin.style.display = 'none';
coinPool.push(coin);
} else {
coin.remove();
}
}
function createFlyingCoin(startX, startY) {
const coin = getCoinFromPool();
coin.style.display = 'block';
coin.style.left = startX - 12 + 'px';
coin.style.top = startY - 12 + 'px';
document.body.appendChild(coin);
// 目標(biāo)位置(帶隨機(jī)偏移,避免所有金幣都落同一個(gè)點(diǎn))
const targetX = targetRect.left + targetRect.width / 2 - 12 + (Math.random() - 0.5) * 10;
const targetY = targetRect.top + targetRect.height / 2 - 12 + (Math.random() - 0.5) * 10;
// 動(dòng)畫參數(shù):隨機(jī)時(shí)長、隨機(jī)拋高,讓每個(gè)金幣軌跡都不一樣
const duration = 0.5 + Math.random() * 0.3; // 0.5-0.8秒
const arcHeight = 60 + Math.random() * 60; // 拋物線拋高60-120px
const startTime = performance.now();
function animate(currentTime) {
const elapsed = (currentTime - startTime) / 1000;
const progress = Math.min(elapsed / duration, 1);
// 緩動(dòng)函數(shù):先快后慢,模擬真實(shí)加速度
const easeProgress = 1 - Math.pow(1 - progress, 3);
// 1. 拋物線軌跡計(jì)算
const t = easeProgress;
const midY = Math.min(startY, targetY) - arcHeight;
const x = startX + (targetX - startX) * t;
const y = (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * midY + t * t * targetY;
// 2. 3D旋轉(zhuǎn):繞X和Y軸同時(shí)旋轉(zhuǎn),模擬金幣翻轉(zhuǎn)
const rotateY = 360 * 2 * progress; // 繞Y軸旋轉(zhuǎn)2圈
const rotateX = 180 * progress; // 繞X軸旋轉(zhuǎn)1圈
// 3. 縮放+透明度:近大遠(yuǎn)小,落地時(shí)縮小消失
const scale = progress > 0.7 ? 1 - (progress - 0.7) / 0.3 * 0.8 : 1;
const opacity = progress > 0.8 ? 1 - (progress - 0.8) / 0.2 : 1;
// 4. 應(yīng)用樣式
coin.style.left = x - 12 + 'px';
coin.style.top = y - 12 + 'px';
coin.style.transform = `rotateY(${rotateY}deg) rotateX(${rotateX}deg) scale(${scale})`;
coin.style.opacity = opacity;
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// 動(dòng)畫結(jié)束:回收金幣 + 落地反饋
recycleCoin(coin);
// 數(shù)字更新+抖動(dòng)效果
coinCountEl.textContent = coinCount;
coinCountEl.style.transform = 'scale(1.3)';
coinBoxEl.style.transform = 'scale(1.1)';
setTimeout(() => {
coinCountEl.style.transform = 'scale(1)';
coinBoxEl.style.transform = 'scale(1)';
}, 150);
}
}
requestAnimationFrame(animate);
}
</script>
</body>
</html>尾跡 / 拖影效果
在金幣飛行路徑上生成短暫的淡金色小圓點(diǎn),模擬速度感:
// 在animate函數(shù)里添加
if (progress < 0.9) {
const trail = document.createElement('div');
trail.style.cssText = `
position: fixed;
left: ${x-6}px;
top: ${y-6}px;
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255,223,79,0.6);
pointer-events: none;
opacity: 0.6;
z-index: 998;
`;
document.body.appendChild(trail);
// 尾跡逐漸消失
setTimeout(() => trail.remove(), 100);
}音效反饋
添加金幣飛行和落地的音效,這里用 Web Audio API 簡單實(shí)現(xiàn):
// 初始化音頻上下文
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// 飛行音效(高音調(diào))
function playFlySound() {
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.frequency.value = 800;
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1);
oscillator.start(audioCtx.currentTime);
oscillator.stop(audioCtx.currentTime + 0.1);
}
// 落地音效(清脆的叮聲)
function playLandSound() {
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.frequency.value = 1200;
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.2, audioCtx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.15);
oscillator.start(audioCtx.currentTime);
oscillator.stop(audioCtx.currentTime + 0.15);
}
// 在createFlyingCoin里調(diào)用飛行音效,在動(dòng)畫結(jié)束時(shí)調(diào)用落地音效把單個(gè)點(diǎn)擊修改成 鼠標(biāo)在屏幕任意位置點(diǎn)擊都會(huì)生成金幣(鼠標(biāo)點(diǎn)哪里,金幣就從哪里飛出來)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>超逼真金幣落袋(鼠標(biāo)任意點(diǎn)擊)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background: #1a1a4e;
overflow: hidden;
user-select: none;
position: relative;
}
/* 頂部金幣UI */
.top-bar {
position: fixed;
top: 20px;
left: 0;
width: 100%;
display: flex;
align-items: center;
gap: 20px;
padding: 0 20px;
z-index: 10;
}
.coin-box {
display: flex;
align-items: center;
background: rgba(255,255,255,0.1);
border-radius: 999px;
padding: 6px 12px;
gap: 8px;
transition: transform 0.1s ease;
}
.coin-icon {
width: 36px;
height: 36px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ffdf4f, #ffb700);
box-shadow: 0 0 8px #ffd000;
}
.coin-count {
color: white;
font-size: 28px;
font-weight: bold;
font-family: sans-serif;
transition: transform 0.15s ease;
}
/* 飛行動(dòng)畫金幣 */
.flying-coin {
position: fixed;
width: 24px;
height: 24px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ffdf4f, #ffb700);
box-shadow: 0 0 10px #ffd000;
pointer-events: none;
z-index: 999;
transform-style: preserve-3d;
perspective: 500px;
}
</style>
</head>
<body>
<!-- 頂部金幣UI -->
<div class="top-bar">
<div class="coin-box" id="coinBox">
<div class="coin-icon"></div>
<span class="coin-count" id="coinCount">844</span>
</div>
</div>
<script>
const coinCountEl = document.getElementById('coinCount');
const coinBoxEl = document.getElementById('coinBox');
let coinCount = 844;
let targetRect;
// 金幣對象池
const coinPool = [];
const maxCoins = 20;
// 更新目標(biāo)位置
function updateTargetRect() {
const coinIcon = document.querySelector('.coin-icon');
targetRect = coinIcon.getBoundingClientRect();
}
window.addEventListener('load', updateTargetRect);
window.addEventListener('resize', updateTargetRect);
// ? 關(guān)鍵:鼠標(biāo)在屏幕任意位置點(diǎn)擊 → 生成金幣
document.addEventListener('click', (e) => {
const count = Math.floor(Math.random() * 3) + 3;
for (let i = 0; i < count; i++) {
setTimeout(() => {
const offsetX = (Math.random() - 0.5) * 60;
const offsetY = (Math.random() - 0.5) * 60;
createFlyingCoin(e.clientX + offsetX, e.clientY + offsetY);
}, i * 50);
}
coinCount += count;
});
// 從對象池獲取金幣
function getCoinFromPool() {
if (coinPool.length > 0) {
return coinPool.pop();
}
const coin = document.createElement('div');
coin.className = 'flying-coin';
return coin;
}
// 回收金幣
function recycleCoin(coin) {
if (coinPool.length < maxCoins) {
coin.style.display = 'none';
coinPool.push(coin);
} else {
coin.remove();
}
}
function createFlyingCoin(startX, startY) {
const coin = getCoinFromPool();
coin.style.display = 'block';
coin.style.left = startX - 12 + 'px';
coin.style.top = startY - 12 + 'px';
document.body.appendChild(coin);
const targetX = targetRect.left + targetRect.width / 2 - 12 + (Math.random() - 0.5) * 10;
const targetY = targetRect.top + targetRect.height / 2 - 12 + (Math.random() - 0.5) * 10;
const duration = 0.5 + Math.random() * 0.3;
const arcHeight = 60 + Math.random() * 60;
const startTime = performance.now();
function animate(currentTime) {
const elapsed = (currentTime - startTime) / 1000;
const progress = Math.min(elapsed / duration, 1);
const easeProgress = 1 - Math.pow(1 - progress, 3);
// 拋物線軌跡
const t = easeProgress;
const midY = Math.min(startY, targetY) - arcHeight;
const x = startX + (targetX - startX) * t;
const y = (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * midY + t * t * targetY;
// 3D旋轉(zhuǎn)
const rotateY = 720 * progress;
const rotateX = 360 * progress;
const scale = progress > 0.7 ? 1 - (progress - 0.7) / 0.3 * 0.8 : 1;
const opacity = progress > 0.8 ? 1 - (progress - 0.8) / 0.2 : 1;
coin.style.left = x - 12 + 'px';
coin.style.top = y - 12 + 'px';
coin.style.transform = `rotateY(${rotateY}deg) rotateX(${rotateX}deg) scale(${scale})`;
coin.style.opacity = opacity;
if (progress < 1) {
requestAnimationFrame(animate);
} else {
recycleCoin(coin);
coinCountEl.textContent = coinCount;
// 抖動(dòng)反饋
coinCountEl.style.transform = 'scale(1.3)';
coinBoxEl.style.transform = 'scale(1.1)';
setTimeout(() => {
coinCountEl.style.transform = 'scale(1)';
coinBoxEl.style.transform = 'scale(1)';
}, 150);
}
}
requestAnimationFrame(animate);
}
</script>
</body>
</html>另一種效果如圖:

先上代碼
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>金幣落袋效果 預(yù)覽</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background: #0b1020;
overflow: hidden;
user-select: none;
}
/* 錢袋 */
.bag {
position: fixed;
bottom: 60px;
left: 50%;
transform: translateX(-50%);
font-size: 120px;
z-index: 10;
}
/* 金幣 */
.coin {
position: fixed;
width: 32px;
height: 32px;
border-radius: 50%;
background: radial-gradient(circle, #ffdf4f, #ffb700);
box-shadow: 0 0 12px #ffd000;
pointer-events: none;
transition: all 0.8s cubic-bezier(0.15, 0.6, 0.3, 1);
}
</style>
</head>
<body>
<div class="bag">??</div>
<script>
const bag = document.querySelector('.bag');
let bagRect;
// 更新錢袋位置
function setBagPos() {
bagRect = bag.getBoundingClientRect();
}
window.addEventListener('load', setBagPos);
window.addEventListener('resize', setBagPos);
// 點(diǎn)擊生成金幣
document.addEventListener('click', e => {
createCoin(e.clientX, e.clientY);
});
function createCoin(x, y) {
const coin = document.createElement('div');
coin.className = 'coin';
coin.style.left = x - 16 + 'px';
coin.style.top = y - 16 + 'px';
document.body.appendChild(coin);
// 目標(biāo):錢袋中心
const targetX = bagRect.left + bagRect.width / 2 - 16;
const targetY = bagRect.top + bagRect.height / 2 - 16;
requestAnimationFrame(() => {
coin.style.left = targetX + 'px';
coin.style.top = targetY + 'px';
coin.style.transform = 'rotate(1080deg) scale(0.1)';
coin.style.opacity = 0;
});
// 銷毀節(jié)點(diǎn)
setTimeout(() => coin.remove(), 800);
}
</script>
</body>
</html>自定義參數(shù)調(diào)整效果代碼
/* 金幣大小 */ width: 30px; height: 30px; /* 動(dòng)畫時(shí)長(越快數(shù)值越?。?*/ transition: all 0.6s; /* 錢袋大小 */ font-size: 100px; /* 金幣顏色(金色系可修改) */ background: radial-gradient(circle, #ffd700 0%, #ffcc00 50%, #e6b800 100%);
連續(xù)點(diǎn)擊批量生成金幣,可修改代碼:
// 點(diǎn)擊生成5個(gè)隨機(jī)金幣
document.addEventListener('click', (e) => {
for(let i=0; i<5; i++){
// 隨機(jī)偏移位置
const offsetX = (Math.random() - 0.5) * 50;
const offsetY = (Math.random() - 0.5) * 50;
createCoin(e.clientX + offsetX, e.clientY + offsetY);
}
});總結(jié)
到此這篇關(guān)于使用html+javascript實(shí)現(xiàn)金幣落袋效果的文章就介紹到這了,更多相關(guān)html js金幣落袋效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決html按鈕切換綁定不同函數(shù)后點(diǎn)擊時(shí)執(zhí)行多次函數(shù)問題
這篇文章主要介紹了如何解決html按鈕切換綁定不同函數(shù)后點(diǎn)擊時(shí)執(zhí)行多次函數(shù)問題,需要的朋友可以參考下2014-05-05
js實(shí)現(xiàn)點(diǎn)擊切換和自動(dòng)播放的輪播圖
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)點(diǎn)擊切換和自動(dòng)播放的輪播圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
javascript將非數(shù)值轉(zhuǎn)換為數(shù)值
parseInt()不能轉(zhuǎn)換浮點(diǎn)型數(shù)值,我們用parseFloat()來解決。這篇文章主要介紹了javascript將非數(shù)值轉(zhuǎn)換為數(shù)值,需要的朋友可以參考下2018-09-09
js圖片閃動(dòng)特效可以控制間隔時(shí)間如幾分鐘閃動(dòng)一下
這篇文章主要介紹一個(gè)圖片閃動(dòng)特效,可以控制間隔時(shí)間如幾分鐘閃動(dòng)一下,需要的朋友不要錯(cuò)過2014-08-08
Javascript實(shí)現(xiàn)圖片加載從模糊到清晰顯示的方法
這篇文章主要介紹了使用Javascript實(shí)現(xiàn)圖片加載從模糊到清晰顯示的方法,通俗易懂,需要的朋友可以參考下。2016-06-06
webpack5的entry和output配置小白學(xué)習(xí)
這篇文章主要為大家介紹了webpack5的entry和output使用配置小白學(xué)習(xí)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
javascript createElement()創(chuàng)建input不能設(shè)置name屬性的解決方法
今天在使用document.createElement()動(dòng)態(tài)創(chuàng)建input時(shí),發(fā)現(xiàn)不能為其name賦值,以下是測試代碼。2009-10-10
JS實(shí)現(xiàn)定時(shí)任務(wù)每隔N秒請求后臺(tái)setInterval定時(shí)和ajax請求問題
這篇文章主要介紹了JS實(shí)現(xiàn)定時(shí)任務(wù)每隔N秒請求后臺(tái)setInterval定時(shí)和ajax請求 的相關(guān)資料,需要的朋友可以參考下2017-10-10

