Animating with Vue

Hello 👋

Ramona Bîscoveanu

👩🏼‍💻 Senior Developer @ SAP

 @CodesOfRa

 

 

🧀 ☕️🌱

⚠️ Motion⚠️

Remember the 90s?

Why animate?

It's all about communication

It's also that animations are  

FUN 👯‍♀️

<Transition/>
<transition name="fade">
    <p v-if="show">hello</p>
</transition>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
  • <transition> as a component's root will no longer trigger transitions when the component is toggled from the outside
  • v-enter-from/v-leave-from

Vue 3

<TransitionGroup />
  • <transition-group> no longer renders a root element by default, but can still create one with the tag attribute.

Vue 3

  <transition-group tag="div" class="tile-section" name="list" appear>
    <TileComp
      v-for="(tile, i) in tiles"
      :key="i + 'tile'"
      :header="tile.title"
      :content="tile.content"
      :footer="tile.footer"
    ></TileComp>
  </transition-group>
.list-enter-active,
.list-leave-active {
  transition: all 1s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateY(70px);
}

JavaScript Hooks

<Transition
  @before-enter="onBeforeEnter"
  @enter="onEnter"
  @after-enter="onAfterEnter"
  @enter-cancelled="onEnterCancelled"
  @before-leave="onBeforeLeave"
  @leave="onLeave"
  @after-leave="onAfterLeave"
  @leave-cancelled="onLeaveCancelled"
>
  <!-- ... -->
</Transition>

🎉

🎉

SVG

+

=

🎉

Tween

gsap.to(".selector", {toVars});

gsap.from(".selector", {fromVars});

gsap.fromTo(".selector", {fromVars}, {toVars});
// special properties (duration, ease, etc.) go in toVar
gsap.set(".selector", {toVars});
 <div class="tile-section">
    <TileComp
      v-for="(tile, i) in tiles"
      :key="i + 'tile'"
      :header="tile.title"
      :content="tile.content"
      :footer="tile.footer"
    ></TileComp>
  </div>
import gsap from "gsap";
...
mounted() {
    gsap.from(".tile", {
      duration: 0.5,
      opacity: 0,
      scale: 0,
      y: 200,
      ease: "power2",
      stagger: 0.1,
    });
  },

https://twitter.com/hexagoncircle/status/1527715562956066817

 <h1 ref="celebrate" class="counter" :class="{ celebrate: isCelebrate }">
      {{ this.numberWithCommas(value) }}
 </h1>
const tl = gsap.timeline();
  mounted() {
    tl.fromTo(
      ".counter",
      {
        innerText: start,
        scale: 0.8,
      },
      {
        innerText: end,
        snap: { innerText: 1 },
        duration: 4,
        ease: "linear",
        onUpdate: () => {
          this.$refs.celebrate.innerText = this.numberWithCommas(
            this.$refs.celebrate.innerText
          );
        },
        onComplete: () => {
          this.celebrate();
        },
      }
    ).to(".counter", {
      scale: 1,
      ease: "elastic.out(1, 0.2)",
      duration: 1.2,
    });
  },
restart() {
      tl.restart();
      this.isCelebrate = false;
}

Page transition

<router-view v-slot="{ Component }">
  <transition name="fade">
    <component :is="Component" />
  </transition>
</router-view>
 <router-view v-slot="{ Component }">
    <transition
      :key="$route.path"
      @enter="onEnter"
      @leave="onLeave"
      :css="false"
    >
      <component :is="Component" />
    </transition>
  </router-view>
import gsap from "gsap";
import SplitText from "gsap/SplitText";

gsap.registerPlugin(SplitText);

gsap.defaults({
  duration: 1,
  ease: "power3.inOut",
});
  mySplitText(el) {
      return new SplitText(el, { type: "words,chars,lines" });
    },
      
...
    onEnter(el, done) {
      const masterTL = gsap.timeline({
        onComplete: () => {
          done;
        },
      });

      masterTL.add(
        this.enterContentTextAnimation(
          mySplitText(".content-text-header").chars
        )
      );
      masterTL.add(
        this.enterContentTextAnimation(".content-text-body"),
        "-=0.9" //overlap with previous by 0.9s
      );
      masterTL.add(this.imgScaleOut(".content img"), "<");
  		//The start of previous animation
    },
    enterContentTextAnimation(id) {
      const tl = gsap.timeline();

      tl.fromTo(
        id,
        {
          yPercent: "100",
          opacity: 0,
        },
        {
          yPercent: "0",
          opacity: 1,
          stagger: 0.03,
        }
      );
    },
      ....
    imgScaleOut(id) {
      const tl = gsap.timeline();

      tl.from(id, {
        scale: 1.5,
      });

      return tl;
    },

SVG

  watch: {
    data(newValue, oldValue) {
      newValue.map((data, index) => {
        var id = "#arc" + index;
        var d = this.calculateArc(data, index);
        var oldValueD = this.calculateArc(oldValue[index]);
        const tl = gsap.timeline();

        tl.fromTo(
          id,
          {
            attr: { d: oldValueD },
          },
          {
            duration: 0.5,
            attr: { d: d },
            yoyo: true,
          }
        );
      });
    },
  },

Thank you!  

https://github.com/CodesOfRa/co2-radial-bar-chart

https://github.com/CodesOfRa/vue-animation-counter

Animating with Vue - Fundamental

By Ramona Biscoveanu

Animating with Vue - Fundamental

  • 290