Js Challenge14 - #12 Img Filter

가짜 개발자·2023년 2월 18일
0

JS Challenge14

목록 보기
13/15
post-thumbnail

✅ Goal

  • input 속성을 활용하여 이미지를 출력 해본다.
  • 자바스크립트를 활용하여 이미지 필터 기능을 구현해본다.
  • 필터값이 적용 된 이미지 저장 기능을 만들어 본다.

✅ Keyword

input file 속성
readAsDataURL
css filter 속성
canvas.getContext("2d");
childNodes

이번 첼린지는 파일 이미지를 활용하여 필터 기능을 만들어 보고자 하였다. 그러기 위해선 input 속성 file 을 활용하면 된다. 공식 문서를 확인 해보면 input type에 file속성을 부여하면 나의 로컬스토리지 안에 있는 파일을 불러 올 수 있다. 또한 accept을 사용하여 이미지의 확장자 설정이 가능하다.
https://developer.mozilla.org/ko/docs/Web/HTML/Element/Input/file


그리고 또 하나 이미지를 브라우저에 출력 하기 위해선 readAsDataURL 메서드를 사용해야 한다. 이메서드는 컨텐츠를 특정 Blob 이나 File에서 읽어 오는 역할을 한다. 즉 해당 이미지의 url을 받을수 있다.
https://developer.mozilla.org/ko/docs/Web/API/FileReader/readAsDataURL


필터 기능은 css속성의 filter를 활용하였다. css에 다양한 filter 속성들이 있다.
https://developer.mozilla.org/ko/docs/Web/CSS/filter


해당 영역의 이미지를 캡쳐하기 위해 canvas를 사용하였고 cdn방식으로 설치하고 사용하였다. filter적용에 대한 내용은 아래 링크 참고하자
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter


childNodes를 활용하면 첫 번째 자식 노드에 index 가 할당된 지정된 요소의 자식의 Node를 반환한다. 필터이미지 생성 후 다시 생성 할 때 기존에 생성한 필터이미지를 지우기 위해 이 키워드를 활용하였다.

https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes

🟢 Create Filter Img

우선 css속성에서 filter속성의 값들을 배열에 담았고 배열에 길이 만큼 이미지가 생성 될 수 있도록 하였다. 여기서 중요한 점은 reader.readAsDataURL(file); 활용하여 파일 url을 받아 오는 것이다. 두번째는 onload하면서 myImg에 필터 값을 적용하여 반복문을 돌리는 것이다.

//index js
//...
const imgFilters = [
  "",
  "grayscale(100%)",
  "sepia(100%)",
  "saturate(8)",
  "hue-rotate(90deg)",
  "brightness(150%)",
  "contrast(200%)",
  "invert(100%)",
  "opacity(50%)",
  "blur(5px)",
];

input.addEventListener("change", (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = () => {
    for (let i = 0; i < imgFilters.length; i++) {
      const myimg = document.createElement("img");
      myimg.src = reader.result;
      myimg.classList.add("filterImg");
      myimg.style.filter = imgFilters[i];
      imgList.appendChild(myimg);
    }
  };
});
//...

그러면 배열의 길이만큼 필터가 적용된 이미지를 출력 할 수 있다.


🟢 Select Img

클릭시 선택된 이미지를 새롭게 생성해서 selectImg(div)에 넣었다. 여기서 중요한 점은 if문 childNodes이다. childNodes를 활용해서 부모요소 selectImg의 자손 요소를 조회 할 수 있으며 길이가 1보다 크면 먼저 선택한 자손요소가 삭제 될 수 있도록 하였다.

const select = document.createElement("img");
imgList.addEventListener("click", (e) => {
  const img = e.target;
  select.src = img.src;
  select.id = "select";
  select.style.filter = img.style.filter;
  selectImg.appendChild(select);
  container.appendChild(saveBtn);
  if (selectImg.childNodes.length > 1) {
    selectImg.removeChild(selectImg.childNodes[0]);
  }
});

이로서 필터 이미지를 선택할 때마다 선택 된 이미지만을 볼 수 있게 되었다.


🟢 Save Filter Img

