0.12.2 • Published 2 months ago

@css-spring-animation/vue v0.12.2

Weekly downloads
-
License
MIT
Repository
github
Last release
2 months ago

CSS Spring Animation

English | 日本語

An intuitive and predictable spring animation library powered by CSS Transition. It is inspired by Animate with springs of WWDC 2023. The features of this library are:

  • Implementing spring animation with CSS Transition
  • Animation options are intuitive and predictable
    • bounce: Bounciness of an animation
    • duration: Perceptive animation duration
  • Graceful degradation with requestAnimationFrame for browsers that do not support the features used in the library

Getting Started

There is a Vue binding of the library. Install it with npm (or yarn, pnpm):

$ npm install @css-spring-animation/vue

When you use <script setup> in a single file component, you can use spring higher-order component as below:

<script setup>
import { ref } from 'vue'
import { spring } from '@css-spring-animation/vue'

const moved = ref(false)
</script>

<template>
  <button type="button" class="button" @click="moved = !moved">Toggle</button>

  <!-- render <div> element animating with style specified on :spring-style -->
  <spring.div
    class="rectangle"
    :spring-style="{
      translate: moved ? '100px' : '0px',
    }"
    :duration="600"
    :bounce="0.3"
  ></spring.div>
</template>

The property name after spring. is the tag name to be rendered. For example, <spring.div> renders <div> element. The element has the style specified on :spring-style prop. Spring animation will be triggered when the value of :spring-style prop is changed.

Bounce and Duration

bounce and duration options are used to specify the bounciness and perceptive duration of an animation.

bounce Bounciness of an animation. The value is between -1 and 1. The default value is 0.

duration Perceptive duration (ms) of an animation. The default value is 1000.

Spring Style Caveats

All numbers in a style value must have the same unit and must be appeared in the same order. For example, the following :spring-style value is invalid and will not work as expected:

<template>
  <!-- ❌ this example will not work as expected -->
  <spring.div
    :spring-style="{ transform: flag ? 'translate(100px, 100px)' : 'scale(2)' }"
  ></spring.div>
</template>

This is because the library parses the numbers in the style value, then calculate the animation for each number in the style value. The library cannot understand the meaning of translate, scale nor predict the difference between 100% and 100px. To fix the above example, you need to specify both translate and scale in the same order and always use the same unit:

<template>
  <!-- ✅ all numbers in the :spring-style have the same unit and are in the same order -->
  <spring.div
    :spring-style="{
      transform: flag
        ? 'scale(1) translate(100px, 100px)'
        : 'scale(2) translate(0, 0)',
    }"
  ></spring.div>
</template>

How It Works

The library sets spring animation expression in an animating CSS value including a custom property that representing elapsed time (let's say --t here). Then register --t by using CSS.registerProperty to be able to apply CSS Transition on it. The pseudo code of the spring animation expression looks like below:

// Register --t
CSS.registerProperty({
  name: '--t',
  syntax: '<number>',
  inherits: false,
  initialValue: 0,
})

// Set initial state
el.style.setProperty('--t', 0)

// Set spring animatioin expression including --t
el.style.translate = 'calc(P * (A * var(--t) + B) * exp(-C * var(--t)) - Q)'

// Re-render
requestAnimationFrame(() => {
  // Trigger animation
  el.style.setProperty('--t', 1)
  el.style.transition = '--t 1000ms linear'
})

The library also provides a graceful degradation for browsers that do not support CSS.registerProperty and exp() function of CSS. In this case, the library will use requestAnimationFrame to animate the style value instead of CSS Transition.

API Reference

<spring> component

It renders a native HTML element as same tag name as the property name (e.g. <spring.div> renders <div> element).

Props

  • spring-style: Style object to be animated
  • bounce
  • duration
<script setup>
import { spring } from '@css-spring-animation/vue'

const position = ref(0)
</script>

<template>
  <spring.div
    :spring-style="{
      translate: `${position.value}px`,
    }"
    :duration="600"
    :bounce="0.3"
  ></spring.div>
</template>

<SpringTransition> component

<SpringTransition> is a spring animation version of Vue's <Transition> component. It triggers animation from enter-from style to spring-style on entering and from spring-style to leave-to on leaving.

Props

  • spring-style: Default style of a child element.
  • enter-from: Style of a child element before entering.
  • leave-to: Style of a child element after leaving. Fallback to enter-from style if not specified.
  • mode
  • bounce
  • duration

Events

  • before-enter
  • after-enter
  • enter-cancelled
  • before-leave
  • after-leave
  • leave-cancelled
<script setup>
import { ref } from 'vue'
import { SpringTransition } from '@css-spring-animation/vue'

const isShow = ref(false)
</script>

