아래로 떨어지면서 쌓이는 애니메이션 도와주세요ㅠㅠ!!!!

아래로 떨어지면서 쌓이는 애니메이션 도와주세요ㅠㅠ!!!!

QA

아래로 떨어지면서 쌓이는 애니메이션 도와주세요ㅠㅠ!!!!

본문

3673009239_1722487078.1125.png

 

아래로 떨어지면서 쌓이는 애니메이션을 구현하고 싶은데 js 만으로 구현이 될까요 ????ㅠㅠ 

전부 리엑트로 구현한 것만 나와서 ㅠㅠ.... 오픈코드 아시는분 부탁드립니다 ,,!!! 

이 질문에 댓글 쓰기 :

답변 5

단순 위에서 아래로 중력에 의한 오브젝트 정렬이라면

 


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Falling Circles Simulation</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const circles = [];
        const numCircles = 6;
        const gravity = 0.1;
        const friction = 0.99;
        const minRadius = 10;
        const maxRadius = 100;
        const canvasWidth = 500;
        const canvasHeight = 500;
        const boxHeight = 100;
        canvas.width = canvasWidth;
        canvas.height = canvasHeight;
        class Circle {
            constructor(x, y, radius, vx = 0, vy = 0) {
                this.x = x;
                this.y = y;
                this.radius = radius;
                this.vx = vx;
                this.vy = vy;
            }
            draw() {
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
                ctx.fillStyle = 'rgba(0, 150, 255, 0.7)';
                ctx.fill();
                ctx.strokeStyle = 'rgba(0, 100, 200, 0.9)';
                ctx.stroke();
            }
            update() {
                this.vy += gravity;
                this.vx *= friction;
                this.vy *= friction;
                this.x += this.vx;
                this.y += this.vy;
                if (this.x + this.radius > canvasWidth) {
                    this.x = canvasWidth - this.radius;
                    this.vx *= -0.8;
                }
                if (this.x - this.radius < 0) {
                    this.x = this.radius;
                    this.vx *= -0.8;
                }
                if (this.y + this.radius > canvasHeight - boxHeight) {
                    this.y = canvasHeight - boxHeight - this.radius;
                    this.vy *= -0.8;
                }
                for (const other of circles) {
                    if (other !== this) {
                        const dx = this.x - other.x;
                        const dy = this.y - other.y;
                        const distance = Math.sqrt(dx * dx + dy * dy);
                        const minDistance = this.radius + other.radius;
                        if (distance < minDistance) {
                            const angle = Math.atan2(dy, dx);
                            const overlap = minDistance - distance;
                            // Separate the circles
                            this.x += Math.cos(angle) * (overlap / 2);
                            this.y += Math.sin(angle) * (overlap / 2);
                            other.x -= Math.cos(angle) * (overlap / 2);
                            other.y -= Math.sin(angle) * (overlap / 2);
                            // Simple collision response
                            const normalX = dx / distance;
                            const normalY = dy / distance;
                            const relativeVelocityX = this.vx - other.vx;
                            const relativeVelocityY = this.vy - other.vy;
                            const dotProduct = (relativeVelocityX * normalX) + (relativeVelocityY * normalY);
                            if (dotProduct > 0) {
                                const coefficientOfRestitution = 0.8; // Bounciness factor
                                const massSum = this.radius + other.radius;
                                const impulse = (2 * dotProduct) / massSum;
                                this.vx -= impulse * other.radius * normalX;
                                this.vy -= impulse * other.radius * normalY;
                                other.vx += impulse * this.radius * normalX;
                                other.vy += impulse * this.radius * normalY;
                            }
                        }
                    }
                }
            }
        }
        function generateRandomCircle() {
            const radius = Math.random() * (maxRadius - minRadius) + minRadius;
            const x = Math.random() * (canvasWidth - radius * 2) + radius;
            const y = Math.random() * -500 - radius;
            const vx = (Math.random() - 0.5) * 2;
            const vy = (Math.random() - 0.5) * 2;
            return new Circle(x, y, radius, vx, vy);
        }
        function createCircles() {
            for (let i = 0; i < numCircles; i++) {
                circles.push(generateRandomCircle());
            }
        }
        function drawBox() {
            ctx.fillStyle = 'rgba(100, 100, 100, 0.5)';
            ctx.fillRect(0, canvasHeight - boxHeight, canvasWidth, boxHeight);
        }
        function animate() {
            ctx.clearRect(0, 0, canvasWidth, canvasHeight);
            drawBox();
            for (const circle of circles) {
                circle.update();
                circle.draw();
            }
            requestAnimationFrame(animate);
        }
        createCircles();
        animate();
    </script>
