Skip to content

Knowledge: In Upload

kimyoungyin edited this page Jun 24, 2022 · 5 revisions

svg에 ReactComponent로 가져오고, theme의 값(색)을 적용하기

stackoverflow: How to get the theme outside styled-components?

  1. 채울 svg 속성을 svg 파일에서 "current"로 변경
// cut.svg -> CutIcon
<svg
    aria-label="자르기 선택"
    fill="current" // 이렇게!
    height="16"
    role="img"
    viewBox="0 0 24 24"
    width="16"
>
    <path
        d="M10 20H4v-6a1 1 0 00-2 0v7a1 1 0 001 1h7a1 1 0 000-2zM20.999 2H14a1 1 0 000 2h5.999v6a1 1 0 002 0V3a1 1 0 00-1-1z"
    ></path>
</svg>
  1. styled-component가 주는 useTheme() hook을 이용하여 styled-component 밖에서도 theme 객체를 읽어내어 svg ReactComponent에 적용
import styled, { useTheme } from "styled-components";

const Cut = ({ currentWidth }: CutProps) => {
    const theme = useTheme();
    
    // return문 일부에서(svg ReactComponent)
                    <button>
                        <CutIcon
                            fill={
                                handlingMode === "ratio"
                                    ? theme.font.default_black
                                    : theme.color.bg_white
                            }
                        />
                    </button>
// ... 생략
}

line-gradient로 input[type="range"] 표현하기

커스텀으로 input range 스타일을 바꾸고 싶을 때 사용합니다.

  1. 기존 스타일 초기화(배경 퍼센트 색 제거 포함)
& > input[type="range"] {
    -webkit-appearance: none;
    width: 100%;
    background: transparent;
}

& > input[type="range"]:focus {
    //  blue border 제거
    outline: none;
}
& > input[type="range"]::-ms-track {
    width: 100%;
    // 투명
    background: transparent;
    border-color: transparent;
    color: transparent;
}
& > input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;  // 동그란 원 기본 속성 제거
}
  1. 각각 커스텀 스타일(생략)

  2. react two way binding

value={scale}
onChange={scaleChangeHandler}
  1. linear-gradient로 value값 배경색으로 나타내줌

주의: linear-gradient의 첫 번째 퍼센트가 작으면 그라데이션 처리되므로, 50%를 기준으로 to right, to left로 나눠줌

style={{
    background:
        scale >= 50
            ? `linear-gradient(to right, white ${scale}%, black ${
                  100 - scale
              }%)`
            : `linear-gradient(to left, black ${
                  100 - scale
              }%, white ${scale}%)`,
}}
스크린샷 2022-02-22 오후 7 15 26

Canvas와 이미지

Canvas의 사용 용도: 복잡한 그래픽 애니메이션 및 이미지 조작(화질 개선, 필터 적용 등)을 위해 사용됩니다.

width, height

.canvas {
    width:400px;
    height:200px;
}
<canvas width={400} height={200}></canvas>

캔버스에 css로 width, height ≠ canvas 태그 내부 속성 width, height

  • css로 적용한 width, height: html 좌표계 -> 현재 브라우저에 보이는 픽셀을 기준으로 설정된 값으로, 우리가 기존에 사용하던 개념과 동일합니다.
  • canvas 태그 내부 속성 width, height: 캔버스 내부 좌표계 -> 캔버스 내부를 몇 개의 픽셀로 구성할 것인가에 대한 값입니다. 자바스크립트로 그려질 캔버스 크기관련 픽셀값은 모두 이 width, height를 기준으로 그려집니다.

응용(고해상도 표현): 기존보다 2배 더 고해상도로 보여주고 싶다면, canvas의 width와 height를 2배로 키우고 그린 다음에 css로 1/2로 줄여줍니다. 해상도가 2배가 됩니다.

참고 영상: 1분코딩: HTML5 Canvas 강의1(17분 부근)

Canvas를 지원하지 않는 브라우저에서 보여줄 요소

<canvas>보여줄 요소</canvas>

그릴 Canvas(2d 기준 렌더링 컨텍스트) 가져오기

  • 렌더링 컨텍스트(CanvasRenderingContext2D): Canvas 태그에서 가져온 객체임과 동시에, canvas에 그릴 여러 프로퍼티를 갖고 있는 객체
// js
const canvas = document.getElementById('tutorial');
const ctx = canvas.getContext('2d');

// react, ts: useRef()와 옵셔널 처리
const canvasRef = useRef<HTMLCanvasElement>(null);