<template>
  <button type="button" class="button" @click="isShow = !isShow">Toggle</button>

  <!-- Trigger spring animation for the child element -->
  <SpringTransition
    :spring-style="{
      translate: '0',
    }"
    :enter-from="{
      translate: '-100px',
    }"
    :leave-to="{
      translate: '100px',
    }"
    :duration="600"
    :bounce="0"
  >
    <!-- .rectangle element will be animated when v-show value is changed -->
    <div v-show="isShow" class="rectangle"></div>
  </SpringTransition>
</template>

<SpringTransitionGroup> component

<SpringTransitionGroup> is a spring animation version of Vue's <TransitionGroup> component. It can have spring-style, enter-from and leave-to props as same as <SpringTransition>.

Props

  • spring-style: Default style of a child element.
  • enter-from: Style of a child element before entering.
  • leave-to: Style of a child element after leaving. Fallback to enter-from style if not specified.
  • tag: Tag name of the wrapper element. It is Fragment (do not render wrapper element) by default.
  • bounce
  • duration

Events

  • before-enter
  • after-enter
  • enter-cancelled
  • before-leave
  • after-leave
  • leave-cancelled
<script setup>
import { SpringTransitionGroup } from '@css-spring-animation/vue'

const list = ref([
  // ...
])
</script>

<template>
  <!-- Trigger spring animation for the child elements -->
  <SpringTransitionGroup
    tag="ul"
    :spring-style="{
      opacity: 1,
    }"
    :enter-from="{
      opacity: 0,
    }"
    :leave-to="{
      opacity: 0,
    }"
    :duration="800"
    :bounce="0"
  >
    <!-- List items must have key prop -->
    <li v-for="item of list" :key="item.id">
      <!-- ... -->
    </li>
  </SpringTransitionGroup>
</template>

useSpring composable

A composable function to generate spring animation style. It also returns the real value and velocity of the corresponding number in the style value. They are as same shape as the style value except that its values are the array of numbers.

The first argument is a function or ref that returns the style object to be animated. The second argument is an options object. It also can be a function or ref that returns the options.

It is expected to be used in a complex situation that <spring> component is not suitable to be used.

<script setup>
import { ref } from 'vue'
import { useSpring } from '@css-spring-animation/vue'

const position = ref(0)

const { style, realValue, realVelocity } = useSpring(
  () => {
    return {
      translate: `${position.value}px`,
    }
  },
  () => {
    return {
      duration: 600,
      bounce: 0.3,
    }
  },
)
</script>

<template>
  <div :style="style"></div>
  <ul>
    <li>realValue: {{ realValue.translate[0] }}</li>
    <li>realVelocity: {{ realVelocity.translate[0] }}</li>
  </ul>
</template>

useSpring provides onFinishCurrent function that is for waiting until the current animation is finished. You can register a callback function that will be called when an ongoing animation is finished.

<script setup>
import { ref } from 'vue'
import { useSpring } from '@css-spring-animation/vue'

const position = ref(0)

const { style, onFinishCurrent } = useSpring(() => {
  return {
    translate: `${position.value}px`,
  }
})

function move() {
  // Move to 100px
  position.value = 100

  // Wait for the animation is finished triggered by the above position update
  onFinishCurrent(() => {
    // Move to 0px
    position.value = 0
  })
}
</script>

v-spring-style and v-spring-options directivies

v-spring-style directive is used to specify the style to be animated. v-spring-options directive is used to specify the options of the animation.

It is expected to be used out of <script setup> where <spring> component is not able to be used.

You can register the directives by using plugin object exported as springDirectives:

import { createApp } from 'vue'
import App from './App.vue'
import { springDirectives } from '@css-spring-animation/vue'

createApp(App).use(springDirectives).mount('#app')

Then you can use the directives in a template:

<template>
  <div
    v-spring-style="{
      translate: `${position}px`,
    }"
    v-spring-options="{
      duration: 600,
      bounce: 0.3,
    }"
  ></div>
</template>
0.12.2

2 months ago

0.12.1

2 months ago

0.12.0

4 months ago

0.11.0

4 months ago

0.10.0

5 months ago

0.9.0

6 months ago

0.8.1

6 months ago

0.8.0

6 months ago

0.7.1

7 months ago

0.7.0

7 months ago

0.6.0

7 months ago

0.5.2

7 months ago

0.5.1

7 months ago

0.5.0

7 months ago

0.4.3

7 months ago

0.4.2

7 months ago

0.4.1

7 months ago

0.4.0

7 months ago

0.3.1

7 months ago

0.3.0

7 months ago

0.2.0

7 months ago

0.1.1

7 months ago

0.1.0

7 months ago