[코드 리뷰] 엔터프라이즈 블록체인 클론 코딩

Carrie·2024년 1월 9일
0
post-thumbnail

1. GSAP 애니메이션 반응형 구현

수정 전🤔

const largeScreen = window.matchMedia("(min-width: 1537px)");
function setupResponsiveAnimation() {
  // possible-slide 가로 이동
  let x1 = largeScreen.matches ? "-40%" : "-51%";
  gsap.to(".possible-slide", {
    scrollTrigger: {
      trigger: ".sc-possible",
      start: "0% 0%",
      end: "50% 0%",
      scrub: true,
      toggleActions: "play none none reverse",
      // markers: true,
    },
    x: x1,
  });

수정 후🤗

gsap.to(".sc-possible .possible-slide", {
  scrollTrigger: {
    trigger: ".sc-possible",
    start: "0% 0%",
    end: "50% 0%",
    scrub: true,
    invalidateOnRefresh: true, // 동적으로 변경되는 함수를 사용할 때 넣어줘야 한다.
    markers: true,
  },
  x: function () {
    return -$(".sc-possible .inner-slide").outerWidth() - 100; // inner-slide의 width만큼 이동, width가 변경될 때를 대비하여 함수를 사용한다.
  },
});

수정 전에는 특정한 고정값만큼 이동하도록 하였다. 미디어 쿼리를 사용하긴 했지만 다양한 디바이스에 적용하기 어려울뿐더러 브라우저의 크기가 변경될 때를 대비할 수가 없다. 그리하여 sc-possible 내의 inner-slide 너비만큼 x축으로 이동하도록 수정하였다. 이렇게 하면 브라우저 크기 변경 시에도 동일한 너비만큼 이동하여 사용자 경험을 향상시킬 수 있다.

💡invalidateOnRefresh: true
브라우저 창 크기 변경과 같은 이벤트가 발생할 때, 애니메이션 관련 값들을 새롭게 계산하는 옵션이다. 이는 동적인 레이아웃 변경에 대응하여 애니메이션을 정확하게 유지하는 데 유용하다.
특히, 함수를 사용하여 애니메이션의 속성 값을 계산할 때, 이 옵션을 활성화해야 한다. 이렇게 하면 창 크기 조정과 같은 상황에서도 함수가 다시 실행되어 적절한 값을 계산할 수 있다.

2. gsap.from vs gsap.set

수정 전🤔

  // 초기 상태 설정
  gsap.from(".sc-banner .box1", { x: -300 });
  gsap.from(".sc-banner .box2", { x: -300 });
  gsap.from(".sc-banner .box3", { x: 300 });
  gsap.from(".sc-banner .box", { filter: "none" });
  let bannerMotion = gsap.timeline({
    scrollTrigger: {
      trigger: ".sc-banner",
      start: "0% 90%",
      end: "100% 0%",
      scrub: true,
      // markers: true,
    },
  });
  bannerMotion
    .to(".sc-banner .box1", { x: 0 }, "motion")
    .to(".sc-banner .box2", { x: 0 }, "motion")
    .to(".sc-banner .box3", { x: 0 }, "motion")
    .to(".sc-banner .banner-content", { opacity: 1 }, "motion2")
    .to(".sc-banner .box", { filter: "blur(50px)" }, "motion2");

수정 후🤗

// sc-banner
gsap.set(".sc-banner .box1", { x: -300 });
gsap.set(".sc-banner .box2", { x: -300 });
gsap.set(".sc-banner .box3", { x: 300 });
const bannerMotion1 = gsap.timeline({
  scrollTrigger: {
    trigger: ".sc-banner",
    start: "0% 98%",
    end: "100% 85%",
    scrub: true,
    // markers: true,
  },
});
bannerMotion1
  .to(".sc-banner .box1", { x: 0 }, "a")
  .to(".sc-banner .box2", { x: 0 }, "a")
  .to(".sc-banner .box3", { x: 0 }, "a");

수정 전 해당 요소에 도달하기도 전에 애니메이션이 먼저 시작되는 문제가 있었다. 애니메이션 순서가 틀어져 아래 다른 섹션에도 영향을 미치는 문제가 있었는데, 이 문제는 from 대신 set을 써서 해결할 수 있었다. from은 이전 상태로부터 현재 값으로 이동하는 애니메이션이기 때문에 이 경우에는 적합하지 않다.

💡gsap.from vs gsap.set

  • from은 요소를 특정 상태에서 시작하여 현재 상태로 애니메이션하는 데 사용된다. 즉, from은 요소에 대해 지정된 시작 값으로부터 요소의 현재 값(또는 기본값)으로 이동하는 애니메이션을 생한다.
  • set은 요소에 즉시 특정 스타일 또는 상태를 적용한다. set은 애니메이션이 없이 즉시 값을 적용하며, 애니메이션의 시작점이나 중간 상태를 설정하는 데 주로 사용된다.

3. 가상 선택자를 이용한 배경 dimm 처리

수정 전🤔

  // sc-intro
  let introMotion = gsap.timeline({
    scrollTrigger: {
      trigger: ".intro-description",
      start: "0% 0%",
      end: "100% 100%",
      scrub: true,
    },
  });
  document
    .querySelectorAll(".intro-description .description-text")
    .forEach((elem, index, array) => {
      introMotion.to(elem, { opacity: 1 });
      if (index < array.length - 1) {
        introMotion.to(elem, { opacity: 0 });
      }
    });

  // sc-intro-bg
  gsap.to(".sc-intro-bg", {
    scrollTrigger: {
      trigger: ".sc-intro",
      start: "0% 0%",
      end: "100% 100%",
      scrub: true,
      toggleActions: "play none none reverse",
    },
    opacity: 1,
  });

수정 후🤗

let introMotion = gsap.timeline({
  scrollTrigger: {
    trigger: ".sc-intro",
    start: "0% 0%",
    end: "100% 100%",
    scrub: true,
  },
});
document
  .querySelectorAll(".intro-description p")
  .forEach((elem, index, array) => { // p 요소에 순차적으로 애니메이션을 적용하기 위해 반복문 사용
    introMotion.to(elem, { opacity: 1 }); // 요소를 노출시킨다
    if (index !== array.length - 1) { // 마지막 요소를 제외한 모든 요소
      if (index === 0) { // 첫번째 요소일때만
        introMotion.to(".sc-intro", { "--opacity": 1 }); // 배경을 dim처리 한다 그 이후에는 dim처리된 그대로 유지한다
      }
      introMotion.to(elem, { opacity: 0 }); // 다시 요소를 숨긴다
    }
  });

수정 전에는 <div class="sc-intro-bg"></div> 별도의 요소를 생성해서 dim처리를 해주었으나, 가상 선택자 ::before를 사용하는 방식으로 변경하여 추가적인 html 마크업 없이 좀 더 가독성 좋고 효율적인 코드로 수정하였다.

💡 가상 선택자를 사용했을 때의 장점은?

  • 추가적인 HTML 마크업 없이 콘텐츠 추가: ::before와 ::after 같은 가상 선택자를 사용하면, 추가적인 HTML 요소를 만들지 않고도 콘텐츠 앞이나 뒤에 텍스트나 이미지 등을 삽입할 수 있어 이는 HTML 문서를 깔끔하게 유지하는 데 도움이 된다.
  • 유지 보수 용이: 스타일링 관련 변경사항을 CSS에서만 처리할 수 있으므로, HTML 구조를 변경하지 않고도 시각적 요소를 쉽게 수정할 수 있다.
  • 콘텐츠와 스타일의 분리: 웹 접근성과 웹 표준을 준수하는 데 도움이 된다. 이는 콘텐츠(HTML)와 스타일링(CSS)을 분리하는 좋은 방법이다.

4. 반복문을 이용한 중복 코드 처리

수정 전🤔

// sc-prove
  let proveMotion = gsap.timeline({
    scrollTrigger: {
      trigger: ".sc-prove",
      start: "0% 70%",
      end: "100% 0%",
      scrub: true,
    },
  });
  proveMotion
    .to(".box-before-prove", { width: "68%" }, "motion")
    .to(".box-after-prove", { width: "68%" }, "motion")
    .to(".prove-content p:first-child", { x: -200 }, "motion")
    .to(".prove-content p:last-child", { x: 500 }, "motion");

  // sc-global
  let globalMotion = gsap.timeline({
    scrollTrigger: {
      trigger: ".sc-global",
      start: "0% 70%",
      end: "100% 0%",
      scrub: true,
      // markers: true,
    },
  });
  globalMotion
    .to(".box-before-global", { width: "68%" }, "motion")
    .to(".box-after-global", { width: "68%" }, "motion")
    .to(".global-content p:first-child", { x: -500 }, "motion")
    .to(".global-content p:last-child", { x: 700 }, "motion");

수정 후🤗

// sc-value, sc-global
$(".side-animation").each(function () { // 두 섹션에 동일한 클래스명을 추가하여 반복문 실행
  const sideAnimation = gsap.timeline({
    scrollTrigger: {
      trigger: $(this), // 해당 요소를 트리거로 지정
      start: "0% 70%",
      end: "100% 90%",
      scrub: true,
      // markers: true,
    },
  });
  let before, after; // 변수 선언
  if ($(this).attr("class").includes("value")) { // if문으로 분기 처리 - 해당 섹션의 클래스명에 "value"가 있을 경우
    before = -172;
    after = 134;
  } else { // 그렇지 않을 경우
    before = -114;
    after = 120;
  }
  sideAnimation
    .to($(this).find("[class*='box-before-']"), { xPercent: -100 }, "a") // 자기 자신의 너비만큼 100% 이동
    .to($(this).find("[class*='box-after-']"), { xPercent: 100 }, "a") // 자기 자신의 너비만큼 100% 이동
    .to($(this).find(".content p:first-child span"), { xPercent: before }, "a")
    .to($(this).find(".content p:last-child span"), { xPercent: after }, "a");
});

수정 전에는 비슷한 구조의 코드가 여러번 반복되고 있었다. start, end 지점 및 모션이 동일한 코드이기 때문에 반복문을 사용하여 하나의 타임라인으로 수정하였다. 이렇게 수정함으로써 코드의 간결성과 가독성을 높였다.

profile
Markup Developer🧑‍💻

0개의 댓글