FrontEnd

vue js 내용정리 (2.7. 트랜지션 ~)

Dean83 2022. 3. 25. 09:25

2.7. 트랜지션
          - CSS 속성값이 서서히 변경되는것. (마치 면접의 신 fade 효과처럼)
          - <transition> 태그를 이용하여 효과 적용
          - v-if, v-show, 동적 컴포넌트, 컴포넌트 루트 노트 상황에서 모든 태그 및 컴포넌트 트랜지션 추가 가능
          - 참조 : https://kr.vuejs.org/v2/guide/transitions.html

          2.7.1. 트랜지션 클래스
                
    - CSS 에서 사용되는 트랜지션 클래스
                      - 해당 클래스별로 CSS를 각각 적용할 수 있음.
                      - <transition> 태그의 name 속성값-클래스명 조합으로 css 파일에 정의 해야 함
                         - 예 : 이름이 test인 경우, CSS 적용을 위해서는, .test-enter { 효과 }
                      - v 는 Transition 태그의 name 속성값으로 치환됨. 만일, name 속성값 없이 <transition> 태그만 사용한다면, v 로 작성해야함
                      
                      2.7.1.1. v-enter
                                  - enter의 시작 상태
                                  - <transition> 태그가 삽입되기 전에 적용되며, 한 프레임 후 제거됨
                      2.7.1.2. v-enter-active
                                  - enter의 활성 및 종료상태
                                  - 삽입되기 전에 적용
                                  - 트랜지션 / 애니메이션이 완료되면 제거됨
                      2.7.1.3. v-enter-to
                                  - 진입상태의 끝에서 실행됨
                                  - 삽입된 후, 트랜지션/애니메이션이 끝나면 제거됨  
                      2.7.1.4. v-leave
                                  - leave를 위한 시작상태
                                  - leave가 트리거 될때 적용되고 한 프레임 후 제거됨
                      2.7.1.5. v-leave-active
                                  - leave 활성 및 종료 상태
                                  - leave가 트리거 될때 적용되고, 트랜지션/애니메이션 완료시 제거됨
                      2.7.1.6. v-leave-to
                                  - leave 끝에서 실행
                                  - leave가 트리거 되고 트랜지션/애니메이션 완료시 동작하며 한 프레임 후 제거됨 
                               

                      2.7.1.7. v-move
                                  - 배열의 아이템들이 위치변경 할때 사용하는 클래스
                                  - 주로 v-for과 함께 쓴다.  2.7.9. 참조
                                  - 예

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>

<div id="flip-list-demo" class="demo">
  <button v-on:click="shuffle">Shuffle</button>
  <transition-group name="flip-list" tag="ul">
    <li v-for="item in items" v-bind:key="item">
      {{ item }}
    </li>
  </transition-group>
</div>

new Vue({
  el: '#flip-list-demo',
  data: {
    items: [1,2,3,4,5,6,7,8,9]
  },
  methods: {
    shuffle: function () {
      this.items = _.shuffle(this.items)
    }
  }
})

//css의 v-move 클래스
.flip-list-move {
  transition: transform 1s;
}

          - 전체 예

//css 파일내용
//fade 는 하단부 transition 태그의 name에 설정한 이름

// transition 태그로 싸여진 항목이 사라지거나 보여질때 적용
// transition: opacity.. 에서 transition은 fade를 의미함
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s; 
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}
===================
<html>
....
<header>
  <link href="css 파일위치" rel="stylesheet" type="text/css">
</header>
...

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>

new Vue({
  el: '#demo',
  data: {
    show: true
  }
})

          2.7.1. 트랜지션 효과
                      - css 의 각 트랜지션 클래스에서 transition: 설정값 으로 사용함
                      - fade 효과를 트랜지션 효과라고 함

          2.7.2. 애니메이션 효과
                      - css 의 각 트랜지션 클래스에서 animation: 설정값 으로 사용함

css 파일 구성

