[Vue] 스위치 모양 Lottie json 파일 삽입하고 기능 추가하는 방법 | vue-lottie

이은진·2021년 5월 2일
3

Vue

목록 보기
2/2
post-thumbnail

1. 작업 내용

B2B회원의 숙소 예약이 확정되면, 입실 시각 전까지는 업무예약과 일반예약을 변경할 수 있는 기능이 추가되었다. 추가된 화면에는 디자인 팀에서 제공한 Lottie(로띠) 파일을 삽입해야 했는데, 여기에서 로띠란 After Effect로 만들어낸 움짤 같은 json 파일을 실행시켜주는 라이브러리다. 대표적인 로띠 라이브러리에는 vue-lottielottie-vuejs 두 가지가 있는데 나는 둘 중 현재 이용자가 많은 vue-lottie를 선택해서 적용했다.

2. 단순 import 방법

로띠 움짤을 스위치기능 말고 다른 용도로 사용해본 적이 없어서 잘은 모르겠지만, 스위치로 쓸 로띠를 적용하는 과정으로는 크게 두 과정으로 나뉜다. 1. 일단 불러와서 움짤이 무한 작동하는지 확인하기, 2. API 문서를 참고하여 내장 메서드로 원하는 기능에 맞게 커스텀하기 이다. 그냥 로띠가 움짤로서의 역할만 하면 되는 상황이라면 전자까지만 해도 될 것 같다.

(1) 적절한 로띠 json파일을 마련한다.

https://lottiefiles.com/recent

디자인팀이나 인터넷에서 받아 온 로띠 json 파일은 이 곳에서 플레이해볼 수 있다.
https://lottiefiles.com/preview

이렇게만 보면 정신없는데 애초에 짧은 영상 움짤이라 이렇게 무한재생되는 게 맞다.

(2) npm install을 한다

https://www.npmjs.com/package/vue-lottie
npm install --save vue-lottie

package.json의 dependencies 확인하기.
"vue-lottie": "^0.2.1",

(3) 작업중인 컴포넌트에 vue-lottie와 json파일을 각각 import한다

// template
<lottie
  :options="options"
  :height="31"
  :width="51"
  @animCreated="handleAnimation"
/>


// script 
import Lottie from 'vue-lottie';
import * as animationData from '@/assets/json/switch_blue.json';

