최근 개발 공부를하면서, canvas로 이미지 다루는 것을 공부하여 기록하고자 한다.
MDN 내용 중(中)
canvas
는 처음에는 src 및 alt 속성이 없다는 점만 제외하면 img
요소처럼 보인다. canvas
요소에는 width와 height의 두 속성만 있다, 이것들은 모두 선택사항이며 DOM 프로퍼티를 사용하여 설정할 수도 있다. 한마디로 정리하자면,
canvas
는 웹페이지에서 그래픽을 그릴때 사용하는 컨테이너이다.
canvas
엘리먼트는 고정 크기의 드로잉 영역을 생성하고 하나 이상의 렌더링 컨텍스(rendering contexts)를 노출하여, 출력할 컨텐츠를 생성하고 다루게 되며, 프론트엔드에서 많이 사용하는것은 2D
렌더링 컨텍스트를 다룬다. (WebGL은 OpenGL ES 을 기반으로 하는 3D 컨텍스트를 사용한다.)
캔버스는 처음에 비어있다. 무언가를 표시하기 위해서, 어떤 스크립트가 랜더링 컨텍스트에 접근하여 그리도록 한다. canvas
요소는 getContext() 메서드를 이용해서, 랜더링 컨텍스트와 (렌더링 컨텍스트의) 그리기 함수들을 사용할 수 있다. getContext() 메서드는 렌더링 컨텍스트 타입을 지정하는 하나의 파라미터를 가지는데, 위에서 언급한거처럼 프론트엔드에서 많이 사용되는 것은 "2d"이다.
간단하게 canvas
에 대해서 알아보았고, 본론으로 넘어가보도록 한다.
CSS 작업을 하면서, 모달로 이미지를 띄우는 경우, 배경화면 색상에 대해서 한번쯤은 고민을 하게된다.
"이 이미지에는 어떤 배경이 잘 어울리지?" , "opacity 처리하기에는 너무 밋밋한데 좋은 방법이 없을까?" 등등
위와 같이 사진 리스트들이 있고, 아무 사진을 클릭했을때, 클릭한 사진이 모달 형식으로 보여지게 되는데, 배경화면은 검은색 화면에 opacity만 적용시켰다.
하지만 여기서 이미지와 배경색깔을 비슷하게 맞춰서 UI가 조금더 낫게 만들수 있다.
이미지와 배경색을 어떻게 비슷하게 맞추면 좋을까?
Get average color of image via Javascript 여기서 많은 사람들이 여러가지 해결방안을 알려줬는데, 그걸 토대로 배경 색상을 구해보고자 한다.
// 첫번째 방법
function getAverageColorOfImage(imgElement) {
// imgElement -> <img src='https://...' alt='...' />
const canvas = document.createElement('canvas'); // canvas element 생성
const context = canvas.getContext && canvas.getContext('2d'); // 2d 그래픽을 그릴 수 있는 메서드를 지닌 HTML5 object
const averageColor = { r: 0, g: 0, b: 0 };
if (!context) return averageColor;
const width = (canvas.width = imgElement.naturalWidth || imgElement.offsetWidth || imgElement.width);
const height = (canvas.height = imgElement.naturalHeight || imgElement.offsetHeight || imgElement.height);
context.drawImage(imgElement, 0, 0); // drawImage는 캔버스에서 이미지를 그려준다.
const imageData = context.getImageData(0, 0, width, height).data; // 지정된 좌표와 폭과 높이를 갖는 사각형으로 표시된 캔버스 영역에 대한 기본 픽셀 데이터를 나타내는 ImageData 객체를 반환한다.
const length = imageData.length;
for (let i = 0; i < length; i += 4) {
averageColor.r += imageData[i];
averageColor.g += imageData[i + 1];
averageColor.b += imageData[i + 2];
}
const count = length / 4;
averageColor.r = ~~(averageColor.r / count);
averageColor.g = ~~(averageColor.g / count);
averageColor.b = ~~(averageColor.b / count);
return averageColor;
}
//두번째 방법
function getAverageColorOfImage(imgElement) {
if (cache.hasOwnProperty(imgElement.src)) return cache[imgElement.src];
const context = document.createElement('canvas').getContext('2d');
context.drawImage(imgElement, 0, 0, 1, 1);
const i = context.getImageData(0, 0, 1, 1).data;
const rgba = `rgba(${i[0]},${i[1]},${i[2]},0.8)`;
// const HEX = '#' + ((1 << 24) + (i[0] << 16) + (i[1] << 8) + i[2]).toString(16).slice(1);
return rgba;
}
필자는 첫번째 방식으로 적용했고, 그 결과는 아래와 같다.
여기서 한가지 유의해야할 점이 있다.
이미지를 클릭했을때, 모달이 떠야하는 상황이기 때문에, img태그에 onClick 이벤트를 달아주어야 하는데, 이때 꼭 img에 Cross Origin 속성 설정을 해줘야 한다.
function PhotoComponent({ photo: { urls, alt } }) {
const openModal = e => {
const averageColor = getAverageColorOfImage(e.target);
{...}
};
return (
<img
{...}
crossOrigin="*" // 이 부분 설정
onClick={openModal}
/>
);
}
export default PhotoComponent;
위와 같이 설정하지 않고 개발을 하다보면 CORS 에러가 생기는데 이유는 다음과 같다.
Canvas를 사용할 때 CORS 승인 없이 이미지를 사용할 수는 있지만,
이미지를 변형한 상태에서 데이터를 캔버스에서 꺼내려 한다면
“canvas tainted” 관련 오류가 발생할 것이다.
누군가가 보았을때는, 사소한 부분이지만, 유저 친화적으로 접근했을때, 꽤 괜찮은 방식의 스타일링이라고 생각한다.
[React] Canvas에서 CORS 에러가 왠 말이야!!
Get average color of image via Javascript
웹 개발 스킬을 한 단계 높여주는 프론트엔드 성능 최적화 가이드 -유동규 지음-