[Canvas-불꽃놀이-03] Canvas 설정하기

이전글 : [Canvas-불꽃놀이-02] UI 구성

 

[Canvas-불꽃놀이-02] UI 구성

이전 글 : 2024.11.15 - [개발/Canvas] - [Canvas-불꽃놀이-01] 프로젝트 초기화 [Canvas-불꽃놀이-01] 프로젝트 초기화글자를 입력하면 불꽃놀이처럼 빵빵 터지는 효과를 만들어보고 싶어져서 시작하게 되

jinsk-joy.tistory.com

 

캔버스 기본 세팅

1. 캔버스 설정하기

대략적인 UI 구성이 완료되어 canvas를 설정하고자 한다.

  • Canvas Class : 캔버스에 그림을 그리는 로직, 애니메이션, 파티클 생성을 담당해 화면에 실제로 그리는 핵심 로직을 담당
  • CanvasOption Class : 캔버스의 ctx, 크기, 색상, 폰트, 해상도 등 기본 옵션을 정의하고 관리

이렇게 옵션 설정과 그리기를 분리하여 한 클래스에 모든 로직이 집중되지 않게 하고 모듈화를 통해 재사용성과 유지보수를 용이하게 만들었다. 프로젝트의 확장이 필요할 때도 비교적 유연하게 대응할 수 있다.

 

2. CanvasOption 클래스

2-1. 캔버스 객체를 불러오고 애니메이션 기본 설정 해주기

  • 캔버스 객체를 생성하고 만약 실패시 에러를 던진다.
  • 상수값 관리를 편하게 하기 위해 상수 파일을 생성해 사용하였다.
  • interval : 설정된 fps 기반으로 각 프레임 사이의 시간 간격을 계산한다. 예를 들어 1초에 60 프레임을 렌더 하면 대략 16.67 밀리초 간격으로 프레임이 업데이트된다.
class CanvasOption {
    constructor() {
        this.canvas = document.getElementById("canvas");
        if (!this.canvas) {
            throw new Error("캔버스 객체를 발견하지 못했습니다. 다시 확인해주세요.");
        }
        // 2d 컨텍스트를 불러오고 이미지를 그릴 때 성능 최적화를 위해 willReadFrequently 설정 추가
        this.ctx = this.canvas.getContext("2d", { willReadFrequently: true });

        // 초당 렌더링할 프레임 수
        this.fps = ANIMATION.FPS;

        // 프레임간 간격을 계산하여 각 프레임이 그려지는 시간 간격을 설정 (밀리초 단위)
        this.interval = 1000 / this.fps;
    }
}

 

2-2. 캔버스 멤버 변수 초기화

  • 리사이즈 이벤트가 일어나면 변경된 크기에 맞춰 캔버스의 설정을 변경해야 하므로 캔버스 사이즈 크기에 영향을 받는 멤버변수들은 따로 멤버변수 초기화 함수함수를 통해 설정
// CanvasOption.js
initCanvasOptionVars() {
    // 최대 3으로 설정하여 성능저하 방지
    this.dpr = Math.min(Math.round(window.devicePixelRatio), SCREEN.MAX_DPR) || 1;
    
    // 캔버스 태그 자체의 width, height
    this.canvasCssWidth = window.innerWidth;
    this.canvasCssHeight = window.innerHeight;
    
    // 모바일 화면 여부 판별
    this.isSmallScreen = window.matchMedia(`(max-width: ${SCREEN.SMALL_WIDTH}px)`).matches;
    
    // 메인 텍스트 기본 좌표값
    this.mainX = this.canvasCssWidth / POS.MAIN_X_DIVISOR;
    this.mainY = Math.floor(this.canvasCssHeight * POS.MAIN_Y_RATIO);
    
    // 텍스트별 폰트 사이즈
    this.mainFontSize = this.setMainFontSize();
    this.subFontSize = this.setSubFontSize();
}

/**
 * @param {number} fontSize
 * @returns {number} 메인 폰트 사이즈 반환 (작은 화면일 경우 별도의 비율 적용)
 */
setMainFontSize(fontSize) {
    if (!fontSize) {
    	// 캔버스 font에는 반응형 적용이 안되니 화면 크기에 따라 폰트 사이즈를 설정해 반응형같은 효과를 만듬
        const ratio = this.isSmallScreen ? FONT.MAIN_RATIO_SMALL : FONT.MAIN_RATIO_GENERAL;
        fontSize = ((this.canvasCssWidth + this.canvasCssHeight) / 2) * ratio;
	}

    return Math.round(fontSize);
}

