viewportに入った時にイケてる演出できるコンポーネント

Vue.jstailwindcss

2024-03-20

概要

viewportに入った時だけアニメーションを適用してほしくて vueのtransitionのwrapperコンポーネントを作成した。

viewportに入った時とでた時に演出を持たせられるコンポーネント

出来上がったもの

ちょっとつまったところ

slotで受けとったものがisVisibleの値を参照しfalseのときはv-ifによってdomからいなくなるため高さが亡くなってしまうので isVisibleを監視してtrueになった時だけ親要素の高さをへんこうするようにした。

改善点

  • IntersectionObserverのoptionは親からpropsで受け取るようにするともっといろんなパターンで使えるかも

コンポーネントソース

<script setup>
import { ref, onMounted, watch } from 'vue'

const isVisible = ref(false)
const containerRef = ref(null)

onMounted(() => {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      isVisible.value = entry.isIntersecting
    })
  }, {
    threshold: 0.5
  })

  watch([containerRef], ([newContainerRef]) => {
    if (newContainerRef) observer.observe(newContainerRef)
  }, { immediate: true })
})
watch(isVisible, async (newVal) => {
  if (newVal) {
    await nextTick()
    let contentHeight = 0
    for (const child of containerRef.value.children) {
      contentHeight += child.offsetHeight
    }
    containerRef.value.style.height = `${contentHeight}px`
  }
})
</script>

<template>
  <div ref="containerRef">
    <span></span>
    <transition v-bind="$attrs">
      <slot v-if="isVisible"></slot>
    </transition>
  </div>
</template>

使い方

例1 四角のが横から回りながら出現

<TransitionInViewportObserver
class="absolute bottom-2 right-5 h-32 w-32"
enter-active-class="transition-all duration-1000 rotate-90"
leave-active-class="transition-all duration-1000"
enter-from-class="-translate-x-20 opacity-0 rotate-90"
leave-to-class="opacity-0">
<div class=" rotate-12 bg-sky-200/10"></div>
</TransitionInViewportObserver>

例2 左右からslideしながら出現

<TransitionInViewportObserver
  v-for="(feature, index) in featureBanners"
  :key="feature.title"
  class="h-52"
  enter-active-class="transition-all duration-1000"
  leave-active-class="transition-all duration-1000"
  :enter-from-class="index % 2 === 0 ? 'translate-x-20 opacity-0' : '-translate-x-20 opacity-0'"
  leave-to-class="opacity-0">
  <div class="bg-base-300 p-4 ">
    {{ feature.title }}
    {{ feature.description }}
  </div>
</TransitionInViewportObserver>

例3 下からslideしながら出現

<TransitionInViewportObserver
  enter-active-class="transition-all duration-1000"
  leave-active-class="transition-all duration-1000"
  enter-from-class="translate-y-20 opacity-0"
  leave-to-class="opacity-0">
  <div class="chat chat-start">
    <div class="avatar chat-image">
      <div class="w-10 rounded-full">
        <img alt="profile" :src="feedback.profile_photo">
      </div>
    </div>
    <div class="chat-bubble">
      {{ feedback.description }}
    </div>
  </div>
</TransitionInViewportObserver>
Sphere
Stacking

Links

  • GitHub
  • X
  • Hollow Shelfie

Legal

  • プライバシーポリシー

© 2024 Sphere. All rights reserved.