본문 바로가기
Web Study/노마드코더

노마드코더 JS로 그림 앱 만들기 - 마지막

by 쿠리의일상 2023. 6. 10.

Meme Maker 구현하기

  <input type="file" accept="image/*" id="file" />

파일을 선택하는 인풋은 type 을 file 로,

여기선 영상은 필요하지 않으므로 accept 속성에 image/* 를 설정하여 이미지 포맷만 가능하게 지정한다.

// 파일 받기
function onFileChange(evt) {
  console.dir(evt.target);
}

fileInput.addEventListener('change', onFileChange);

파일을 선택하고 콘솔을 확인하면, target의 files 에 내가 넣어준 이미지가 존재함을 볼 수 있다.

이 파일은 이제 브라우저의 메모리에 존재하게 되고

브라우저의 url 을 받아오면 해당 이미지를 볼 수 있게 된다.

// 파일 받기
function onFileChange(evt) {
  const file = evt.target.files[0];
  const url = URL.createObjectURL(file);
  console.log(url);
}

fileInput.addEventListener('change', onFileChange);

target의 files 배열에 접근하여 넣어준 이미지가 0번째에 있으므로 읽어오고,

url 은 URL의 createObjectURL() 메서드로 접근할 수 있다. 해당 파일을 넣어주면 아래와 같은 콘솔이 뜬다.

blob:http://127.0.0.1:5500/4155fa20-bd5f-4701-beb5-8b362b928ed8

blob 을 포함한 내용을 주소창에 넣어주면 해당 이미지가 브라우저 메모리에 잘 읽혔음을 확인할 수 있다.

다만 이 주소는 현실의 인터넷에 존재하는 것이 아닌, 로컬 브라우저에서만 처리된 것이다.

 

// 파일 받기
function onFileChange(evt) {
  // console.dir(evt.target);
  const file = evt.target.files[0];
  const url = URL.createObjectURL(file);
  // console.log(url);

  const image = new Image(); // <img />
  image.src = url;
  image.onload = function() {
    ctx.drawImage(image, 0, 0, 500, 500);
  }
}

fileInput.addEventListener('change', onFileChange);

url 을 받아와서, new Image() 로 img 태그를 만들어주고, src 를 읽어온 url 로 넣어준다.

그다음 이미지가 onload (다 읽어와주면) 되면 콜백함수를 넣어줘서 실행되게 해준다.

컨텍스트의 drawImage(이미지태그, x, y, width, height) 로 넣어주면 된다.

 

Text 넣어주기

text 를 넣어주기 위한 input 필드와 캔버스를 더블 클릭하면 그 텍스트가 보이게끔 만드려고 한다.

그렇다면 텍스트 값을 가져와서 strokeText 나 fillText 메서드를 사용해주면 되는데,

이때 lineWidth 가 그림 그리는 크기에 맞춰져 있어서 글자가 깨져나오게 된다.

이를 막기 위해선 글자를 출력할 땐 lineWidth 를 1로 바꾸고, 원래의 설정값으로 돌아가게 만드는 방법을 써야 하는데

이를 위한 메서드가 save 와 restore 이다.

function onDoubleClick(evt) {
  ctx.save();
  const txt = textInput.value;
  ctx.lineWidth = 1;
  ctx.strokeText(txt, evt.offsetX, evt.offsetY);
  ctx.restore();
}

canvas.addEventListener('dblclick', onDoubleClick);
  • ctx.save 는 현재 컨텍스트의 상태를 저장
  • ctx.restore 는 저장된 컨텍스트의 상태를 다시 불러옴

 

이제 글자 크기를 조절하려면 옆에 사용하던 range 바를 이용한다.

강의에선 하드코딩했는데 이런식도 가능하다.

function onDoubleClick(evt) {
  const txt = textInput.value;
  if(txt === '') return;

  ctx.save();
  ctx.font = `${ctx.lineWidth * 12}px ` + 'serif';
  ctx.lineWidth = 1;
  ctx.fillText(txt, evt.offsetX, evt.offsetY);
  ctx.restore();
  textInput.value = '';
}

canvas.addEventListener('dblclick', onDoubleClick);

기준은 12px 로 했음!

 

브러쉬의 끝을 둥글게 만들기

컨텍스트의 lineCap 속성을 바꿔주면 되는데, 기본값이 squre 이므로 round 로 바꿔준다.

ctx.lineCap = 'round';

 

 

이미지 저장하기

function onSaveClick() {
  const url = canvas.toDataURL();
  const a = document.createElement('a');
  a.href = url;
  a.download = 'myDrawing.png';
  a.click();

  onDestroyClick();
}

saveBtn.addEventListener('click', onSaveClick);

canvas 의 이미지를 url로 만드는 메서드가 toDataURL() 이며,

해당 url을 a링크의 href 속성에 넣어주고, a링크에는 download 라는 속성이 있는데, 넣어주는 값은 다운로드될 때 이름이 됨

해당 속성을 넣어주면 클릭되는 순간 해당 이미지가 다운로드 된다!

싱기하당

 

CSS 로 간단하게 꾸며주기

여기선 몰랐던 개념들만 포스트하려고 한다.

input의 file 타입을 꾸미는 방법 ---> for 속성에 id를 넣어주면 해당 input 을 대신하여 label을 사용해줄 수 있다!

      <label for="file">
        Add Photo
        <input type="file" accept="image/*" id="file" />
      </label>

그래서 기존의 input은 display none 처리 해버리고 label을 꾸며주자.

for 안에 값을 id명으로 잘 넣었다면 굳이 저렇게 감싸주지 않아도 정상적으로 작동한다.

기초 완성!

 

챌린지 코드 - 폰트의 크기와 종류 변경...

일단 폰트 크기는 range 로 변경할 수 있는 상태이다.

종류 변경을 위해서 

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font

 

CanvasRenderingContext2D: font property - Web APIs | MDN

The CanvasRenderingContext2D.font property of the Canvas 2D API specifies the current text style to use when drawing text. This string uses the same syntax as the CSS font specifier.

developer.mozilla.org

위 문서 내용을 참고했다.

 

input 의 accept 속성의 값들은 위와 같은 값이 들어간다고 한다.

폰트의 확장자의 경우, TTF와 OTF 가 대표적으로 알고 있어서 두가지만 허용시켰다.

      <label for="font-file">
        Change Font
        <input type="file" accept=".TTF,.OTF" id="font-file" />
      </label>

, 로 구분된다고 해줘서 그렇게 해줬고 테스팅을 위해서 아무 폰트나 다운 받아보았다.

이 폰트를 쓰는 곳이 많아서 예뻐서 다운.

// 원하는 폰트로 변경하기
let newFont = 'serif';
async function onChangeFont(evt) {
  const font = evt.target.files[0];
  const url = URL.createObjectURL(font);
  
  newFont = new FontFace('newFont', `url(${url})`);
  await newFont.load().then((loadedFont) => {
    console.log(loadedFont);
    document.fonts.add(loadedFont);
  })
}

fontFile.addEventListener('change', onChangeFont);

공식 문서대로 이렇게 했고

여기서 조금 헤맸는데 font 적용해줄 때 new FontFace에서 지어준 'newFont' 부분이 문자열이 되어야 제대로 설정된다.

  ctx.font = `${ctx.lineWidth * 12}px ` + (newFont === null ? newFont : 'newFont');

이거때문에 자꾸 FontFace Object가 찍혀서 안되는 바람에 공식 문서를 쥐잡듯이 찾았다.. 이리 쉬운걸 ㅠㅠ

위의 삼항조건문은 혹시 폰트를 넣지 않았을 때 예외처리를 해준것이다. 원래 지정했던 serif 가 들어가있음.

잘된다.

그외 Stroke 와 Fill 선택 버튼 및 그리는대로 fill 해주는 등 챌린지가 있었지만... 여기까지!

충분히 canvas 와 친해진 기분이다.