필터 된 이미지를 저정하기 위해 저번 아트보드 첼린지때 사용했던 코드를 활용하였다. 하지만 필터값이 적용된 이미지가 아닌 원본 이미지가 계속 나왔다. 그래서 공식문서를 확인 하던 중 변수 ctx에 filter값을 줄 수가 있었고 그대로 적용하였더니 해결 되었다.

const saveBtn = document.createElement("button");
saveBtn.innerText = "저장";
saveBtn.classList.add("saveBtn");

saveBtn.addEventListener("click", () => {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  const img = new Image();
  img.src = select.src;
  img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.filter = select.style.filter;
    ctx.drawImage(img, 0, 0);
    saveAs(canvas.toDataURL("image/jpg"), "image.jpg");
  };
});

function saveAs(url, filename) {
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  link.click();
}

개인적으로 canvas 를 잘 활용한다면 다양한 사이드 프로젝트를 만들수 있을 것 같다.


🟥 조금은 고민했던 부분

이번 첼린지는 여러모로 고생을 했다. 필터값 적용 안되는 것 부터해서 어떻게 필터값을 적용해서 할지 다양한 고민을 한 것 같다. 그중에서 조금 고민을 많이 했던 부분은 필터 이미지를 생성한 후 다시 이미지를 생성하면 중복해서 값들이 쌓이게 되는 경우 였다.

input.addEventListener("click", (e) => {
  const filterImg = document.querySelectorAll(".filterImg");
  filterImg.forEach((img) => {
    img.remove();
    select.remove();
    saveBtn.remove();
  });
});

그래서 위와 같이 한번 더 클릭을 하면 이미지와 선택한 이미지를 초기화 할 수 있도록 코드를 작성 하였다. 이로서 필터이미지를 생성하고, 다시 이미지를 생성하면 기존에 있는 이미지들을 지워지고 새로 만든 필터이미지들이 생성 된다.



✅ 기능 시연 및 코드

🟢 Create Filter Img


🟢 Select Img


🟢 Save Filter Img


🟢 js code

// index.js
const container = document.getElementById("container");
const imgList = document.createElement("div");
const selectImg = document.createElement("div");

imgList.className = "imgList";
selectImg.id = "selectImg";

const label = document.createElement("label");
label.innerText = "이미지 필터 생성하기!";
label.htmlFor = "file-input";
label.classList.add("label");
container.appendChild(label);

const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.id = "file-input";
input.classList.add("input");
container.appendChild(input);

const imgFilters = [
  "",
  "grayscale(100%)",
  "sepia(100%)",
  "saturate(8)",
  "hue-rotate(90deg)",
  "brightness(150%)",
  "contrast(200%)",
  "invert(100%)",
  "opacity(50%)",
  "blur(5px)",
];

input.addEventListener("change", (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = () => {
    for (let i = 0; i < imgFilters.length; i++) {
      const myimg = document.createElement("img");
      myimg.src = reader.result;
      myimg.classList.add("filterImg");
      myimg.style.filter = imgFilters[i];
      imgList.appendChild(myimg);
    }
  };
});

container.appendChild(imgList);
container.appendChild(selectImg);
input.addEventListener("click", (e) => {
  const filterImg = document.querySelectorAll(".filterImg");
  filterImg.forEach((img) => {
    img.remove();
    select.remove();
    saveBtn.remove();
  });
});

const select = document.createElement("img");
imgList.addEventListener("click", (e) => {
  const img = e.target;
  select.src = img.src;
  select.id = "select";
  select.style.filter = img.style.filter;
  selectImg.appendChild(select);
  container.appendChild(saveBtn);
  if (selectImg.childNodes.length > 1) {
    selectImg.removeChild(selectImg.childNodes[0]);
  }
});

const saveBtn = document.createElement("button");
saveBtn.innerText = "저장";
saveBtn.classList.add("saveBtn");

saveBtn.addEventListener("click", () => {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  const img = new Image();
  img.src = select.src;
  img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.filter = select.style.filter;
    ctx.drawImage(img, 0, 0);
    saveAs(canvas.toDataURL("image/jpg"), "image.jpg");
  };
});

function saveAs(url, filename) {
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  link.click();
}

https://github.com/fake-dp/Js-Challenge14-Mini-Project/tree/main/ImgFilter
배포링크

profile
프론트 개발 일지

0개의 댓글