JavaScript Canvas의 초공간 여행

우리는 Starwars에서 멋진 초공간 비주얼을 보았습니다. 여기서 우리는 Javascript 캔버스를 사용하여 동일한 것을 모방할 것입니다.

따라서 먼저 html 파일에 canvas 요소를 만들고 javascript를 보관할 script 태그도 만듭니다.

<body>
    <canvas></canvas>
    <script></script>
</body>


그런 다음 script 태그에서 캔버스 요소를 얻습니다.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");






이제 Star 에 대한 청사진을 정의해 보겠습니다. 각 별에는 위치 (x,y) , radius , color 및 캔버스에 별을 그리는 draw 함수가 있습니다.

function Star(x,y,radius,color){
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.draw = function (){
        ctx.strokeStyle = color;
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
        ctx.stroke();
        ctx.fill();
    }
}


별 개체를 보관할 배열을 만들고 const 생성할 별의 수를 지정합니다.

const NUMBER_OF_STARS = 1000;
let stars = [];


별을 만드는 함수를 작성하고 임의의 위치를 ​​사용하므로 간단한 난수 생성기를 사용할 수 있습니다.


function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min) + min);
}

function createStars() {
    for (let counter = 0; counter < NUMBER_OF_STARS; counter++) {
        let radius = getRandomInt(1, 3);
        let x = getRandomInt(radius, (canvas.width - radius));
        let y = getRandomInt(radius, (canvas.height - radius));
        let color = "lightblue";
        stars.push(
            new Star(x, y, radius, color)
        );
    }
}


화면에 그리는 또 다른 빠른 루프.

createStars();
stars.forEach(star => star.draw());


반지름이 다른 별을 만들면 멀리 있는 사람은 거의 없고 가까이 있는 사람은 거의 없는 것처럼 느껴집니다.

이제 중요한 내용인 초공간으로 점프하는 방법입니다.

움직임



영화 장면에서 우리는 광속으로 여행할 때 각 별이 해당 위치에서 화면 가장자리로 이동한다는 것을 알 수 있지만 angle 또는 linepath를 계산해야 합니다.

이를 단순화하기 위해 중심점을 가정할 수 있으며 중심에서 별까지 선을 그리면 기본적으로 별이 화면 가장자리에 도달할 때까지 따라가는 경로입니다. 아래와 같이



이제 각 라인에 두 개의 포인트가 있습니다. 첫 번째 포인트는 화면의 중심이고 두 번째 포인트는 별의 위치입니다. 다음 공식을 사용하여 나중에 별을 이동하는 데 사용할 수 있는 선의 slope를 계산할 수 있습니다.


m=(y2−y1)(x2−x1)
m =\frac {(y_2-y_1)} {(x_2-x_1)}
m=(x2 −x1 )(y2 −y1 )

따라서 slope 속성을 Star에 추가하겠습니다.

function Star(x,y,radius,color, slope){
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
    this.slope = slope;
    this.draw = function (){
        ctx.strokeStyle = color;
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
        ctx.stroke();
        ctx.fill();
    }
}


다음과 같이 함수를 정의합니다.

function slopeFromCenter(x, y) {
    let x1 = x;
    let y1 = y;
    let x2 = centerX;
    let y2 = centerY;
    return (y2 - y1) / (x2 - x1);
}


이제 Star Object 생성을 다음과 같이 변경하십시오.

stars.push(new Star(x, y, radius, color, slopeFromCenter(x, y)));


애니메이션 루프



애니메이션은 프레임마다 한 번씩 호출되는 requestAnimationFrame 메서드를 사용하고 스페이스바 키를 사용하여 이동을 시작하고 중지하기 위해 다음과 같이 변수travel를 생성합니다.

let travel = null;


기본jump 메서드도 생성하고 나중에 변경할 수 있습니다.

function jump() {

    for (let i = 0; i < stars.length; i++) {
        const star = stars[i];
        ctx.beginPath();
        ctx.moveTo(star.x, star.y);
        // we will work these in a minute
        const nextXY = [star.x + 1, star.y + 1];
        const nextX = nextXY[0];
        const nextY = nextXY[1];
        ctx.lineTo(nextX, nextY);
        ctx.strokeStyle = star.color;
        ctx.lineWidth = star.radius;
        ctx.stroke();
        ctx.closePath();
        star.x = nextX;
        star.y = nextY;
    }
}


우리가 하는 일은 단순히 1를 별의 좌표xy에 더하는 것뿐입니다.

let travel = null;
function animate() {
    travel = requestAnimationFrame(animate);
    jump();
}

window.addEventListener("keypress", function (event) {
    if (event.key === " ") {
        if (travel == null) {
            travel = window.requestAnimationFrame(animate);
        } else {
            window.cancelAnimationFrame(travel);
            travel = null;
        }
    }
});



xy 값을 1씩 증가시키면서 별이 오른쪽 하단 모서리로 이동하는 것을 볼 수 있습니다. 이제 다음과 같이 nextStop 함수를 정의하여 실제 이동 코드를 수행해 보겠습니다.