.bounce-enter-active {
  animation: bounce-in .5s;
}
.bounce-leave-active {
  animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}

          2.7.3. 사용자 지정 트랜지션 클래스
                      - 원본 클래스를 오버라이드
                      - css 에서 하지 않고 html 의 transition 태그의 속성으로 설정함
                      - 클래스명="css 에 정의된 동작 방식" 으로 사용
                      - 기존 css 라이브러리와 결합하기 위해 사용
                      - enter-class
                      - enter-active-class
                      - enter-to-class
                      - leave-class
                      - leave-active-class
                      - leave-to-class

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

<div id="example-3">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition
    name="custom-classes-transition"
    enter-active-class="animated tada" //animated tada 는 위의 link에 적용된 css 파일에서 효과를 사용하는 방식임. tada 효과를 사용하려면 class값에 animated 효과명
    leave-active-class="animated bounceOutRight"
  >
    <p v-if="show">hello</p>
  </transition>
</div>

          2.7.4. 트랜지션 지속시간 지정
                      - transition 태그의 속성으로 :duration="시간(mil)" 지정
                      - enter, leave 별로 지속시간 지정 가능
                         - :duration ="{enter:500, leave : 800}"

          2.7.5. 트랜지션 발생시, JavaScript 함수 호출
                      - transition 태그의 속성에서 설정 가능
                      - v-on:트랜지션클래스명(예 : enter)="javascript 함수명"
                      - 호출된 javascript 함수 내부에 작업 완료후 함수 리턴을 대신해 done() 코드 작성 필요.
                      - CSS 에서 효과를 적용할 수도 있으므로, CSS skip 속성값을 설정해야 함
                         - v-bind:css="false"
​​​​​​                      - 예

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

<div id="example-4">
  <button @click="show = !show">
    Toggle
  </button>
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false"
  >
    <p v-if="show">
      Demo
    </p>
  </transition>
</div>

new Vue({
  el: '#example-4',
  data: {
    show: false
  },
  methods: {
    beforeEnter: function (el) {
      el.style.opacity = 0
    },
    enter: function (el, done) {
      Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
      Velocity(el, { fontSize: '1em' }, { complete: done })
    },
    leave: function (el, done) {
      Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
      Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
      Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
      }, { complete: done })
    }
  }
})

          2.7.6. appear 속성
                      - 최초 렌더링 시 트랜지션을 적용할 때 사용
                      - transition 태그에 appear 속성을 사용

                      2.7.6.1. CSS 연동

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class" (2.1.8+)
  appear-active-class="custom-appear-active-class"
>
  <!-- ... -->
</transition>

// css 에서는
.custom-appear-class {
...
} 이런식으로 작성? 예제에 css는 안나와 있음

                      2.7.6.2. javascript 연동

<div id="test"
<transition
  appear
  v-on:before-appear="customBeforeAppearHook"
  v-on:appear="customAppearHook"
  v-on:after-appear="customAfterAppearHook"
  v-on:appear-cancelled="customAppearCancelledHook"
>
  <!-- ... -->
</transition>

=== javascript에서
new Vue({
   el:'#test',
   ....
   methods:
  {  
         customBeforeAppearHook : function(el,done)
         { ... }
  }
})

          2.7.7. 트랜지션 태그 안에 동일한 태그가 2개 이상 있을경우
                      - 예 : <transition> <button...> <button...> </transition> 일 경우.
                      - 각 중복되는 이름의 태그에 고유한 key 값을 배정해줘야 함. 

<transition>
  <button v-if="isEditing" key="save">
    Save
  </button>
  <button v-else key="edit">
    Edit
  </button>
</transition>

이는 아래로 대체 가능함
<transition>
  <button v-bind:key="isEditing">
    {{ isEditing ? 'Save' : 'Edit' }}
  </button>
</transition>

                      2.7.7.1. 동일 태그 3개 이상시

<transition>
  <button v-if="docState === 'saved'" key="saved">
    Edit
  </button>
  <button v-if="docState === 'edited'" key="edited">
    Save
  </button>
  <button v-if="docState === 'editing'" key="editing">
    Cancel
  </button>
