You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
spa_instat_tv/src/features/StreamPlayer/hooks/useParticles.tsx

138 lines
4.1 KiB

import {
useEffect,
useRef,
useState,
} from 'react'
import { isMobileDevice } from 'config'
type Particle = {
height: number, // Высота частицы
image: HTMLImageElement, // Изображение частицы
life: number, // Время жизни частицы
opacity: number, // Непрозрачность частицы,
vx: number, // Горизонтальная скорость частицы
vy: number, // Вертикальная скорость частицы
width: number, // Ширина частицы
x: number, // Cмещение по горизонтали
y: number, // Смещение по вертикали
}
const BIRTH_RATE = 100
const PARTICLES_COUNT = isMobileDevice ? 1 : 2
export const useParticles = () => {
const canvasRef = useRef<HTMLCanvasElement>(null)
const likeButtonRef = useRef<HTMLButtonElement>(null)
const requestAnimationIdRef = useRef<number | null>(null)
const [generateParticles, setGenerateParticles] = useState(false)
useEffect(() => {
const canvas = canvasRef.current
const likeButton = likeButtonRef.current
if (!canvas || !likeButton) return () => {}
let intervalId: NodeJS.Timeout
const particleImage = new Image()
particleImage.src = '/images/like-active-icon.svg'
const ctx = canvas.getContext('2d')
if (!ctx) return () => {}
const canvasRect = canvas.getBoundingClientRect()
const likeButtonRect = likeButton.getBoundingClientRect()
// Массив для хранения частиц
const particles: Array<Particle> = []
// Начальные координаты вылета частиц
const startX = (likeButtonRect.left - canvasRect.left) + likeButtonRect.width / 2
const startY = likeButtonRect.top - canvasRect.top
// Функция для создания частиц
const createParticles = (count: number) => {
for (let i = 0; i < count; i++) {
const particleSize = Math.random() * (isMobileDevice ? 30 : 40)
const particle: Particle = {
height: particleSize,
image: particleImage,
life: 500,
opacity: 0.5,
vx: 0,
vy: -(Math.random() * 2 + 2),
width: particleSize,
x: startX - particleSize / 2 + Math.random() * 40 - 20,
y: startY,
}
particles.push(particle)
}
}
// Функция для обновления частиц
const updateParticles = () => {
ctx.clearRect(
0,
0,
canvas.width,
canvas.height,
)
for (let i = 0; i < particles.length; i++) {
const particle = particles[i]
particle.life-- // уменьшаем время жизни частицы
// Удаляем частицу из массива, если её время жизни истекло
if (particle.life <= 0) {
particles.splice(i, 1)
i--
} else {
particle.x += particle.vx // Обновляем положение частицы по горизонтали
particle.y += particle.vy // Обновляем положение частицы по вертикали
ctx.globalAlpha = particle.opacity
ctx.drawImage(
particle.image,
particle.x,
particle.y,
particle.width,
particle.height,
)
}
}
if (particles.length === 0 && requestAnimationIdRef.current) {
cancelAnimationFrame(requestAnimationIdRef.current)
return
}
requestAnimationIdRef.current = requestAnimationFrame(updateParticles)
}
if (generateParticles) {
createParticles(PARTICLES_COUNT)
intervalId = setInterval(() => createParticles(PARTICLES_COUNT), BIRTH_RATE)
updateParticles()
}
return () => {
intervalId && clearInterval(intervalId)
}
}, [generateParticles])
useEffect(() => () => {
requestAnimationIdRef.current && cancelAnimationFrame(requestAnimationIdRef.current)
}, [])
return {
canvasRef,
likeButtonRef,
setGenerateParticles,
}
}