Vue.js <transition>

강정우·2023년 4월 5일
0

vue.js

목록 보기
27/72
post-thumbnail
post-custom-banner

3항 연산자로 class 부여하기

  • 앞서도 포스팅하였지만 더 간단한 방법이 있다.
<div class="block" :class="{animate:animatedBlock}"></div>
  • 일반 class는 고정값이고 :을 동적으로 바인딩하여 : 3항 연산자로 참일때 클래스를 부여하도록 하였다.

문제점

  • react와 같은 문제를 갖고있다. dom에서 해제되면서 즉시 사라지기 때문이다. 이를 react에서는 react-transition을 이용했었다. 그럼 vue는 어떻게 해결할까?

<transition> (feat.CSS)

  • <transition>는 단 하나의 child 컴포넌트를 갖는다.

mount

  1. <transition>는 child 컴포넌트에 CSS utility class를 추가한다.

    • enter-from, enter-active 그리고 enter-to 클래스를 추가한다.
      처음에는 enter-fromenter-active를 동시에 추가하고
      이후 enter-from는 없애고 enter-active는 남겨 둔다.
      그리고 애니메이션이 끝나면 enter-to를 바로 추가한다.
  2. 이때 Vue가 이들 특수 CSS 클래스를 분석하여 그 안의 transition과 애니메이션을 찾고 애니메이션이 얼마나 지속되어야 하는지 읽는다.

  3. Vue가 CSS 코드를 분석하여 특수 CSS 클래스 이름으로부터 지속 시간을 찾고 해당 지속 시간만큼 특수 CSS 클래스를 추가해서 해당 요소가 나타나는 시간 동안 여러 스타일을 지정할 수 있도록 한다.

unmount

  • 항상 분해는 결합의 역순
  1. v-if를 통해 조건이 거짓이라면 마운트가 해제된다.

  2. 이때 Vue가 leave-from과 leave-active 클래스를 동시에 추가하고
    leave-from 클래스는 제거한 뒤 leave-to 클래스를 추가한다.

사용법

css 추가

  • 각기 다른 프로퍼티를 정의해 볼 텐데 최소한 하나의 transition 효과나 애니메이션이 있어야 Vue가 각 클래스로부터 지속 시간을 읽어들일 수 있다. 지속 시간을 읽어들이는 것 자체가 Vue가 내부적으로 실행하는 핵심 기능이니 말이다.
.v-leave-from{
  opacity: 1;
  transform: translateY(0);
}

.v-leave-active{
  transition: all 0.3s ease-in;
}

.v-leave-to{
  opacity: 0;
  transform: translateY(-30px);
}
  • v-enter-from : 시작 상태를 정의

  • v-enter-active : transition과 유사하게 정의

  • v-enter-to : 최종 상태(위치)를 정의

DOM상에 추가 및 제거될 때 사용되기 때문에 v-if, v-show등의 directive가 아니라면 굳이 transition 컴포넌트를 사용하지 않아도 된다.

<transition> 에 animation 속성사용하기


.v-leave-from{}

.v-leave-active{
  animation : slide-fade 0.5s ease-out
}

.v-leave-to{}
  • 참고로 여기서는 forward 키워드가 필요없는데 어차피 animation이 끝나면 모두 사라지기 때문이다.

  • 그래서 화면에 나타나는 마지막 상태는 CSS를 통해 정의한 일반 스타일이 적용되어 나온다. 그래서 아마 거의 안 쓸것 같다.

이름을 부여하여 따로 from, to, active 정의하기

<transition name="이름">
	<p v-if="paraIsVisible"> U can see when u trigger button </p>
<transition/>
  • 여기에 v 대신 사용할 접두어를 명하면 된다.
이름-enter-from{}
이름-enter-active{}
이름-enter-to{}

이름을 부여하여 아예 새로운 class이름 정의하기

  • 만약 당신이 다른 third-party-library를 사용하게되어 기존의 class를 사용하게 못 한다면 또 방법이있다.
<transition enter-to-class="" enter-active-class="" enter-from-class="">
	<p v-if="paraIsVisible"> U can see when u trigger button </p>
</transition>
  • 그래서 유연하게 지정할 수 있다.

<transition> 은 반드시 1개의 요소를 가져야한다고 했는 이는 루트 요소를 뜻한다. 무슨 뜻이냐면 커스텀 컴포넌트의 리프노드를 가봤을 때 native html 태그가 2개이상 있으면 안 된다는 것이다. 왜 이러냐면 "Fall Through" 개념 때문이다.

html 태그가 2개이상 성립할 때

  • transition 내의 자식 요소가 DOM Tree에서 빠지는 동시에 이를 즉시 대체하는 자식 요소가 있을 시에 성립된다.
<transition>
	<button @click="showUsers" v-if="!usersAreVisible">Show users</button>
	<button @click="hideUsers" v-if="usersAreVisible">Hide users</button>
</transition>

promise느낌의 mode

  • 위 코드를 작업하다보면 위와같은 모습이 보이는데 변환하는 과정에서 내가 원하지 않은 디자인이 나온것을 확인할 수 있다. 이를 타파하고자 나온 개념이 바로 mode이다.

  • mode에는 in-out, out-in 값을 지정할 수 있다.
    in-out : 추가되는 버튼 먼저 애니메이션이 적용되고 있던 버튼이 사라지는 식
    out-in : 사라지는 버튼 먼저 애니메이션 적용 후 추가버튼 적용

<transition> (feat.JS)

before-enter, enter, before-leave, after-enter

  • 애니메이션의 시작이나 끝에 특정 동작을 추가하고자 할 수도 있다
    바로 v-on 혹은 @ 을 이용하여 여러 이벤트를 수신할 수 있다.
