본문 바로가기
Javascript

Canvas Bubble Effect 기록(react + typescript)

by YoYoHa 2022. 12. 7.
728x90
반응형

 

Mouse Bubble Effect Code 기록

import React, {useEffect, useRef} from 'react';

interface BubbleMouseType {
    x?: number,
    y?: number,
    radius: number
}

export default function Bubble(){

    const canvasRef = useRef<HTMLCanvasElement>(null);
    const mouse: BubbleMouseType = { x: undefined, y: undefined, radius: 150 }
    let particlesArray: any = [];
    let adjustX = 0;
    let adjustY = 0;


    useEffect(() => {
        const canvas = canvasRef?.current;

        if (canvas) {

            let ctx = canvas.getContext('2d');


            class Particle {
                private x: any;
                private y: any;
                private size: number;
                private baseX: any;
                private baseY: any;
                private density: number;
                private distance: number | undefined;
                constructor(x: any, y: any) {
                    this.x = x;
                    this.y = y;
                    this.size = 3;
                    this.baseX = this.x;
                    this.baseY = this.y;
                    this.density = (Math.random() * 30) + 1;
                }
                draw() {
                    if (ctx) {
                        ctx.fillStyle = 'rgba(255,255,255,0.8)';
                        ctx.strokeStyle = 'rgba(34,147,214,1)';
                        ctx.beginPath();

                        if (this.distance) {
                            if (this.distance < mouse.radius -5) {
                                this.size = 13;
                                ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                                ctx.stroke();
                                ctx.closePath();
                                ctx.beginPath();
                                ctx.arc(this.x - 3, this.y - 3, this.size / 2.5, 0, Math.PI * 2);
                                ctx.arc(this.x + 7, this.y + 1, this.size / 3.5, 0, Math.PI * 2);
                            } else if (this.distance <= mouse.radius) {
                                this.size = 10;
                                ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                                ctx.stroke();
                                ctx.closePath();
                                ctx.beginPath();
                                ctx.arc(this.x - 2, this.y - 2, this.size / 3, 0, Math.PI * 2);
                            } else {
                                this.size = 8;
                                ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                                ctx.stroke();
                                ctx.closePath();
                                ctx.beginPath();
                                ctx.arc(this.x - 1, this.y - 1, this.size / 3, 0, Math.PI * 2);
                            }
                        }

                        ctx.closePath();
                        ctx.fill();
                    }
                }
                update() {
                    if (mouse.x && mouse.y) {
                        let dx = mouse.x - this.x
                        let dy = mouse.y - this.y;
                        let distance = Math.sqrt(dx * dx + dy * dy);
                        this.distance = distance;
                        let forceDirectionX = dx / distance;
                        let forceDirectionY = dy / distance;
                        let maxDistance = mouse.radius;
                        let force = (maxDistance - distance) / maxDistance;
                        let directionX = forceDirectionX * force * this.density;
                        let directionY = forceDirectionY * force * this.density;
                        if (distance < mouse.radius) {
                            this.x -= directionX;
                            this.y -= directionY;
                        } else {
                            if (this.x !== this.baseX) {
                                let dx = this.x - this.baseX;
                                this.x -= dx / 10;
                            }
                            if (this.x !== this.baseX) {
                                let dy = this.y - this.baseY;
                                this.y -= dy / 10;
                            }
                        }
                    }
                }
            }

            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;

            window.addEventListener('mousemove', (event) => {
                mouse.x = event.x;
                mouse.y = event.y;
            });

            if (ctx) {
                ctx.lineWidth = 3;
                ctx.fillStyle = 'white';
                ctx.font = '15px Verdana';
                ctx.fillText('요요', 25, 25);
                const textCoordinates = ctx.getImageData(0,0,100,100);

                const init = () => {
                    particlesArray = [];
                    for (let y=0, y2 = textCoordinates.height;y<y2;y++) {
                        for (let x = 0, x2 = textCoordinates.width;x<x2;x++) {
                            if (textCoordinates.data[(y * 4 * textCoordinates.width) + (x * 4) + 3] > 128) {
                                let positionX = x + adjustX;
                                let positionY = y + adjustY;
                                particlesArray.push(new Particle(positionX * 25, positionY * 25));
                            }
                        }
                    }
                }
                init();

                const connect = () => {
                    let opacityValue = 1;
                    for (let a = 0;a<particlesArray.length;a++) {
                        for (let b = a;b<particlesArray.length;b++) {
                            let dx = particlesArray[a].x - particlesArray[b].x;
                            let dy = particlesArray[a].y - particlesArray[b].y;
                            let distance = Math.sqrt(dx * dx + dy* dy);
                            opacityValue = 1 - (distance/50);
                            if (ctx) ctx.strokeStyle = 'rgba(255,255,255,' + opacityValue + ')';

                            if (distance < 50 && ctx) {
                                ctx.lineWidth = 2;
                                ctx.beginPath();
                                ctx.moveTo(particlesArray[a].x, particlesArray[a].y);
                                ctx.lineTo(particlesArray[b].x, particlesArray[b].y);
                                ctx.stroke();
                            }
                        }
                    }
                }

                const animate = () => {
                    if (ctx) {
                        ctx.clearRect(0,0, canvas.width, canvas.height);
                        for (let i=0;i<particlesArray.length;i++) {
                            particlesArray[i].draw();
                            particlesArray[i].update();
                        }
                        // connect();
                        requestAnimationFrame(animate);
                    }
                };

                animate();



            }

        }

    }, []);

    return (
        <canvas className="canvas" ref={canvasRef}></canvas>
    );
};

 

 

.canvas{ position: absolute; z-index: 1; background: black; width: 100%; height: 100%; top: 0; left: 0; }

 

 

 

728x90
반응형

댓글