[Nuxt.js] 스크롤 이벤트 적용 (ios vh 이슈)

babypig·2023년 1월 25일
2

Nuxt.js

목록 보기
10/10
post-thumbnail

Nuxt 스크롤 이벤트 적용

data

이전 스크롤 높이, 현재 섹션, 스크롤된 값, 이전 스크롤 높이 + header 높이, headerHeight, 맞닿는 지점 Boolean 값 설정, scroll되는 Container 설정.

  data() {
    return {
      prevScrollHeight: 0,
      currentSection: 0,
      currentScrollPosition: 0,
      currentYOffset: 0,
      headerHeight: 0,
      enterSec: false,
      scrollInfo: [
        {
          container: null,
        },
        {
          container: null
        },
        {
          container: null
        },
        {
          container: null
        },
        {
          container: null
        }
      ]
    };
  },

mounted

mounted는 DOM의 모든 변경사항을 반영하고나서 실행되는 훅이다. Javascript는 비동기로 처기되기때문에 DOM의 모든 변경사항이 반영 되기 전에 DOM에 접근하려고하면 undefined나 null 에러가 발생한다.
따라서 Vue.js에서는 데이터 갱신, UI 업데이트 등DOM의 모든 변경사항이 완전히 반영된 후에 로직을 실행할 수 있도록 nextTick()을 사용하여 이벤트를 등록한다.

  mounted() {
    this.$nextTick(() => {
      window.addEventListener('resize', this.viewportSetting);
      window.addEventListener('scroll', this.dataProgressSetting);
      window.addEventListener('scroll', this.dataSectionProgressSetting);
    });
    this.viewportSetting();
    this.dataProgressSetting();
    this.dataSectionProgressSetting();
  },

beforeDestroy

Destroyed가 호출되기전에 이벤트 리스너를 제거한다.

  beforeDestroy() {
    window.removeEventListener('resize', this.viewportSetting);
    window.removeEventListener('scroll', this.dataProgressSetting);
    window.removeEventListener('scroll', this.dataSectionProgressSetting);
  },

methods

viewportSetting()

ios는 주소창 높이를 포함한 상태를 꽉찬 상태로 보는 vh 이슈가 있다.
해당 이슈로 vh로 작업한 section들이 height가 조정되어 튕기는 현상이 발생하여
vh를 스크립트로 set 해주고 이전 width와 값지 않을때에만 vh를 set 해주게 적용한다.

dataProgressSetting()

전체 viewport에서의 스크롤 비율을 계산하는 함수 적용

dataSectionProgressSetting()

section 별 해당 section의 스크롤 비율을 계산

ratioStyleCalc(currentYOffset, minValue, maxValue, startValue, endValue)

스크롤 비율에 따른 적용할 css를 계산하는 함수
minValue = 시작 값
maxValue = 끝 값
startValue = 시작 스크롤 비율
endValue = 끝 스크롤 비율

playAnimation()

switch문을 이용하여 currentSection 별 style 적용

resetAnimate()

값을 초기화 하기 위한 함수

    viewportSetting() {
      const vh = window.innerHeight * 0.01;
      const vw = window.innerWidth * 0.01;
      const fh = document.querySelector('footer').offsetHeight;
      document.documentElement.style.setProperty('--footer-height', `${fh}px`);
      if (this.prevWidth !== window.innerWidth) {
        document.documentElement.style.setProperty('--vh', `${vh}px`);
        document.documentElement.style.setProperty('--vw', `${vw}px`);
      }
      this.prevWidth = window.innerWidth;
      this.scrollInfo.forEach((section, index) => {
        this.scrollInfo[index].container = this.$refs[`scrollSection${index}`];
      });
    },
    dataProgressSetting() {
      const viewportRatio = window.scrollY / (document.body.clientHeight - window.innerHeight);
      document.documentElement.setAttribute('data-progress', viewportRatio);
      document.body.setAttribute('id', `show-current-${this.currentSection}`);
    },
	dataSectionProgressSetting() {
      this.prevScrollHeight = 0;
      this.headerHeight = document.querySelector('header').offsetHeight;
      this.currentScrollPosition = window.scrollY;
      if (this.enterSec) this.enterSec = false;

      for (let i = 0; i < this.currentSection; i++) {
        this.prevScrollHeight += this.scrollInfo[i].container.scrollHeight;
      }

      const scrollInfoScrollHeight = this.scrollInfo[this.currentSection].container.scrollHeight;
      const currentYOffset = this.currentScrollPosition - this.prevScrollHeight;
      const scrollRatio = currentYOffset / (scrollInfoScrollHeight + this.headerHeight);
      this.scrollInfo[this.currentSection].container.setAttribute('data-section-progress', scrollRatio);

      if (
        this.currentScrollPosition >
        this.scrollInfo[this.currentSection].container.scrollHeight + this.prevScrollHeight + this.headerHeight
      ) {
        this.scrollInfo[this.currentSection].container.setAttribute('data-section-progress', 1);
        this.currentSection++;
        this.enterSec = true;
        document.body.setAttribute('id', `show-current-${this.currentSection}`);
      }

      if (this.currentScrollPosition < this.prevScrollHeight + this.headerHeight) {
        if (this.currentSection === 0) return;
        this.scrollInfo[this.currentSection].container.setAttribute('data-section-progress', 0);
        this.currentSection--;
        this.enterSec = true;
        document.body.setAttribute('id', `show-current-${this.currentSection}`);
      }
      if (this.currentScrollPosition < window.innerHeight) this.resetAnimate();
      else this.playAnimation();
    },
 	ratioStyleCalc(currentYOffset, minValue, maxValue, startValue, endValue) {
      let result;

      const scrollHeight = this.scrollInfo[this.currentSection].container.scrollHeight;
      const scrollStart = startValue * scrollHeight;
      const scrollEnd = endValue * scrollHeight;
      const scrollProgress = scrollEnd - scrollStart;
      currentYOffset = this.currentScrollPosition - this.prevScrollHeight - this.headerHeight;
      this.currentYOffset = currentYOffset;

      if (currentYOffset >= scrollStart && currentYOffset <= scrollEnd) {
        result = ((currentYOffset - scrollStart) / scrollProgress) * (maxValue - minValue) + minValue;
      }
      if (currentYOffset < scrollStart) {
        result = minValue;
      }
      if (currentYOffset > scrollEnd) {
        result = maxValue;
      }

      return result;
    },
    playAnimation() {
      if (this.enterSec) return;
      const dataSet = this.scrollInfo[this.currentSection].container.dataset.sectionProgress;
      switch (this.currentSection) {
        case 0:
          break;
        case 1:
		  this.$refs.titleMessage1.style.opacity = this.ratioStyleCalc(this.currentYOffset, 0, 1, 0.35, 0.4);
          this.$refs.titleMessage1.style.fontSize = `${this.ratioStyleCalc(this.currentYOffset,8,8,0.35,0.4)}rem`;
          break;
      }
	},
    resetAnimate() {
		  this.$refs.doorBg.style.opacity = 0;
    }
profile
babypig

0개의 댓글