Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion playground/pages/tabs/tab4.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<strong>Basic animation</strong>
</IonLabel>
<IonAnimation
ref="animation1"
id="animation1"
v-slot="{ animation }"
:duration="2000"
:fromTo="[
Expand All @@ -42,6 +42,7 @@
<strong>Keyframes animation</strong>
</IonLabel>
<IonAnimation
id="animation2"
v-slot="{ animation }"
:duration="3000"
:keyframes="[
Expand All @@ -67,6 +68,7 @@
<strong>Animation that repeats forever</strong>
</IonLabel>
<IonAnimation
id="animation3"
v-slot="{ animation }"
:duration="1000"
:keyframes="[
Expand All @@ -91,6 +93,7 @@
<strong>Animation with style hooks</strong>
</IonLabel>
<IonAnimation
id="animation4"
v-slot="{ animation }"
:duration="2000"
:keyframes="[
Expand Down Expand Up @@ -123,6 +126,7 @@
<strong>Animation with specific easing</strong>
</IonLabel>
<IonAnimation
id="animation5"
v-slot="{ animation }"
:duration="2000"
:keyframes="[
Expand Down Expand Up @@ -156,6 +160,7 @@
<strong>Reversed animation direction</strong>
</IonLabel>
<IonAnimation
id="animation6"
v-slot="{ animation }"
:duration="3000"
:keyframes="[
Expand Down
49 changes: 26 additions & 23 deletions src/runtime/components/IonAnimation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ interface AnimationOptions {
easing?: string
fill?: AnimationFill
direction?: AnimationDirection
from?: AnimationFromObject | AnimationFromObject[]
fromTo?: AnimationFromToObject | AnimationFromToObject[]
keyframes?: AnimationKeyFrames
from?: AnimationFromObject | AnimationFromObject[] | null
fromTo?: AnimationFromToObject | AnimationFromToObject[] | null
keyframes?: AnimationKeyFrames | null
playOnMount?: boolean
playOnVisible?: boolean
beforeStyles?: AnimationStyles
beforeAddClass?: string | string[]
beforeClearStyles?: string[]
afterStyles?: AnimationStyles
afterAddClass?: string | string[]
afterClearStyles?: string[]
beforeStyles?: AnimationStyles | null
beforeAddClass?: string | string[] | null
beforeClearStyles?: string[] | null
afterStyles?: AnimationStyles | null
afterAddClass?: string | string[] | null
afterClearStyles?: string[] | null
}

const props = withDefaults(defineProps<AnimationOptions>(), {
Expand All @@ -63,13 +63,15 @@ const props = withDefaults(defineProps<AnimationOptions>(), {
afterClearStyles: null,
})

const element = ref<HTMLDivElement>(null)
const element = ref<HTMLDivElement | null>(null)

const animation = ref<Animation>(null)
const animation = ref<Animation | null>(null)

let observer: IntersectionObserver

onMounted(() => {
animation.value = createAnimation(props.id)
.addElement(element.value)
.addElement(element.value!)
.duration(props.duration)
.iterations(props.iterations)
.easing(props.easing)
Expand All @@ -86,14 +88,14 @@ onMounted(() => {
let hasKeyframes = Array.isArray(props.keyframes) && props.keyframes.length > 0

if (hasKeyframes) {
animation.value.keyframes(props.keyframes)
animation.value.keyframes(props.keyframes!)
}

// From
if (props.from !== null && !hasKeyframes) {
if (Array.isArray(props.from)) {
props.from.forEach(({ property, fromValue }) => {
animation.value.from(property, fromValue)
animation.value!.from(property, fromValue)
})
} else {
animation.value.from(props.from.property, props.from.fromValue)
Expand All @@ -104,18 +106,18 @@ onMounted(() => {
if (props.fromTo !== null && !hasKeyframes) {
if (Array.isArray(props.fromTo)) {
props.fromTo.forEach(({ property, fromValue, toValue }) => {
animation.value.fromTo(property, fromValue, toValue)
animation.value!.fromTo(property, fromValue, toValue)
})
} else {
animation.value.fromTo(props.fromTo.property, props.fromTo.fromValue, props.fromTo.toValue)
}
}

if (props.playOnVisible && !props.playOnMount) {
const observer = new IntersectionObserver(
observer = new IntersectionObserver(
() => {
// Play animation
animation.value.play()
animation.value!.play()
// Disconnect observer - making animation always trigger ONLY ONCE
observer.disconnect()
},
Expand All @@ -127,17 +129,18 @@ onMounted(() => {
}
)
// Start observing for animation element
observer.observe(element.value)

// Disconnect observer when component is about to be unmounted
onBeforeUnmount(() => observer.disconnect())
observer.observe(element.value!)
} else if (props.playOnMount) animation.value.play()
})
onBeforeUnmount(() => {
animation.value.destroy()
//Destroy animation and disconnect observer when component is about to be unmounted if it is defined
animation.value?.destroy()
if (observer) observer.disconnect()
})
</script>

<template>
<slot ref="element" :animation="animation" />
<div ref="element">
<slot :animation="animation" />
</div>
</template>