</body>
</html>

 

1982090578_1722491753.477.png

 

이정도면 될것 같은데 이게 단순 math로 구현하기엔 변수가 너무 많습니다.

별도 플러그인도 해외에서 찾아보면 있을것 같기도 한데

구글신에게 의뢰해보세요..

아래의 코드 한번 참고를 해보시겠어요..

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Falling Animation</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
            margin: 0;
            overflow: hidden;
        }

        .container {
            position: relative;
            width: 300px;
            height: 400px;
            border: 2px solid #ccc;
            border-radius: 10px;
            overflow: hidden;
            background-color: white;
        }

        .shape {
            position: absolute;
            width: 50px;
            height: 50px;
            background-color: red;
        }

        .shape.square {
            border-radius: 0;
            background-color: blue;
        }

        .shape.rectangle {
            width: 30px;
            height: 60px;
            border-radius: 10px;
            background-color: green;
        }

        .shape.triangle {
            width: 0;
            height: 0;
            border-left: 25px solid transparent;
            border-right: 25px solid transparent;
            border-bottom: 50px solid yellow;
            background-color: transparent;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="shape"></div>
        <div class="shape square"></div>
        <div class="shape rectangle"></div>
        <div class="shape triangle"></div>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const shapes = document.querySelectorAll('.shape');
            const containerHeight = document.querySelector('.container').offsetHeight;

            shapes.forEach((shape, index) => {
                let delay = index * 0.5; // 각 도형마다 0.5초 간격으로 떨어지게 설정

                shape.style.top = `-50px`; // 초기 위치 위로 설정
                shape.style.left = `${Math.random() * 250}px`; // 무작위 위치 설정

                // 애니메이션 시작
                setTimeout(() => {
                    shape.style.transition = `top 2s ease-in-out`;
                    shape.style.top = `${containerHeight - shape.offsetHeight}px`; // 바닥까지 떨어지게 설정
                }, delay * 1000);
            });
        });
    </script>
</body>
</html>

 

 

 

js 로는 어렵습니다..

아예 불가능한건 아닌데..

물리엔진 구현에는 한계가 있어서요...

 

떨어져야 하는 위치가 정해져 있다면 js+css 만으로도 가능해보이고

수박게임 처럼 위치가 불규칙적이라면 단순 js 만으로는 불가합니다.