</transition>

또는 

<transition>
  <button v-bind:key="docState">
    {{ buttonMessage }}
  </button>
</transition>

new Vue({
...
computed: {
  buttonMessage: function () {
    switch (this.docState) {
      case 'saved': return 'Edit'
      case 'edited': return 'Save'
      case 'editing': return 'Cancel'
    }
  }
}
})

                      2.7.7.2. mode 속성
       
                             - enter, leave가 동시에 트랜지션이 됨
                                    - 따라서, 같은 위치의 ui가 겹쳐 보일 수 있음
                                       - 한 ui는 사라지고, 다른 ui는 보여질 경우, 잠깐동안 2개가 모두 렌더링 됨

                                    2.7.7.2.1. in-out
       
                                              - 처음에는 enter가 트랜지션 되고, 완료 후 leave가 트랜지션
                                    2.7.7.2.2. out-in
       
                                              - 처음에는 leave가 드랜지션 되고, 완료 후 enter가 트랜지션

<transition name="fade" mode="out-in">
  <!-- ... the buttons ... -->
</transition>

          2.7.8. 컴포넌트 간 트랜지션
                      - 동적 컴포넌트 전환시 트랜지션
                      - key 속성을 설정할 필요 없음
                      - transition 태그 안에 component 태그에서, is를 사용한 동적 컴포넌트 할당을 이용
                      - 동적 컴포넌트는 2.3.10. 동적 컴포넌트 (내부 컴포넌트) 참조
                      - 예

