|
<script lang="ts"> |
|
import { fade } from 'svelte/transition'; |
|
import { onMount } from 'svelte'; |
|
|
|
export let effects: Array<{type: string, emoji: string, duration: number}> = []; |
|
export let flash: boolean = false; |
|
|
|
|
|
const flickerCount = 19; |
|
const frameDelay = 2; |
|
const flickerDuration = 600; |
|
|
|
|
|
let isFlickering = false; |
|
let flickerVisible = true; |
|
let flickerFrame = 0; |
|
let flickerInterval: number; |
|
|
|
|
|
const PARTICLES_PER_EFFECT = 5; |
|
const SPAWN_RADIUS = 60; |
|
|
|
|
|
$: particleList = effects.flatMap((effect, effectIndex) => { |
|
const particles = []; |
|
for (let i = 0; i < PARTICLES_PER_EFFECT; i++) { |
|
|
|
const angle = (Math.PI * 2 * i) / PARTICLES_PER_EFFECT + (Math.random() - 0.5) * 0.5; |
|
const distance = SPAWN_RADIUS * (0.7 + Math.random() * 0.6); |
|
const x = Math.cos(angle) * distance; |
|
const y = Math.sin(angle) * distance; |
|
|
|
|
|
const scale = 0.8 + Math.random() * 0.4; |
|
const rotation = Math.random() * 360; |
|
const duration = effect.duration + (Math.random() - 0.5) * 200; |
|
const delay = Math.random() * 100; |
|
|
|
particles.push({ |
|
id: `${effectIndex}-${i}`, |
|
type: effect.type, |
|
emoji: effect.emoji, |
|
x, |
|
y, |
|
scale, |
|
rotation, |
|
duration, |
|
delay |
|
}); |
|
} |
|
return particles; |
|
}); |
|
|
|
|
|
$: if (flash && !isFlickering) { |
|
startFlickerAnimation(); |
|
} |
|
|
|
function startFlickerAnimation() { |
|
isFlickering = true; |
|
flickerFrame = 0; |
|
|
|
|
|
const totalFrames = flickerCount * (frameDelay + 1); |
|
const frameDuration = flickerDuration / totalFrames; |
|
|
|
flickerInterval = setInterval(() => { |
|
if (flickerFrame >= totalFrames) { |
|
|
|
clearInterval(flickerInterval); |
|
isFlickering = false; |
|
flickerVisible = true; |
|
return; |
|
} |
|
|
|
|
|
const flickerCycle = Math.floor(flickerFrame / (frameDelay + 1)); |
|
flickerVisible = flickerCycle % 2 === 0; |
|
|
|
flickerFrame++; |
|
}, frameDuration); |
|
} |
|
|
|
onMount(() => { |
|
return () => { |
|
if (flickerInterval) { |
|
clearInterval(flickerInterval); |
|
} |
|
}; |
|
}); |
|
</script> |
|
|
|
<!-- Effects wrapper with relative positioning for particles --> |
|
<div class="effects-wrapper"> |
|
|
|
<div class="effects-container" style="opacity: {(flash && isFlickering) ? (flickerVisible ? 1 : 0) : 1};"> |
|
<slot /> |
|
</div> |
|
|
|
|
|
{#each particleList as particle (particle.id)} |
|
<div |
|
class="effect-particle {particle.type}" |
|
style=" |
|
left: {particle.x}px; |
|
top: {particle.y}px; |
|
animation-duration: {particle.duration}ms; |
|
animation-delay: {particle.delay}ms; |
|
--initial-scale: {particle.scale}; |
|
--initial-rotation: {particle.rotation}deg; |
|
" |
|
> |
|
<span class="effect-emoji">{particle.emoji}</span> |
|
</div> |
|
{/each} |
|
</div> |
|
|
|
<style> |
|
.effects-wrapper { |
|
position: relative; |
|
display: inline-block; |
|
} |
|
|
|
.effects-container { |
|
position: relative; |
|
display: inline-block; |
|
transition: opacity 0.05s ease; |
|
} |
|
|
|
.effect-particle { |
|
position: absolute; |
|
pointer-events: none; |
|
z-index: 5; |
|
animation-fill-mode: forwards; |
|
transform-origin: center center; |
|
} |
|
|
|
.effect-emoji { |
|
font-size: 20px; |
|
display: block; |
|
filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.4)); |
|
transform: scale(var(--initial-scale, 1)) rotate(var(--initial-rotation, 0deg)); |
|
} |
|
|
|
|
|
.effect-particle.burn { |
|
animation: statusBurn ease-in-out; |
|
} |
|
|
|
.effect-particle.poison { |
|
animation: statusPoison ease-in-out; |
|
} |
|
|
|
.effect-particle.paralyze { |
|
animation: statusParalyze linear; |
|
} |
|
|
|
.effect-particle.sleep { |
|
animation: statusSleep ease-in-out; |
|
} |
|
|
|
.effect-particle.freeze { |
|
animation: statusFreeze ease-out; |
|
} |
|
|
|
|
|
.effect-particle.attackUp, |
|
.effect-particle.defenseUp, |
|
.effect-particle.speedUp, |
|
.effect-particle.accuracyUp { |
|
animation: statIncrease ease-out; |
|
} |
|
|
|
|
|
.effect-particle.attackDown, |
|
.effect-particle.defenseDown, |
|
.effect-particle.speedDown, |
|
.effect-particle.accuracyDown { |
|
animation: statDecrease ease-in; |
|
} |
|
|
|
|
|
.effect-particle.critical, |
|
.effect-particle.superEffective { |
|
animation: criticalBurst ease-out; |
|
} |
|
|
|
.effect-particle.notVeryEffective, |
|
.effect-particle.miss { |
|
animation: missSwirl ease-in-out; |
|
} |
|
|
|
.effect-particle.heal { |
|
animation: healRise ease-out; |
|
} |
|
|
|
|
|
@keyframes statusBurn { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.3); |
|
opacity: 0; |
|
} |
|
15% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 45deg)) scale(1.2); |
|
opacity: 1; |
|
} |
|
50% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 180deg)) scale(1.0); |
|
opacity: 0.9; |
|
} |
|
85% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 315deg)) scale(0.8); |
|
opacity: 0.4; |
|
} |
|
100% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 360deg)) scale(0.5); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes statusPoison { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.4); |
|
opacity: 0; |
|
} |
|
20% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 30deg)) scale(1.1); |
|
opacity: 1; |
|
} |
|
40% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 30deg)) scale(0.9); |
|
opacity: 0.8; |
|
} |
|
60% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 20deg)) scale(1.0); |
|
opacity: 0.6; |
|
} |
|
80% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 15deg)) scale(0.7); |
|
opacity: 0.3; |
|
} |
|
100% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.5); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes statusParalyze { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.2); |
|
opacity: 0; |
|
} |
|
10% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 10deg)) scale(1.3); |
|
opacity: 1; |
|
} |
|
20% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 10deg)) scale(1.1); |
|
opacity: 0.9; |
|
} |
|
30% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 8deg)) scale(1.2); |
|
opacity: 0.8; |
|
} |
|
40% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 8deg)) scale(1.0); |
|
opacity: 0.7; |
|
} |
|
50% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 5deg)) scale(0.9); |
|
opacity: 0.6; |
|
} |
|
100% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.3); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes statusSleep { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.5); |
|
opacity: 0; |
|
} |
|
25% { |
|
transform: translate(-50%, -55%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 15deg)) scale(1.1); |
|
opacity: 1; |
|
} |
|
50% { |
|
transform: translate(-50%, -45%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 15deg)) scale(1.0); |
|
opacity: 0.9; |
|
} |
|
75% { |
|
transform: translate(-50%, -55%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 10deg)) scale(0.9); |
|
opacity: 0.5; |
|
} |
|
100% { |
|
transform: translate(-50%, -60%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.4); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes statusFreeze { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.3); |
|
opacity: 0; |
|
} |
|
30% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 5deg)) scale(1.4); |
|
opacity: 1; |
|
} |
|
60% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 3deg)) scale(1.2); |
|
opacity: 0.8; |
|
} |
|
90% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 2deg)) scale(0.8); |
|
opacity: 0.3; |
|
} |
|
100% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.6); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes statIncrease { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.4); |
|
opacity: 0; |
|
} |
|
25% { |
|
transform: translate(-50%, -70%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 90deg)) scale(1.3); |
|
opacity: 1; |
|
} |
|
50% { |
|
transform: translate(-50%, -90%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 180deg)) scale(1.1); |
|
opacity: 0.9; |
|
} |
|
75% { |
|
transform: translate(-50%, -110%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 270deg)) scale(0.9); |
|
opacity: 0.6; |
|
} |
|
100% { |
|
transform: translate(-50%, -130%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 360deg)) scale(0.5); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes statDecrease { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.4); |
|
opacity: 0; |
|
} |
|
25% { |
|
transform: translate(-50%, -30%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 30deg)) scale(1.2); |
|
opacity: 1; |
|
} |
|
50% { |
|
transform: translate(-50%, -10%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 30deg)) scale(1.0); |
|
opacity: 0.8; |
|
} |
|
75% { |
|
transform: translate(-50%, 10%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 20deg)) scale(0.8); |
|
opacity: 0.4; |
|
} |
|
100% { |
|
transform: translate(-50%, 30%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 10deg)) scale(0.6); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes criticalBurst { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.2); |
|
opacity: 0; |
|
} |
|
15% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 45deg)) scale(1.8); |
|
opacity: 1; |
|
} |
|
30% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 30deg)) scale(1.4); |
|
opacity: 0.9; |
|
} |
|
50% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 60deg)) scale(1.6); |
|
opacity: 0.8; |
|
} |
|
70% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 45deg)) scale(1.2); |
|
opacity: 0.5; |
|
} |
|
100% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 720deg)) scale(0.3); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes missSwirl { |
|
0% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.6); |
|
opacity: 0; |
|
} |
|
25% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 90deg)) scale(1.2); |
|
opacity: 0.7; |
|
} |
|
50% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 180deg)) scale(1.0); |
|
opacity: 0.5; |
|
} |
|
75% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 270deg)) scale(0.8); |
|
opacity: 0.3; |
|
} |
|
100% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 360deg)) scale(0.4); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
@keyframes healRise { |
|
0% { |
|
transform: translate(-50%, -30%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.5); |
|
opacity: 0; |
|
} |
|
20% { |
|
transform: translate(-50%, -50%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 30deg)) scale(1.3); |
|
opacity: 1; |
|
} |
|
40% { |
|
transform: translate(-50%, -70%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 20deg)) scale(1.1); |
|
opacity: 0.9; |
|
} |
|
60% { |
|
transform: translate(-50%, -90%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) + 15deg)) scale(1.0); |
|
opacity: 0.7; |
|
} |
|
80% { |
|
transform: translate(-50%, -110%) scale(var(--initial-scale)) rotate(calc(var(--initial-rotation) - 10deg)) scale(0.8); |
|
opacity: 0.4; |
|
} |
|
100% { |
|
transform: translate(-50%, -130%) scale(var(--initial-scale)) rotate(var(--initial-rotation)) scale(0.5); |
|
opacity: 0; |
|
} |
|
} |
|
</style> |