혹시 원하시는 형태로 정확히 동작하는 샘플 주소나 다른 코드 경로 찍어주실 수 있으세요~


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Falling Circles Simulation</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const circles = [];
        const numCircles = 6; // 떨어질 오브젝트 갯수
        const gravity = 0.1; // 적용될 중력
        const friction = 0.99; // 낙하 가속도
        const minRadius = 10; // 오브젝트 최소 크기
        const maxRadius = 100; // 오브젝트 최대 크기
        const canvasWidth = 500; // 캔바스 너비
        const canvasHeight = 500; // 캔바스 높이
        const boxHeight = 100; // border
        const Circlestexts = ['하나', '둘', '셋', '넷', '다섯', '여섯']; // 오브젝트 갯수만큼 텍스트 배열
        const Circlesbg = ['#fff100', '#cc0000', '#111111', '#444444', '#888888', '#000000']; // 오브젝트 갯수만큼 배경컬러 배열
        canvas.width = canvasWidth;
        canvas.height = canvasHeight;
        class Circle {
            constructor(x, y, radius, vx = 0, vy = 0, text = '', bgColor = '#fff') {
                this.x = x;
                this.y = y;
                this.radius = radius;
                this.vx = vx;
                this.vy = vy;
                this.text = text;
                this.bgColor = bgColor;
            }
            draw() {
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
                ctx.fillStyle = this.bgColor;
                ctx.fill();
                ctx.strokeStyle = 'rgba(0, 100, 200, 0.9)';
                ctx.stroke();
                // Draw text
                ctx.font = 'bold 20px Arial';
                ctx.fillStyle = '#000';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(this.text, this.x, this.y);
            }
            update() {
                this.vy += gravity;
                this.vx *= friction;
                this.vy *= friction;
                this.x += this.vx;
                this.y += this.vy;
                if (this.x + this.radius > canvasWidth) {
                    this.x = canvasWidth - this.radius;
                    this.vx *= -0.8;
                }
                if (this.x - this.radius < 0) {
                    this.x = this.radius;
                    this.vx *= -0.8;
                }
                if (this.y + this.radius > canvasHeight - boxHeight) {
                    this.y = canvasHeight - boxHeight - this.radius;
                    this.vy *= -0.8;
                }
                for (const other of circles) {
                    if (other !== this) {
                        const dx = this.x - other.x;
                        const dy = this.y - other.y;
                        const distance = Math.sqrt(dx * dx + dy * dy);
                        const minDistance = this.radius + other.radius;
                        if (distance < minDistance) {
                            const angle = Math.atan2(dy, dx);
                            const overlap = minDistance - distance;
                            // Separate the circles
                            this.x += Math.cos(angle) * (overlap / 2);
                            this.y += Math.sin(angle) * (overlap / 2);
                            other.x -= Math.cos(angle) * (overlap / 2);
                            other.y -= Math.sin(angle) * (overlap / 2);
                            // Simple collision response
                            const normalX = dx / distance;
                            const normalY = dy / distance;
                            const relativeVelocityX = this.vx - other.vx;
                            const relativeVelocityY = this.vy - other.vy;
                            const dotProduct = (relativeVelocityX * normalX) + (relativeVelocityY * normalY);
                            if (dotProduct > 0) {
                                const coefficientOfRestitution = 0.8; // Bounciness factor
                                const massSum = this.radius + other.radius;
                                const impulse = (2 * dotProduct) / massSum;
                                this.vx -= impulse * other.radius * normalX;
                                this.vy -= impulse * other.radius * normalY;
                                other.vx += impulse * this.radius * normalX;
                                other.vy += impulse * this.radius * normalY;
                            }
                        }
                    }
                }
            }
        }
        function generateRandomCircle(index) {
            const radius = Math.random() * (maxRadius - minRadius) + minRadius;
            const x = Math.random() * (canvasWidth - radius * 2) + radius;
            const y = Math.random() * -500 - radius;
            const vx = (Math.random() - 0.5) * 2;
            const vy = (Math.random() - 0.5) * 2;
            const text = Circlestexts[index];
            const bgColor = Circlesbg[index];
            return new Circle(x, y, radius, vx, vy, text, bgColor);
        }
        function createCircles() {
            for (let i = 0; i < numCircles; i++) {
                circles.push(generateRandomCircle(i));
            }
        }
        function drawBox() {
            ctx.fillStyle = 'rgba(100, 100, 100, 0.5)';
            ctx.fillRect(0, canvasHeight - boxHeight, canvasWidth, boxHeight);
        }
        function animate() {
            ctx.clearRect(0, 0, canvasWidth, canvasHeight);
            drawBox();
            for (const circle of circles) {
                circle.update();
                circle.draw();
            }
            requestAnimationFrame(animate);
        }
        createCircles();
        animate();
    </script>
</body>
</html>

 

이후엔 직접 한번 코드를 파보세요..

답변을 작성하시기 전에 로그인 해주세요.
전체 17,115
QA 내용 검색

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT