Lottie animations
Last updated
Was this helpful?
Was this helpful?
import type { AnimationItem } from 'lottie-web';
import { ref, onMounted, onUnmounted, watch, type ComputedRef } from 'vue';
const IDLE_FALLBACK_DELAY_MS = 200;
const IDLE_TIMEOUT_MS = 2000;
const scheduleIdle = (fn: () => void): void => {
if (typeof window === 'undefined') return;
if ('requestIdleCallback' in window) {
window.requestIdleCallback(fn, { timeout: IDLE_TIMEOUT_MS });
} else {
setTimeout(fn, IDLE_FALLBACK_DELAY_MS);
}
};
export const useLottieAnimationLocal = (
loadAnimationData: () => Promise<object>,
showAnimation: ComputedRef<boolean>,
) => {
const lottieContainer = ref<HTMLElement>();
const lottieAnimationInitialized = ref(false);
let lottieInstance: AnimationItem | null = null;
const initializeLottieAnimation = async () => {
if (lottieContainer.value && !lottieAnimationInitialized.value) {
try {
const [{ default: lottie }, animationData] = await Promise.all([
import('lottie-web'),
loadAnimationData(),
]);
if (!lottieContainer.value) return;
lottieInstance = lottie.loadAnimation({
container: lottieContainer.value,
renderer: 'svg',
loop: true,
autoplay: true,
animationData,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
},
});
lottieAnimationInitialized.value = true;
} catch (error) {
if (process.env.NODE_ENV === 'development') {
console.warn('Failed to initialize Lottie animation:', error);
}
}
}
};
watch(showAnimation, (newValue) => {
if (newValue && !lottieAnimationInitialized.value) {
scheduleIdle(initializeLottieAnimation);
}
});
onMounted(() => {
if (showAnimation.value) {
scheduleIdle(initializeLottieAnimation);
}
});
onUnmounted(() => {
lottieInstance?.destroy();
});
return { lottieContainer };
};export function sectionAssetUrl(filename: string, moduleUrl: string): string {
let base = moduleUrl;
const clientIdx = base.lastIndexOf('/client/');
if (clientIdx !== -1) {
base = base.slice(0, clientIdx);
}
base = base.replace(/\/js\/main$/, '');
return `${base}/assets/${encodeURIComponent(filename)}`;
}import { sectionAssetUrl } from '../../../shared/utils/section-asset-url';
async function loadAnimationData() {
const url = sectionAssetUrl('my-animation.json', import.meta.url);
const response = await fetch(url);
return response.json();
}<template>
<div v-show="show" class="my-section__animated-bg">
<div ref="lottieContainer" class="my-section__lottie-animation"></div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { sectionAssetUrl } from '../../../shared/utils/section-asset-url';
import { useLottieAnimationLocal } from '../utils/use-lottie-animation-local';
interface Props {
show: boolean;
}
const props = defineProps<Props>();
async function loadAnimationData() {
const url = sectionAssetUrl('my-animation.json', import.meta.url);
const response = await fetch(url);
return response.json();
}
const showAnimation = computed(() => props.show);
const { lottieContainer } = useLottieAnimationLocal(loadAnimationData, showAnimation);
</script><template>
<div v-show="show" class="my-section__animated-bg">
<div class="my-section__lottie-animation"></div>
</div>
</template>
<script setup lang="ts">
interface Props {
show: boolean;
}
defineProps<Props>();
</script><!-- MyClient.vue -->
<script setup lang="ts">
import AnimatedBackground from './components/AnimatedBackground.vue';
</script>
<!-- MyServer.vue -->
<script setup lang="ts">
import AnimatedBackground from './components/AnimatedBackgroundServer.vue';
</script>showAnimatedBackground: {
type: 'TOGGLE',
label: '$label.show_animated_background.label',
description: '$label.show_animated_background.description',
defaults: {
enabled: true,
},
},const showAnimation = computed(() => settings.showAnimatedBackground.enabled);