history/slides/components/AnimatableSvg.vue (77 lines of code) (raw):
<!--
Copyright 2024 -l
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<!-- Removed 'h-auto' since 'fixed-height' should define the height -->
<div :id="containerId" class="svg-container w-full"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const props = defineProps({
svgFile: {
type: String,
required: true,
},
})
// Create a unique ID for each component instance
const containerId = ref(`svg-container-${Math.random().toString(36).substr(2, 9)}`)
// Function to trigger the animation
function animateSVG(svgFile: string, containerId: string) {
fetch(svgFile)
.then((response) => response.text())
.then((svgContent) => {
// Inject SVG content
document.getElementById(containerId).innerHTML = svgContent
const svgElement = document.querySelector(`#${containerId} svg`)
svgElement.style.width = "100%"
svgElement.style.height = "auto"
svgElement.style.overflow = "visible"
const allPaths = svgElement.querySelectorAll("path")
allPaths.forEach((path) => {
const length = path.getTotalLength()
path.style.strokeDasharray = length
path.style.strokeDashoffset = length
})
// GSAP animation for paths
allPaths.forEach((path) => {
gsap.to(path, {
strokeDashoffset: 0,
duration: 10,
ease: 'power1.inOut',
stagger: 0.2,
})
})
// Fade in all paths
gsap.fromTo(
allPaths,
{ opacity: 0 },
{ opacity: 1, duration: 10, ease: 'power1.out' }
)
})
.catch((error) => console.error('Error loading the SVG:', error))
}
// MutationObserver to detect when the slide becomes visible
function observeSlideVisibility(containerId: string) {
const figureElement = document.querySelector(`#${containerId}`).closest('figure')
if (!figureElement) {
console.error('Figure element not found!')
return
}
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
const currentClass = figureElement.className
if (currentClass.includes('slidev-vclick-current')) {
console.log('Slide is visible, triggering animation')
animateSVG(props.svgFile, containerId)
observer.disconnect() // Stop observing after the animation is triggered
}
}
})
})
observer.observe(figureElement, {
attributes: true, // Watch for attribute changes (like class)
attributeFilter: ['class'], // Only monitor the class attribute
})
}
// Trigger the observer on mount
onMounted(() => {
observeSlideVisibility(containerId.value)
})
</script>