<transition name="para"
                    @before-enter="beforeEnter"
                    @enter="enter"
                    @after-enter="beforeLeave"
                    @before-leave="beforeLeave"
                    @leave=""
                    @after-leave=""
        >
	<p v-if="paraIsVisible"> U can see when u trigger button </p>
</transition>
  • 또한 이를 메서드에서 el 로 매개변수를 받아서 가령 JavaScript로 애니메이션을 제어하여 스타일링을 바꿀 수도 있는 등
    본인이 하고싶은 작업을 추가로 할 수 있다.
  ...
methods: {
        beforeEnter(el){
            console.log("before Enter")
            console.log(el)
        },
        beforeLeave(el){
            console.log("before Leave")
            console.log(el)
        },
  ...

enter

  • v-enter-active CSS 클래스와 동일한데 enter 이벤트는 beforeEvent가 끝난 뒤 트리거되는 다음 단계라고 할 수 있다.

after-enter

  • 모든 CSS 모션이 완료된 후 작동한다.

이러한 것들은 CSS가 아닌 JS animation으로 제어할 때 유용하다.
바로 JS기반 third-party-library인 GreenSock이 대표적이다. 이를 이용하면 transition을 굉장히 쉽게 구축할 수 있다.

  • 이때 Vue는 이를 인식하지 못한다. 로직이 너무 많이 들어갔기 때문인데 Vue가 이를 파싱하거나 이런 코드를 작성한 이유를 이해하지 못한다. 따라서 enter가 끝났을 때 Vue에게 이를 알려줘야 한다.
methods:{
  ...
  enter(el, done) {
    console.log(el)
    let round = 1;
    const interval = setInterval(function () {
      el.style.opacity = round * 0.01;
      round++
      if(round>100){
        clearInterval(interval);
        done();
      }
    }, 20)
  },
  ...
  • 바로 done 함수이다.
    이 함수는 Vue로 하여금 이 훅의 동작이 끝났을 때를 알려주는 역할을 한다.
    CSS 기반으로 애니메이션을 설정할 때는 호출할 필요가 없다.
    Vue가 CSS 코드의 지속 시간을 읽어들일 수 있기 때문이다.

  • 하지만 CSS를 사용하지 않고 자체적인 로직이 있을 때는 다르다.
    지속 시간이 round와 if 조건 그리고 특정 interval 시간에 따라 바뀌고 끝나면 명백히 done 함수를 호출하여 Vue에게 이를 알려야 한다.

enter-cencelled, leave-cencelled

  • 바로 성질급한 한국인들을 위해 나왔다. 만약 당신이 연속해서 클릭해버리면 enter과 leave단에 JS 로직으로 작업해놓은 animate 함수들이 겹치면서 원하지 않은 결과를 받아볼 수 있다. 이를 방지하기위해 나온 이벤트들이다.
<transition name="para"
	:css="flase"
	@before-enter="beforeEnter"
	@enter="enter"
	@after-enter="afterEnter"
	@before-leave="beforeLeave"
	@leave="leave"
	@after-leave="afterLeave"
	@enter-cancelled="enterCancelled"
	@leave-cancelled="leaveCancelled"
>
  • 이들은 애니메이션이 삭제됐을 때마다 호출되어 메서드를 실행한다. 이 두 이벤트는 이벤트가 삭제되면 항상 발생하는데 지금처럼 JavaScript를 쓰든 이전처럼 CSS를 사용하든 동일하게 발생한다.

  • 추가적으로 :css 를 사용하여 Vue에게 해당 <transition>에는 JS 에니메이트외에 css는 사용하지 않겠다고 선언할 수 있다. 그렇게 되면 Vue가 굳이 css를 찾으러다니지 않아도 되서 조금 더 퍼포먼스가 증가할 수 있다.

  • 하지만 여기서 2가지 문제점이 생긴다.

  1. const interval 이 지역변수라는 점.
  2. this. 가 너무 내부에 있어서 pointing을 정확히 못 한다는 점.

바로 해결법을 알아보자.

1. const interval을 data prop 으로 설정, 2. function(X), =>(O)

data() {
  return {
    animatedBlock: false,
    dialogIsVisible: false,
    paraIsVisible: false,
    usersAreVisible: false,
    enterInterval : null,
    leaveInterval : null
  };
},
methods:{
  ...
  enter(el, done) {
    console.log(el)
    let round = 1;
    this.enterInterval = setInterval(() => {
      el.style.opacity = round * 0.01;
      round++
      if(round>100){
        clearInterval(this.enterInterval);
        done();
      }
    }, 20)
  },
}

css boiler code

<transition name="popUp">
      <div v-if="show" class="popbodyBg"
        :style="{ width: modalWidth, height: modalHeight, top: modalTop, left: modalLeft }">
        ...
      </div>
    </transition>
.route-enter-from {
    opacity: 0;
    transform: translateY(-30px);
}

.route-leave-to {
    opacity: 0;
    transform: translateY(30px);
}

.route-enter-active {
    transition: all 0.4s ease-out;
}

.route-leave-active {
    transition: all 0.4s ease-in;
}

.route-enter-to,
.route-leave-from {
    opacity: 1;
    transform: translateY(0);
}

# 위 template 코드처럼 name 이 있다면
.popUp-enter-from,
.popUp-leave-to {
  opacity: 0;
  transform: scale(0.8);
}

.popUp-enter-active {
  transition: all 0.3s ease-out;
}

.popUp-leave-active {
  transition: all 0.3s ease-in;
}

.popUp-enter-to,
.popUp-leave-from {
  opacity: 1;
  transform: scale(1);
}
profile
智(지)! 德(덕)! 體(체)!
post-custom-banner

0개의 댓글