if (!canvasRef.current) return;
const canvas = canvasRef.current;
const context = canvas.getContext("2d");
if (context) {
// ...
}

Canvas에 이미지 그리기: CanvasRenderingContext2D.drawImage()

  • parameters: 3개 혹은 5개 혹은 9개가 가능합니다. 저는 5개를 사용하였습니다.

drawImage의 첫번째 파라미터는 이미지 src가 아니라 '이미지 관련 객체'를 사용하며, 보통 다음과 같이 생성자 함수로 객체를 생성한 후 사용합니다.

// 제 코드의 일부분입니다.
const img = new Image(currentImageWidth, currentImageHeight); // url만으로 canvas에 이미지를 그릴 수 없습니다. 생성자 함수를 통해 새로운 img 객체를 만들어줍니다.
img.src = currentFile.url;
// img 객체가 load될 때까지 기다린 후에 img 객체를 사용할 수 있으므로, 약간의 딜레이가 발생할 수 있습니다.
img.onload = () => {
// 캔버스에 넣기 등 img 객체를 사용한 작업
}
  • 이미지의 일부분만 그리려면?: (1)sx, sy로 원본 이미지의 일부를 잘라 canvas에 그리거나, (2)dx, dy를 음수로 조정하여 캔버스 밖에서 그리기 시작하는 방법

저는 (2)를 선택했습니다.

canvas_drawimage

// drawImage 코드
context.drawImage(
    img,
    -(
    currentImageWidth / 2 -
    currentCanvasWidth / 2 -
    currentFile.translateX
    ),
    -(
    currentImageHeight / 2 -
    currentCanvasHeight / 2 -
    currentFile.translateY
    ),
    img.width,
    img.height,
);

꼭 그리기 '전'에 context의 필터를 적용해야 합니다. Canvas API가 기본적으로 제공하는 api를 적용했습니다.

// useEffect에서 구현
        if (!canvasRef.current) return;
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d");
        if (context) {
            const img = new Image(currentImageWidth, currentImageHeight); // url만으로 canvas에 이미지를 그릴 수 없습니다. 생성자 함수를 통해 새로운 img 객체를 만들어줍니다.
            img.src = currentFile.url;
            img.onload = () => {
                context.clearRect(
                    0,
                    0,
                    currentCanvasWidth,
                    currentCanvasHeight,
                );
                // 이미지 로드가 완료되었을 떄 함수가 실행됩니다.
                // 이미지 자체의 시작지점(sx,sy)를 조작하면 이미지 크기가 초기화되버리므로,
                // canvas에 그리기 시작하는 좌표(dx,dy)를 조작하여 간접적으로 translate를 구현합니다.
                context.filter = `brightness(${
                    currentFile.brightness / 3 + 100
                }%) contrast(${currentFile.contrast / 3 + 100}%) saturate(${
                    currentFile.saturate + 100
                }%)
                blur(${currentFile.blur / 50}px)
                `;
                context.drawImage(
                    img,
                    -(
                        currentImageWidth / 2 -
                        currentCanvasWidth / 2 -
                        currentFile.translateX
                    ),
                    -(
                        currentImageHeight / 2 -
                        currentCanvasHeight / 2 -
                        currentFile.translateY
                    ),
                    img.width,
                    img.height,
                );
            };
        }

클릭된 요소로부터 상대적인 마우스 위치 계산하기

이미지 클릭 이벤트라면

const changeSearchBarPosition = (
        event: React.MouseEvent<HTMLImageElement>,
    ) => {
        const { clientX, clientY, currentTarget } = event; // clientX, clientY는 브라우저 왼쪽, 위로부터 마우스까지 거리
        const {
            x: left, // 브라우저 왼쪽으로부터 클릭한 요소까지 거리
            y: top, // 브라우저 위쪽으로부터 클릭한 요소까지 거리
            width, // 클릭한 요소의 너비
            height, // 클릭한 요소의 높이
        } = currentTarget.getBoundingClientRect(); // getBoundingCientRect()는 클릭된 요소 관련 좌표를 얻을 수 있다
        const x = ((clientX - left) / width) * 100; // %(비율) 즉, 상대값으로 계산하면 브라우저 resize event에도 대응할 수 있다.
        const y = ((clientY - top) / height) * 100;
        setSearchBarPosition({ x, y }); // 상대적인 마우스 위치에 뭔가 띄우고 싶다면 setState를 통해 스타일에 적용(인라인 등등)
    };

참고 사이트 좌표 마우스 클릭 좌표

Clone this wiki locally