//css 
.component-fade-enter-active, .component-fade-leave-active {
  transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to
/* .component-fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

//html
<transition name="component-fade" mode="out-in">
  <component v-bind:is="view"></component>
</transition>

//javascript
new Vue({
  el: '#transition-components-demo',
  data: {
    view: 'v-a'
  },
  components: {
    'v-a': {
      template: '<div>Component A</div>'
    },
    'v-b': {
      template: '<div>Component B</div>'
    }
  }
})

          2.7.9. v-for 를 이용한 transition
                      - <transition-group> 태그를 사용
                      -  실제 렌더링 될 태그는 <transition-group> 태그 안에 구현해야 함. (v-for 이용)
                      - <transition-group> 의 tag 속성으로, 렌더링될 자식 태그들의 부모태그를 지정할 수 있음
                      - 자식 태그에는 v-bind:key 속성을 반드시 사용해야함
                      - 예

<div id="list-demo">
  <button v-on:click="add">Add</button>
  <button v-on:click="remove">Remove</button>
  <transition-group name="list" tag="p"> // 자식 태그인 span의 부모 태그로 p를 지정
    <span v-for="item in items" v-bind:key="item" class="list-item"> //v-bind:key 지정 필요. span이 실제 렌더링 되는 자식임
      {{ item }}
    </span>
  </transition-group>
</div>

new Vue({
  el: '#list-demo',
  data: {
    items: [1,2,3,4,5,6,7,8,9],
    nextNum: 10
  },
  methods: {
    randomIndex: function () {
      return Math.floor(Math.random() * this.items.length)
    },
    add: function () {
      this.items.splice(this.randomIndex(), 0, this.nextNum++)
    },
    remove: function () {
      this.items.splice(this.randomIndex(), 1)
    },
  }
})

//css
.list-item {
  display: inline-block;
  margin-right: 10px;
}
.list-enter-active, .list-leave-active {
  transition: all 1s;
}
.list-enter, .list-leave-to /* .list-leave-active below version 2.1.8 */ {
  opacity: 0;
  transform: translateY(30px);
}

                      2.7.9.1. CSS 대신 Javascript 연동
       
                             - v-on:클래스명="javascript 함수명"
                                    - 2.7.5. 참조
                                    - 예 : 필터 효과 예제 

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

<div id="staggered-list-demo">
  <input v-model="query">
  <transition-group
    name="staggered-fade"
    tag="ul"
    v-bind:css="false"
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
  >
    <li
      v-for="(item, index) in computedList"
      v-bind:key="item.msg"
      v-bind:data-index="index"
    >{{ item.msg }}</li>
  </transition-group>
</div>

new Vue({
  el: '#staggered-list-demo',
  data: {
    query: '',
    list: [
      { msg: 'Bruce Lee' },
      { msg: 'Jackie Chan' },
      { msg: 'Chuck Norris' },
      { msg: 'Jet Li' },
      { msg: 'Kung Fury' }
    ]
  },
  computed: {
    computedList: function () {
      var vm = this
      return this.list.filter(function (item) {
        return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
      })
    }
  },
  methods: {
    beforeEnter: function (el) {
      el.style.opacity = 0
      el.style.height = 0
    },
    enter: function (el, done) {
      var delay = el.dataset.index * 150
      setTimeout(function () {
        Velocity(
          el,
          { opacity: 1, height: '1.6em' },
          { complete: done }
        )
      }, delay)
    },
    leave: function (el, done) {
      var delay = el.dataset.index * 150
      setTimeout(function () {
        Velocity(
          el,
          { opacity: 0, height: 0 },
          { complete: done }
        )
      }, delay)
    }
  }
})

          2.7.10. 트랜지션 재사용
                      - <transition> 태그 또는 <transition-group> 태그를 html에 작성하지 않고, 컴포넌트의 template에 정의
                      - 예

Vue.component('my-special-transition', {
  template: '
    <transition
      name="very-special-transition"
      mode="out-in"
      v-on:before-enter="beforeEnter"
      v-on:after-enter="afterEnter"
    >
      <slot></slot>  // slot은 2.3.9. 참조. html에서 작성한 태그 내용을 이곳에 보여줌 (동일레벨)
    </transition>',
  methods: {
    beforeEnter: function (el) {
      // ...
    },
    afterEnter: function (el) {
      // ...
    }
  }
})

          2.7.11. 동적 트랜지션
                      - <transition> 태그 또는 <transition-group> 태그의 name 속성에 v-bind를 이용
                      - 뷰 인스턴스의 맴버변수를 바인딩 함 (v-bind:name="vue인스턴스 변수명")
                      - css 에 각 변수이름-트랜지션 클래스 별 동작할 코드를 추가
                      - 상황에 맞게 맴버변수 이름을 변경하면, 동작하는 css 함수가 달라지므로, 효과가 동적으로 변경된다. 
                      - 예 (css 파일 사용치 않고 javascript에서 처리하는 방식
          

<div id="test">
<transition
    v-bind:css="false"
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:name="transtion-name"
  >
  .....
</div>

new Vue({
el:'#test'
data : {
   transtionName:''
}

methods :
{
    beforeEnter: function(el) {
           if(transtionName == 'a')
                   ...
           else
                   ...
    }
    ...
}
})

2.8. Mixin
       - 컴포넌트 및 뷰 인스턴스의 공통 기능 및 옵션 객체 구현, 각 컴포넌트에서 이를 상속받아 공통으로 사용하기 위한 것
       - 예

// 공통기능으로 사용할 객체 구현
var myMixin = {
  created: function () { //생성자
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// mixin을 사용할 컴포넌트 정의
// Vue.extend : 위의 공통 기능으로 사용할 객체 구현과 비슷한 개념
// new Component를 통해 미리 정의된 옵션등이 있는 객체를 생성한다.
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"

또는,

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('component hook called')
  }
})

// mixins 옵션을 통해 직접 배정 할 수도 있음.

          2.8.1. 옵션 병합 또는 대체
                      - 옵션 이름이 동일할 경우 발생
                      - hook 의 경우, 병합이 되어, 동일한 옵션이 다 호출됨.
                         - 순서는 mixin 으로 사용하는 객체 -> mixin객체를 mixins 옵션을 통해 설정한 객체

var mixin = {
  created: function () {
    console.log('mixin hook called')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('component hook called')
  }
})