export default {
    name: 'SwitchLottie',
    components: {
      'lottie': Lottie
    },
    data() {
      return {
        options: {animationData: animationData},
        animationSpeed: 1
      }
    },
    methods: {
      handleAnimation: function (anim) {
        this.anim = anim;
    },
  ...

불러온 로띠 파일은 프레임을 0부터 20까지로 보자면, 0은 스위치가 꺼져 있는 상태, 20은 스위치가 완전히 활성화된 상태다. 따로 활성화된 상태에서 꺼진 상태를 나타내는 로띠 파일을 받지 않았기 때문에 사용자경험 상 스위치로서의 역할을 하려면 스위치를 켤 때는 프레임을 0에서 20으로, 끌 때는 20에서 0으로 자연스럽게 작동하도록 커스텀해야 했다.

3. 불러온 로띠에 다양한 옵션 추가하기

이제 불러온 로띠가 정신없이 무한재생하는 것을 확인했으니, 얌전히 스위치 기능만을 수행하도록 만들어줘야 한다. 나는 아래의 문서를 참고하여 작업했다.
https://github.com/airbnb/lottie-web

(1) 컴포넌트 mounted 됐을 때, 로띠 프레임이 원하는 상태에 맞게 멈춰 있도록 만들기

기업회원이 이전에 숙소를 업무 용도로 예약을 했다면 예약페이지에 처음 접근했을 때 업무 스위치가 켜진 상태로 멈춰있어야 한다. 나중에 다시 일반 업무로 바꾸고 싶으면 스위치를 끌 수 있록 만들어야 하기 때문이다. 반대로 기업회원이 잘 모르고 일반 용도로 예약을 했다가 업무 용도로 바꾸어야 할 때도 마찬가지다. 따라서 로띠 컴포넌트를 import하는 부모 컴포넌트에서 props로 businessTripStatus 상탯값을 넘겨주었다. businessTripStatus는 부모의 부모 컴포넌트에서 비동기로 받아온 예약데이터의 일부다. 간단히 표현해보자면 아래와 같다.

// 부모 컴포넌트

// template
...
  <switch-lottie
    :parentStatus="businessTripStatus"
  />
...
    
// script
    props: {
        businessTripStatus: Boolean,
        isB2bUser: Boolean,
        order: {
          biztrip: Boolean,
        },
    },
// 자식(로띠) 컴포넌트

// script
  data: () => {
    return {
      options: {
        animationData: animationData.default,
        autoplay: false,
        loop: false,
      },
    };
  },
  props: {
    parentStatus: {
      type: Boolean,
      required: true,
    },
  },
  mounted() {
    if (this.parentStatus) {
      this.anim.setSpeed(1);
      this.anim.play();
    }
  },

로띠 옵션에 autoplayloop 속성값을 일단 false로 두고, 업무예약 상태일 때만 play를 하도록 만들어야 한다. 부모 컴포넌트에서는 현재 예약 state를 props로 넘겨주었고, 자식인 로띠 컴포넌트에서는 그 props에 따라서 mount되는 시점에 로띠를 1회 play한다. this.anim.setSpeed() 메서드는 json 움짤 프레임이 실행되는 속도를 지정해주는 역할을 한다.

이건 작은 디테일이지만 페이지 마운트 됐을 때 애초부터 프레임 위치를 맨 뒤로 두는 것도 괜찮지만 사용자에게 스위치가 활성화됐음을 인지하게 하고 싶은 생각에 mounted 상태에서 this.anim.play()가 뿅 하고 실행되도록 만들었다.

(2) 클릭했을 때 로띠가 1회 실행되고 멈추도록 만들기

// 부모 컴포넌트

// temlate
<switch-lottie
  @click="businessOrderToggle"
  :parentStatus="businessTripStatus"
  :isChanged="isChanged"
/>

클릭기능을 만들기 위해 부모 컴포넌트에 businessOrderToggle 이라는 메서드를 만들어서 자식 컴포넌트로 넘겨준다. 이 메서드에서는 예약정보를 비동기로 변경하고, 응답 결과에 따라 성공과 실패를 처리해주는 역할을 한다. businessOrderToggle를 클릭했을 때 값이 '변경'이 되었다면 isChanged: true, 실패했으면 isChanged: false이다.

// 자식 로띠 컴포넌트

// script
props: {
  parentStatus: {
    type: Boolean,
    equired: true,
  },
  isChanged: {
    type: Boolean,
    required: true,
  },
},
methods: {
  handleAnimation(anim) {
    this.anim = anim;
  },
  toggle() {
    this.$emit('click');
  },
},
mounted() {
  if (this.parentStatus) {
    this.anim.setSpeed(1);
    this.anim.play();
  }
},
watch: {
  parentStatus: function (parentStatus) {
    if (this.isChanged) {
      parentStatus ? this.anim.setSpeed(1) : this.anim.setSpeed(-1);
      this.anim.play();
    }
  },
},

로띠를 클릭했을 때 반대 방향으로 실행하려면 this.anim.setSpeed()에 -1 값을 넣어야 한다. 음수를 넣으면 방향이 반대가 된다. 방향을 반대로 한 후 play를 하지 않고, 단순히 프레임을 맨 앞으로 두는 this.anim.goToAndStop() 등의 메서드를 쓰면 UX상 스위치를 실행하는 느낌보다는 뚝뚝 끊기는 느낌이 들어서 로띠를 쓰는 이유가 없어지기 때문에 프레임을 반대로 실행하는 this.anim.setSpeed(-1)를 사용하였다.

여기서 methods 내의 toggle 메서드에서 바로 애니메이션을 클릭하지 않고 특정 상탯값을 지켜보다가 변화가 있을 때 코드를 실행하는 watch를 사용했다. 그 이유는 클릭을 하여 부모의 businessOrderToggle 메서드를 실행시킨다고 해서 꼭 상태의 변화가 있다고 보장할 수 없기 때문이다. 비동기처리를 하다가 실패를 하면 클릭을 했음에도 제대로 된 응답을 받지 못한 상태로 남아있는데 스위치만 실행되는 상황이 발생할 수 있다. 따라서 부모 컴포넌트의 data에 따로 isChanged를 추가하여, 그 값을 지켜보는 방식을 사용했다.

4. 결과물

잘 작동이 된다.

profile
빵굽는 프론트엔드 개발자

0개의 댓글