Motion+

Characters remaining

An example of creating a character counter with spring animations in Motion for Vue

Vue

Source code

<script setup>
import { ref, watch,computed } from 'vue'
import { motion, transform , useAnimate } from 'motion-v'

const value = ref('')
const maxLength = 12
const charactersRemaining = computed(() => maxLength - value.value.length)
const [counterRef, animate] = useAnimate()

const mapRemainingToColor = transform([2, 6], ['#ff008c', '#ccc'])


watch(charactersRemaining, (newVal) => {
  if (newVal > 6) return

  const mapRemainingToSpringVelocity = transform([0, 5], [50, 0])
  
  animate(
    counterRef.value,
    { scale: 1 },
    {
      type: 'spring',
      velocity: mapRemainingToSpringVelocity(newVal),
      stiffness: 700,
      damping: 80,
    }
  )
})

</script>

<template>
  <div class="container">
    <input v-model="value" />
    <div>
      <motion.span
        ref="counterRef"
        :style="{
          color: mapRemainingToColor(charactersRemaining),
          willChange: 'transform',
        }"
      >
        {{ charactersRemaining }}
      </motion.span>
    </div>
  </div>
</template>

<style>
.container,
input {
  position: relative;
  font-size: 32px;
  line-height: 1;
}

input {
  background-color: var(--layer);
  color: var(--white);
  border: 2px solid var(--border);
  border-radius: 10px;
  padding: 20px;
  padding-right: 70px;
  width: 300px;
}

input:focus {
  border-color: var(--hue-blue);
}

.container div {
  color: #ccc;
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0) 0%,
    var(--layer) 20%
  );
  position: absolute;
  top: 50%;
  right: 2px;
  transform: translateY(-50%);
  padding: 10px;
  padding-right: 20px;
  padding-left: 50px;
}

.container div span {
  display: block;
}
</style>

Related examples

Latest in Vue

Motion+

Unlock all 400+ examples

  • Source code for every Plus example.
  • Provide examples direct to your agent via Motion's MCP.
  • Lifetime access to new examples and APIs.