// => "mixin hook called"
// => "component hook called"

                      - 그외 옵션은 mixin 객체를 사용하는 (mixins 옵션을 통해) 옵션으로 대체됨.                  

var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

          2.8.2. 전역 mixin
                      - 이후에 생성하는 모든 vue 인스턴스에 적용되므로 권장하지 않음
                      - Vue.mixin({ }) 을 통해 설정

Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello!'
})
// => "hello!"

2.9. 사용자 지정 디렉티브
       - Vue.directive('명칭', { }) 혹은 directives: 를 통해 정의 가능
       - 사용시, 기존 다른 디렉티브 (예 : v-on) 처럼 사용가능
          - <태그명 v-사용자지정명>
       - 예

// 전역 사용자 정의 디렉티브 v-focus 등록
Vue.directive('focus', {
  // 바인딩 된 엘리먼트가 DOM에 삽입되었을 때...
  inserted: function (el) {
    // 엘리먼트에 포커스를 줍니다
    el.focus()
  }
})

// 지역 사용자 정의
new Vue({ 혹은 Vue.compoment('명칭'{
....
directives: {
  focus: {
    // 디렉티브 정의
    inserted: function (el) {
      el.focus()
    }
  }
}
})

<태그명 v-focus> 으로 사용

          2.9.1. 훅 함수
                      - 사용자 정의 디렉티브에서 특정 상태일때 동작하기 위함

                      2.9.1.1. bind
                                   - 속성에 처음 바인딩 될때 한번만 호출됨. 일회성 
                      2.9.1.2. inserted
                                   - 바인딩된 html태그가 삽입되었을때
                      2.9.1.3. update
                                   - 포함하는 컴포넌트가 업데이트 된 후 호출됨. 불필요한 업데이트가 있을 수 있음
                      2.9.1.4. componentUpdated
                                   - 컴포넌트 및 그 자식들이 업데이트 된 후 호출
                      2.9.1.5. unbind
                                   - 언바인딩된 경우 한번 호출

          2.9.2. 훅 함수 매개변수
                      - 오브젝트 맴버변수
                      2.9.2.1. el
                                   - html 태그 오브젝트 
                                   - 이를 통해 html태그 접근, 사용 가능
                      2.9.2.2. binding
                                   - binding.변수명 으로 접근하여 사용
                                   2.9.2.2.1. name
                 
                                   - 디렉티브 이름으로 v- 프리픽스 없음
                                   2.9.2.2.2. value
                 
                                   - 디렉티브에서 전달받은 값
                                                    - 예 : v-value:변수명="1+1" 이면 2를 전달받음 
                                   2.9.2.2.3. oldvalue
                 
                                   - update, componentUpdate에서만 사용가능. 변경전 값
                                   2.9.2.2.4. expression
                 
                                   - 표현식 문자열
                                                   - 예 : v-value:변수명="1+1" 이면, "1+1"을 전달받음
                                   2.9.2.2.5. arg
                 
                                   - 디렉티브 전달인자. 있는경우에만 존재함
                                                    - 예 : v-value:변수명="1+1" 이면, "변수명" 을 전달받음
                                   2.9.2.2.6. modifiers
                 
                                   - 디렉티브 수식어 객체, 있는 경우에만 존재
                                                    - 예 : v-value:변수명.A.B 이면, {A:true,b:true} 를 전달받음 
                      2.9.2.3. vnode
                                   - Vue 컴파일러가 만든 가상노드
                      2.9.2.4. oldVnode
                                   - 이전 가상노드. update, componentUpdate 에서만 사용 가능
                      - 예

<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>

Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ')
  }
})

new Vue({
  el: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})

//
name : "demo"
value : "hello!"
expression : "message"
arguemnt: "foo"
modifiers : {"a":true,"b":true}

          2.9.3. 객체 리터럴
                      - 디렉티브에 여러값이 필요할경우 javascript 양식을 활용할 수 있음.
                      - 예

<div v-demo="{ color: 'white', text: 'hello!' }"></div>

Vue.directive('demo', function (el, binding) {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text)  // => "hello!"
})