/**
 * @returns {number} 서브 폰트 사이즈 반환
 */
setSubFontSize() {
    return Math.round(this.mainFontSize) * FONT.SUB_RATIO;
}

 

2-3. 설정 초기화를 위한 init 함수 생성

  • 클래스 인스턴스 생성할 때 초기화하고, 화면 사이즈 변경 시에도 영향을 받는 멤버 변수의 값을 업데이트하기 위해 초기화를 위한 init 메서드를 만들고 initCanvasOptionVars() 메서드를 실행시킨다.
class CanvasOption {
    constructor() {
        this.canvas = document.getElementById("canvas");
        if (!this.canvas) {
            throw new Error("캔버스 객체를 발견하지 못했습니다. 다시 확인해주세요.");
        }
        this.ctx = this.canvas.getContext("2d", { willReadFrequently: true });
        this.fps = ANIMATION.FPS;
        this.interval = 1000 / this.fps;
        // 멤버변수 초기화
        this.initCanvasOptionVars();
    }

    init() {
        this.initCanvasOptionVars();
    }
    // 생략 ......
  }

 

3. Canvas 클래스

  • CanvasOption 클래스에서 설정한 캔버스 옵션을 사용하기 위해 Canvas 클래스는 CanvasOption 클래스를 상속받는다.
  • 불꽃놀이 화면(canvas)에 진입 시 init 함수를 실행하여 캔버스 객체 멤버변수의 초기화를 진행한다.
  • 캔버스 화면에서 선명한 이미지 구현을 위해 캔버스의 크기를 물리적 크기와 CSS 크기를 같이 사용한다. dpr(Device Pixel Ratio)를 활용하여 캔버스의 픽셀 밀도를 높여 선명한 이미지를 렌더 할 수 있게 한다.
  • 캔버스 물리적 크기 설정 : 캔버스 CSS크기에 dpr을 곱해 물리적 크기를 증가시킨다.
  • 컨텍스트 스케일 조정 : 캔버스 렌더링 단위를 dpr만큼 확대하여 고해상도를 반영하도록 설정한다
    • dpr을 곱해서 캔버스 물리적 크기를 키우면 물리적인 픽셀 수는 늘어나나 캔버스의 렌더링 스케일은 물리적 크기를 키우기 전 사이즈다
    • 그래서 물리적 크기가 늘어난 만큼 캔버스 렌더링 스케일을 dpr 크기만큼 키우면 (픽셀 크기 == 렌더링 스케일)이 되니 고화질로 렌더링 할 수 있게 된다.
    • 예를 들어 캔버스 물리적 크기가 500px인데 dpr(2)를 곱하면 캔버스의 물리적 크기는 1000px이 된다. 이때 렌더링 스케일도 dpr에 맞춰 같이 확대하지 않으면 500px 기준으로 그려진 이미지를 1000px로 맞춰서 그려야 하니 이미지가 흐려지게 된다.
  • 캔버스 CSS 크기 설정 : 캔버스 태그의 스타일을 현재 창크기에 맞춰 확대된 픽셀 밀도에도 화면에 현재 창의 크기대로 보일 수 있게 유지한다.
class Canvas extends CanvasOption {
    consturctor() {
        super();
    }

    init() {
        // 리사이즈시 CanvasOption도 수정해야 하므로 부모클래스의 init 메서드도 실행한다.
        super.init();

        // 고해상도를 위한 캔버스 물리적 크기 설정
        this.canvas.width = this.canvasCssWidth * this.dpr;
        this.canvas.height = this.canvasCssHeight * this.dpr;
        this.ctx.scale(this.dpr, this.dpr);

        // 캔버스 CSS 크기는 선명한 이미지를 유지할 수 있도록 현재 창 크기로 설정
        this.canvas.style.width = `${this.canvasCssWidth}px`;
        this.canvas.style.height = `${this.canvasCssHeight}px`;
    }
}

 

Github 링크

 

GitHub - jinsk9268/text-fireworks

Contribute to jinsk9268/text-fireworks development by creating an account on GitHub.

github.com