[Canvas-불꽃놀이-06] TextData 클래스

이전 글 : 2024.11.18 - [개발/Canvas] - [Canvas-불꽃놀이-05] 화면 전환, 입력 이벤트 정의

 

[Canvas-불꽃놀이-05] 화면 전환, 입력 이벤트 정의

이전글 : 2024.11.17 - [개발/Canvas] - [Canvas-불꽃놀이-04] Canvas 설정 - 물리적 크기와 CSS 크기 [Canvas-불꽃놀이-04] Canvas 설정 - 물리적 크기와 CSS 크기이전 글 : 2024.11.16 - [개발/Canvas] - [Canvas-불꽃놀이-03

jinsk-joy.tistory.com

 

파티클을 그릴때 필요한 데이터를 얻기 위해 TextData 클래스 생성

1. TextData class 생성

  • 유저가 입력한 text를 캔버스에 그리고 픽셀 데이터를 가져오기 위해 TextData 클래스를 생성하였다.
  • Canvas의 크기 설정과 관련된 속성을 사용하기 위해 CanvasOption을 상속하였다.
  • strokeStyle을 black로 한 이유는 현재 배경색이 black이므로 혹시나 화면에 남아도 보여지지 않게 하기 위해서다.
// CanvasOption 상속
class TextData extends CanvasOption {
    /**
     * 사용자가 입력한 Text를 캔버스에 그리고 픽셀 데이터를 생성하기 위한 클래스
     * @param {string} userInput
     * @param {number} fontSize
     */
    constructor(userInput, fontSize) {
        super();

        // 사용자가 입력한 text데이터와 설정할 폰트 사이즈
        this.text = userInput;
        this.fontSize = fontSize;
        
        // ctx의 font관련 속성을 설정
        this.ctx.font = this.setFontStyle(fontSize);
        this.ctx.textAlign = "center";
        this.ctx.textBaseline = "middle";
        this.ctx.strokeStyle = "black";
        
        // text를 그린 후 픽셀데이터를 저장하기 위한 멤버변수
        this.textPixelData = {};
    }
 }

CanvasOption을 상속하여 

  • ctx의 font 속성을 설정할때 반복되는 코드를 줄이기 위해 CanvasOption 클래스에 폰트 스타일을 반환하는 메서드를 추가 하였다.
// CanvasOption.js에 추가
setFontStyle(fontSize) {
    return `${fontSize}px ${FONT.FAMILY}`; 
}

// constant.js FONT 객체에 추가
 FAMILY: "Do Hyeon" // 구글에서 로드한 폰트 명

 

2. 캔버스에 텍스트 그리기

  • 사용자가 입력한 텍스트를 캔버스에 그리고 불꽃놀이 파티클 생성을 위한 픽셀 데이터를 받아오는 메서드를 구현했다.
  • ctx.measureText로 캔버스에 그린 이미지의 width와 fontBoundingBoxAscent, fontBoundingBoxDescent 데이터를 가져온 이유는 이미지 데이터를 가져올때 전체가 아닌 Text가 그려진 위치를 계산하기 위해서다.
    • fontBoundingBoxAscent : 베이스 라인으로 부터 텍스트의 위쪽 끝까지의 거리 
    • fontBoundingBoxDescent : 베이스 라인으로 부터 텍스트 아래쪽 끝까지의 거리
    • 아무리 ctx의 textBaseline을 middle로 해놓는다고 해도 영문, 한글 또는 글씨체에 따라 위로 올라갈수도 아래로 내려갈수 있으므로 안전 마진으로 더해주는 것이 정확한 데이터를 가져올 수 있다.
  • ctx.getImageData로 텍스트 이미지의 픽셀 데이터를 가져올 때 x, y 좌표
    • x좌표 : 텍스트가 화면 정중앙에 그려지므로 그려진 텍스트의 절반 길이를 빼줘야 시작점을 알 수 있다.
    • y좌표 : 텍스트가 그려진 높이에 텍스트의 높이의 절반 길이를 빼줘야 시작점을 알 수 있다. 텍스트의 높이는 대체로 fontSize와 거의 유사하지만 좀 더 정확한 데이터를 가져오기 위해 fontBoundingBoxAscent를 빼줘 기준점을 높였다.
  • ctx.getImageData로 텍스트 이미지의 픽셀 데이터를 가져올 때 dpr을 곱한 이유 
    • ctx.measureText를 통해 화면에 렌더링 되는 글자의 데이터는 캔버스 CSS 크기 기준이다.
    • ctx.getImageData를 통해 가져오는 글자의 데이터는 캔버스 물리적 크기 기준이다. 고해상도 디스플레이에서도 정확한 데이터 추출을 위해 dpr 값을 곱해 물리적 크기로 변환해야 한다.
// TextData.js
drawText() {
    // 다른 데이터가 섞이는 것을 방지하기 위해 캔버스 전체 화면을 지우고 시작한다.
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    
    // stroke 스타일로 x, y 좌표 위치에 사용자가 입력한 텍스트를 그린다. 
    this.text.strokeText(this.text, this.mainX, this.mainY);

    // measureText 메서드를 통해 텍스트의 크기 데이터 값을 가져온다.
    // (fontBoundingBoxAscent와 fontBoundingBoxDescent를 사용하여 텍스트 주위에 안전 마진을 추가)
    const { width, fontBoundingBoxAscent = 0, fontBoundingBoxDescent = 0 } 
    	= this.ctx.measureText(this.text);

    // 이미지 데이터를 가져올때 필요한 좌표와 이미지 크기 정보
    const x = (this.mainX - width / 2) * this.dpr;
    const y = (this.mainY - this.fontSize / 2 - fontBoundingBoxAscent) * this.dpr;
    const imgWidth = width * this.dpr;
    const imgHeight = (this.fontSize + fontBoundingBoxDescent) * this.dpr;

    // measureText를 통해 가져온 width값이 유효한 값일때만 이미지를 가져온다.
    if (width) {
    	// 불러온 이미지 데이터 저장
        this.textPixelData = this.ctx.getImageData(x, y, imgWidth, imgHeight);
        
        // measureText를 통해 가져온 
        // text의 fontBoundingBoxAscent, fontBoundingBoxDescent 값을 textPixelData에 추가한다. 
        // 이미지 데이터를 가져올 시 y와 imgHeight에 위 값들을 포함하여 반영했기 때문에
        // 추후 TextParticle 렌더링 시 파티클이 정확한 곳에 위치하도록 보정하기 위함이다.
        this.textPixelData.fontBoundingBoxAscent = fontBoundingBoxAscent;
        this.textPixelData.fontBoundingBoxDescent = fontBoundingBoxDescent;
    }
}

화면에 그리기 & 데이터 가져오기

 

Github 링크