[HTML/canvas] Basic animations(2/2)

mdn의 캔버스 튜토리얼 따라하기
참고 : 캔버스 튜토리얼





파노라마

* ********************************
* 파노라마
* *********************************
*/
var img = new Image();

// 변수
img.src = "images/shinee_concert.jpg";
var CanvasXSize;
var CanvasYSize;
var speed = 1; // 애니메이션 업데이트 속도
var scale = 0.5;
var y = 0; // 수직 옵셋

var dx = 0.75; // 캔버스에서의 이동량(음수 왼쪽, 양수 오른쪽)
var imgW;
var imgH;
var x = 0; // 이미지 위치
var clearX;
var clearY;
var ctx;

img.onload = () => {
    // 캔버스 요소 열기
    ctx = document.getElementById("canvas3").getContext("2d");

    CanvasXSize = ctx.canvas.width;
    CanvasYSize = ctx.canvas.height;

    // 이미지 크기 조정
    imgW = img.width * scale;
    imgH = img.height * scale;

    // 이미지가 캔버스보다 크면 초기 위치 조정
    // 오른쪽으로 흘러가므로 시작 위치는 이미지의 오른쪽 끝
    if(imgW > CanvasXSize) {
        x = CanvasXSize - imgW;
    }

    // 캔버스 비우기 사이즈
    /* 굳이 비교해서 가져오는 이유는 
    * 1. 이미지 크기에 따라 캔버스 크기가 동적으로 전환될 수 있음
    * 2. 이미지의 일부분만 클리어하지 않도록 보장
    */
    clearX = Math.max(imgW, CanvasXSize);
    clearY = Math.max(imgH, CanvasYSize);

    // 화면 갱신 속도 설정
    return setInterval(drawAnni2, speed);
}

function drawAnni2() {
    // 캔버스 비우기
    ctx.clearRect(0,0,clearX, clearY);

    // 이미지가 캔버스보다 작거나 같다면
    if(imgW <= CanvasXSize) {
        // 재설정, 처음부터 시작
        if(x > CanvasXSize) {
            x = -imgW + x;
        }

        // 추가 이미지 그리기
        if(x > 0) {
            ctx.drawImage(img, -imgW + x, y, imgW, imgH);
        }

        if(x - imgW > 0) {
            ctx.drawImage(img, -imgW * 2 + x, y, imgW, imgH);
        }
    } else { 
        // 이미지가 캔버스보다 크다면
        if(x > CanvasXSize) {
            x = CanvasXSize - imgW;
        }

        // 추가 이미지 그리기
        if(x > CanvasXSize - imgW) { 
            ctx.drawImage(img, x - imgW + 1, y, imgW, imgH);
        }
    }

    // 이미지 그리기
    ctx.drawImage(img, x, y, imgW, imgH);

    // 움직임 정도
    x += dx;
}



마우스 따라오는 애니메이션

/* ********************************
* mouse following animation
* *********************************
*/
const cwCanvas = document.getElementById("cw");
const context = cwCanvas.getContext("2d");
context.globalAlpha = 0.5;

// 마우스커서 초기값
const cursor = {
    x: innerWidth / 2,
    y: innerHeight / 2,
};

console.log('innerWidth', innerWidth);


let particlesArray = [];

generateParticles(101);
setSize(); // 캔버스 크기 설정
anim();

// 마우스 움직임 이벤트시 좌표값 설정
addEventListener("mousemove", (e) => {
    cursor.x = e.clientX;
    cursor.y = e.clientY;
});

// 터치 이동 시 기본 동작 막고 cursor 객체 좌표값 업데이트
addEventListener(
    "touchmove",
    (e) => {
        e.preventDefault(); // 기본 동작 실행하지 않도록 지정
        cursor.x = e.touches[0].clientX;
        cursor.y = e.touches[0].clientY;
    },
    { passive: false },
);

// 창 크기 조절시 캔버스 크기 재조정
addEventListener("resize", () => setSize());

function generateParticles(amount) {
    for(let i=0; i<amount; i++) {
        particlesArray[i] = new Particle(
            innerWidth / 2,
            innerHeight / 2,
            4,
            generateColor(),
            0.02,
        );
    }
}

function generateColor() {
    let hexSet = "0123456789ABCDEF";
    let finalHexString = "#";
    for(let i=0; i<6; i++) {
        finalHexString += hexSet[Math.ceil(Math.random() * 15)];
    }
    return finalHexString;
}

function setSize() {
    cwCanvas.height = 500;
    cwCanvas.width = 500;
}

// 입자(초기 위치, 꼬리 너비, 색상, 회전 속도)
function Particle(x, y, particleTrailWidth, strokeColor, rotateSpeed) {
    this.x = x;
    this.y = y;
    this.particleTrailWidth = particleTrailWidth;
    this.strokeColor = strokeColor;
    this.theta = Math.random() * Math.PI * 2;
    this.rotateSpeed = rotateSpeed;
    this.t = Math.random() * 150;

    this.rotate = () => {
        // 입자의 이전 위치
        const ls = {
            x: this.x,
            y: this.y,
        };
        this.theta += this.rotateSpeed; // 회전각도
        this.x = cursor.x + Math.cos(this.theta) * this.t;
        this.y = cursor.y + Math.sin(this.theta) * this.t;
        this.y -= 300;
        context.beginPath();
        context.lineWidth = this.particleTrailWidth;
        context.strokeStyle = this.strokeColor;
        context.moveTo(ls.x, ls.y);
        context.lineTo(this.x, this.y);
        context.stroke();
    };
}

function anim() {
    requestAnimationFrame(anim);

    context.fillStyle = "rgb(0 0 0 / 5%)";
    context.fillRect(0, 0, cwCanvas.width, cwCanvas.height);

    particlesArray.forEach((particle) => particle.rotate());
}