This commit is contained in:
HuangHai
2026-01-20 13:48:35 +08:00
parent f7d55a64dc
commit 3c41a46b21

View File

@@ -193,6 +193,113 @@
to { transform: translateY(-20px); }
}
/* 枪击特效样式 */
.gun-wrapper {
position: absolute;
right: -300px; /* 初始在屏幕外 */
top: 50%;
transform: translateY(-50%);
width: 300px;
height: 200px;
z-index: 50;
transition: right 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
pointer-events: none;
}
.gun-wrapper.active {
right: 50px; /* 伸出来 */
}
/* 枪身 SVG 容器 */
.gun-svg {
width: 100%;
height: 100%;
filter: drop-shadow(5px 5px 5px rgba(0,0,0,0.5));
}
/* 枪口火光 */
.muzzle-flash {
position: absolute;
left: -40px;
top: 35px;
width: 80px;
height: 80px;
background: radial-gradient(circle, #fff, #ffff00, #ff0000, transparent);
border-radius: 50%;
transform: scale(0);
opacity: 0;
z-index: 51;
}
.muzzle-flash.bang {
animation: flash 0.1s ease-out;
}
@keyframes flash {
0% { transform: scale(0); opacity: 1; }
50% { transform: scale(1.5); opacity: 1; }
100% { transform: scale(0.5); opacity: 0; }
}
/* 开枪时的后坐力 */
.gun-wrapper.shoot .gun-svg {
animation: recoil 0.2s ease-out;
}
@keyframes recoil {
0% { transform: translateX(0) rotate(0); }
10% { transform: translateX(50px) rotate(10deg); }
100% { transform: translateX(0) rotate(0); }
}
/* 砰文字 */
.bang-text {
position: absolute;
left: -80px;
top: 0;
font-size: 4rem;
color: #fff;
font-weight: bold;
text-shadow: 0 0 10px red;
transform: scale(0) rotate(-20deg);
opacity: 0;
z-index: 52;
}
.bang-text.show {
animation: bangPop 0.5s ease-out;
}
@keyframes bangPop {
0% { transform: scale(0) rotate(-20deg); opacity: 1; }
50% { transform: scale(1.5) rotate(0deg); opacity: 1; }
100% { transform: scale(1) rotate(0deg); opacity: 0; }
}
/* 名字倒地动画 */
.victim-name.dead {
animation: fallDead 1.5s forwards cubic-bezier(0.6, -0.28, 0.735, 0.045);
}
.victim-name.dead .face-emoji {
content: "😵"; /* 可以在 CSS 中无法直接改内容,通过 JS 改 */
animation: none; /* 停止跳动 */
transform: rotate(180deg); /* 表情倒过来 */
transition: all 0.5s;
}
@keyframes fallDead {
0% { transform: rotate(0); color: #fff; }
10% { transform: rotate(-5deg) translateX(0); color: #ff0000; } /* 中弹瞬间变红 */
30% { transform: rotate(10deg) translateX(10px); }
100% {
transform: rotate(100deg) translateY(300px);
opacity: 0.6;
color: #57606f; /* 变成死灰色 */
text-shadow: none;
}
}
.close-btn {
margin-top: 50px;
padding: 10px 30px;
@@ -232,6 +339,22 @@
???
<div class="face-emoji">😱</div>
</div>
<!-- 枪的容器 -->
<div id="gunWrapper" class="gun-wrapper">
<div class="bang-text">BANG!</div>
<div class="muzzle-flash"></div>
<!-- SVG 手枪 -->
<svg class="gun-svg" viewBox="0 0 100 60" fill="#333">
<path d="M10,10 L60,10 L60,20 L80,20 L80,30 L60,30 L60,40 L30,40 L30,50 L10,50 Z" stroke="#000" stroke-width="2"/>
<rect x="0" y="5" width="10" height="5" fill="#555"/>
<rect x="60" y="15" width="5" height="5" fill="#555"/>
<!-- 简单的手枪造型优化 -->
<path d="M5,10 h50 a5,5 0 0 1 5,5 v5 h25 v10 h-25 v15 a5,5 0 0 1 -5,5 h-20 a5,5 0 0 1 -5,-5 v-15 h-20 a5,5 0 0 1 -5,-5 v-10 a5,5 0 0 1 5,-5 z" fill="#2d3436"/>
<rect x="60" y="12" width="20" height="4" fill="#000"/>
</svg>
</div>
<button class="close-btn" onclick="closeModal()">放过他/她吧</button>
</div>
@@ -364,22 +487,66 @@
// ================= 结果与特效 =================
function showResult(name) {
victimNameEl.innerHTML = `${name} <div class="face-emoji">😱</div>`;
victimNameEl.classList.remove('dead'); // 重置状态
modal.classList.add('active');
// 播放惨叫/音效
playScreamSound();
speakName(name);
// 启动火焰粒子
initFire();
// 发射弹幕
launchDanmaku();
// 枪击流程
startShootingSequence(name);
}
function startShootingSequence(name) {
const gunWrapper = document.getElementById('gunWrapper');
const muzzleFlash = document.querySelector('.muzzle-flash');
const bangText = document.querySelector('.bang-text');
const faceEmoji = victimNameEl.querySelector('.face-emoji');
// 1. 枪伸出来 (0.5s后)
setTimeout(() => {
gunWrapper.classList.add('active');
}, 500);
// 2. 开枪 (1.5s后)
setTimeout(() => {
// 视觉特效
gunWrapper.classList.add('shoot');
muzzleFlash.classList.add('bang');
bangText.classList.add('show');
// 名字倒地
victimNameEl.classList.add('dead');
faceEmoji.innerText = "😵"; // 表情变成晕倒
// 音效
playGunShot();
playScreamSound(); // 惨叫配合枪声
// 朗读名字
setTimeout(() => speakName(name), 500);
}, 1500);
// 3. 弹幕稍后出现 (2.5s后)
setTimeout(() => {
launchDanmaku();
}, 2500);
}
function closeModal() {
modal.classList.remove('active');
cancelAnimationFrame(fireAnimationId);
// 重置枪的状态
const gunWrapper = document.getElementById('gunWrapper');
const muzzleFlash = document.querySelector('.muzzle-flash');
const bangText = document.querySelector('.bang-text');
gunWrapper.classList.remove('active', 'shoot');
muzzleFlash.classList.remove('bang');
bangText.classList.remove('show');
// 清除弹幕
document.querySelectorAll('.danmaku').forEach(el => el.remove());
}
@@ -424,6 +591,37 @@
osc.stop(audioCtx.currentTime + 0.1);
}
function playGunShot() {
if (audioCtx.state === 'suspended') audioCtx.resume();
// 噪声源模拟枪声
const bufferSize = audioCtx.sampleRate * 0.5; // 0.5秒
const buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
const noise = audioCtx.createBufferSource();
noise.buffer = buffer;
// 滤波器,模仿枪声的闷响
const filter = audioCtx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 1000;
// 包络
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(1, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2);
noise.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
noise.start();
}
function playScreamSound() {
// 模拟一个低沉的“咚”声,表示厄运降临
if (audioCtx.state === 'suspended') audioCtx.resume();