function nextStop(currentX, currentY, m, velocity) {
    let s = velocity / Math.sqrt(1 + m * m);
    let nextX = currentX + s;
    let nextY = currentY + m * s;
    return [Math.ceil(nextX), Math.ceil(nextY)];
}


이 함수는 단순히 (x,y) , slopevelocity 를 취하고 새로운 좌표를 반환합니다. 속도를 조정하여 이동 속도도 조정할 수 있습니다.

다음과 같이 jump 함수를 사용하도록 nextStop 함수를 업데이트합니다.

function jump() {

    for (let i = 0; i < stars.length; i++) {
        const star = stars[i];
        ctx.beginPath();
        ctx.moveTo(star.x, star.y);
        // update the following line
        const nextXY = nextStop(star.x, star.y, star.slope, 20);
        const nextX = nextXY[0];
        const nextY = nextXY[1];
        ctx.lineTo(nextX, nextY);
        ctx.strokeStyle = star.color;
        ctx.lineWidth = star.radius;
        ctx.stroke();
        ctx.closePath();
        star.x = nextX;
        star.y = nextY;
    }
}




하지만...



보시다시피 오른쪽 절반은 오른쪽이고 왼쪽 절반은 화면이 반전되어 있습니다. 이것은 우리가 계산한 기울기 때문입니다. 해당 업데이트를 수정하려면 다음과 같이 slopeFromCenter 함수를 업데이트하십시오.

function slopeFromCenter(x, y) {

    let x1 = x;
    let y1 = y;
    let x2 = centerX;
    let y2 = centerY;
    let q = null; // Quadrant
    let direction = 1;

    if (x > centerX && y <= centerY) {
        q = "Q1";
    } else if (x <= centerX && y <= centerY) {
        q = "Q2";
    } else if (x <= centerX && y > centerY) {
        q = "Q3";
    } else if (x > centerX && y > centerY) {
        q = "Q4";
    }

    if (q == "Q2" || q == "Q3") {
        direction = -1;
    }

    return [(y2 - y1) / (x2 - x1), direction];
}


그리고 다음과 같이 createStars 메서드를 업데이트합니다.

function createStars() {
    for (let counter = 0; counter < NUMBER_OF_STARS; counter++) {
        let radius = getRandomInt(1, 4);
        let x = getRandomInt(radius, (canvas.width - radius));
        let y = getRandomInt(radius, (canvas.height - radius));
        let color = "white";
        let slopeAndDirection = slopeFromCenter(x, y);
        stars.push(
            new Star(x, y, radius, slopeAndDirection[0], slopeAndDirection[1], color)
        );
    }
}


또한 jump 기능을 업데이트하여 방향을 사용하고 별이 화면 가장자리에 도달하면 임의의 위치에서 별을 다시 생성합니다. 새 개체를 만드는 대신 단순히 위치를 변경하여 새 별처럼 느껴지도록 합니다. 메모리 및 성능에 더 좋습니다.

function jump() {
    for (let i = 0; i < stars.length; i++) {
        const star = stars[i];
        if (
            star.x <= 0 ||
            star.x > canvas.width ||
            star.y <= 0 ||
            star.y > canvas.height
        ) {
            star.x = Math.ceil(
                Math.random() * (window.innerWidth - star.radius * 2 + star.radius)
            );
            star.y = Math.ceil(
                Math.random() * (window.innerHeight - star.radius * 2 + star.radius)
            );
            const slopeAndDirection = slopeFromCenter(star.x, star.y);
            star.slope = slopeAndDirection[0];
            star.direction = slopeAndDirection[1];
        }
        ctx.beginPath();
        ctx.moveTo(star.x, star.y);
        const nextXY = nextStop(
            star.x * star.direction,
            star.y * star.direction,
            star.slope,
            20
        );
        const nextX = nextXY[0];
        const nextY = nextXY[1];
        ctx.lineTo(nextX * star.direction, nextY * star.direction);
        ctx.strokeStyle = star.color;
        ctx.lineWidth = star.radius;
        ctx.stroke();
        ctx.closePath();
        star.x = nextX * star.direction;
        star.y = nextY * star.direction;
    }
}


이제 잘 작동하겠지만... 시간이 지남에 따라 화면이 완전히 채워질 것입니다... 멋진 애니메이션을 만들기 위해 animate 기능을 수정합니다. 여기서는 ctx.fillRect를 사용하여 화면이 완전히 채워지는 것을 방지하는 검은색 투명 색상으로 화면을 채웁니다.

function animate() {
    travel = requestAnimationFrame(animate);
    ctx.fillStyle = "rgba(0,0,0,0.1)";
    ctx.fillRect(0, 0, window.innerWidth, innerHeight);
    jump();
}


최종 결과에 액세스할 수 있습니다here.
시작하거나 중지하려면 Spacebar를 누르십시오.+ 속도를 높이는 키,- 속도를 늦추고 여행을 즐기는 열쇠 :)

기타 개선 사항이 거의 없는 전체 코드가 링크됨here

고맙습니다...

좋은 웹페이지 즐겨찾기