Skip to content

Blur Fade

Blur fade in and out animation. Used to smoothly fade in and out content.

Hello World 👋
Nice to meet you ✨
View source on GitHub

Installation

This component uses the v-motion directive from @vueuse/motion; register MotionPlugin as described in Getting Started.

Copy the following files into src/components/spark-ui/blur-fade/:

blur-fade.vue
vue
<script setup lang="ts">
import { computed, reactive, ref, watch } from "vue";

interface BlurFadeProps {
  class?: string;
  variant?: {
    hidden: { y: number };
    visible: { y: number };
    enter: { y: number };
  };
  duration?: number;
  delay?: number;
  yOffset?: number;
  inView?: boolean;
  blur?: string;
  inViewMargin?: string;
}

const props = withDefaults(defineProps<BlurFadeProps>(), {
  duration: 0.4,
  delay: 500,
  yOffset: 6,
  inView: false,
  inViewMargin: "-50px",
  blur: "6px",
});

const elementRef = ref<HTMLElement | null>(null);
const isVisible = ref(false);
const combinedVariants = computed(() => {
  const shown = {
    y: -props.yOffset,
    opacity: 1,
    filter: "blur(0px)",
    transition: {
      delay: props.delay,
      duration: props.duration * 1000,
      ease: "easeIn",
    },
  };
  return (
    props.variant ?? {
      hidden: { y: props.yOffset, opacity: 0, filter: `blur(${props.blur})` },
      visible: shown,
      enter: shown,
    }
  );
});

// The directive captures its binding once, so keep this reactive object stable.
const motionVariants = reactive({
  initial: computed(() => combinedVariants.value.hidden),
  enter: computed(() => {
    if (!props.inView) return combinedVariants.value.enter;
    return isVisible.value ? combinedVariants.value.visible : combinedVariants.value.hidden;
  }),
});

watch(
  [elementRef, () => props.inView, () => props.inViewMargin],
  ([element, inView, rootMargin], _, onCleanup) => {
    isVisible.value = false;
    if (!element || !inView) return;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry) isVisible.value = entry.isIntersecting;
      },
      { rootMargin },
    );
    observer.observe(element);
    onCleanup(() => observer.disconnect());
  },
  { flush: "post" },
);
</script>

<template>
  <div ref="elementRef" v-motion="motionVariants" :class="props.class">
    <slot />
  </div>
</template>

Props

PropTypeDescriptionDefault
classstringThe class name to be applied to the component
variantobjectCustom animation variants for motion component
durationnumberDuration (seconds) for the animation0.4
delaynumberDelay in milliseconds before the animation starts500
yOffsetnumberVertical offset for the animation6
inViewbooleanWhether to trigger animation when component is in viewfalse
inViewMarginstringIntersectionObserver root margin; updates reactively"-50px"
blurstringAmount of blur to apply during the animation"6px"

duration is in seconds; delay is in milliseconds to match the existing demos. With inView, the animation returns to its hidden variant when it leaves the observer region. Custom variant objects replace the defaults and control their own transitions. Motion props and replacement variants update without remounting.

Released under the MIT License.