'commit'
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user