Skip to content

Commit

Permalink
feat: add custom & initial props to animate-presence
Browse files Browse the repository at this point in the history
  • Loading branch information
nandorojo committed Aug 31, 2021
1 parent 2bba522 commit 7fafc30
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 97 deletions.
4 changes: 2 additions & 2 deletions examples/with-expo/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// export { default } from './src/Moti.Skeleton'
// export { default } from './src/Moti.Gallery'
// export { default } from './src/Moti.Sequence'
// export { default } from './src/Moti.Presence'
export { default } from './src/Moti.Presence'
// export { default } from './src/Headless-Exit'
export { default } from './src/Moti.Progress'
// export { default } from './src/Moti.Progress'
// export { default } from './src/Moti.HelloWorld'
// export { default } from './src/Moti.Variants'
1 change: 0 additions & 1 deletion examples/with-expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"expo": "^41.0.0",
"expo-splash-screen": "~0.8.1",
"expo-status-bar": "~1.0.3",
"framer-motion": "^3.2.1",
"lodash.set": "^4.3.2",
"moti": "^0.11.0",
"react": "16.13.1",
Expand Down
27 changes: 8 additions & 19 deletions examples/with-expo/src/Moti.Gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ import { MotiImage, AnimatePresence, Text } from 'moti'
import React, { useState } from 'react'
import { StyleSheet, View, Image, Dimensions } from 'react-native'

// const photos: { url: string }[] = new Array(50).fill('').map((_, i) => ({
// url: `https://source.unsplash.com/random?sig=${i}`,
// }))

const photos = [
`https://images.unsplash.com/photo-1551871812-10ecc21ffa2f?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=929&q=80`,
`https://images.unsplash.com/photo-1530447920184-b88c8872?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTN8fHJvY2tldHxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60`,
Expand All @@ -20,20 +16,9 @@ const width = size.width
export default function Gallery() {
const [[index, direction], setIndex] = useState([0, 0])

// React.useEffect(() => {
// photos.forEach(({ url }) => {
// Image.prefetch(url).catch(() => console.log('🥸'))
// })
// }, [])

const paginate = (direction: 1 | -1) => () => {
setIndex(([current]) => {
const nextIndex = current + direction
// if (nextIndex > photos.length - 1) return [0, direction * -1]

// if (nextIndex < 0) return [photos.length - 1, direction * -1]

// return [nextIndex, direction]

const normalizedIndex = Math.max(
0,
Expand All @@ -47,7 +32,7 @@ export default function Gallery() {

return (
<View style={styles.container}>
<AnimatePresence>
<AnimatePresence custom={direction}>
<MotiImage
from={{
opacity: 0,
Expand All @@ -57,9 +42,13 @@ export default function Gallery() {
opacity: 1,
translateX: 0,
}}
exit={{
opacity: 0,
translateX: direction < 0 ? -width : width,
exit={(custom) => {
'worklet'
console.log('[gallery] exiting', { custom })
return {
opacity: 0,
translateX: custom < 0 ? -width : width,
}
}}
style={styles.image}
key={url}
Expand Down
18 changes: 14 additions & 4 deletions examples/with-expo/src/Moti.Presence.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ function Shape() {
opacity: 1,
scale: 1,
}}
exit={{
opacity: 0,
scale: 0.9,
exit={(custom) => {
'worklet'
console.log('[exit]', { custom })
return {
opacity: 0,
scale: 0.9,
}
}}
// exit={{
// opacity: 0,
// scale: 0.9,
// }}
exitTransition={{
type: 'timing',
duration: 2500,
Expand All @@ -31,7 +39,9 @@ export default function Presence() {

return (
<Pressable onPress={toggle} style={styles.container}>
<AnimatePresence>{visible && <Shape />}</AnimatePresence>
<AnimatePresence custom={!visible ? 'hidden' : 'visible'}>
{visible && <Shape key={'i'} />}
</AnimatePresence>
</Pressable>
)
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ export interface MotiProps<
*
* It follows the same API as the `exit` prop from `framer-motion`. Feel free to reference their docs: https://www.framer.com/api/motion/animate-presence/
* */
exit?: AnimateWithTransitions | boolean
exit?:
| AnimateWithTransitions
| boolean
| ((custom?: unknown) => AnimateWithTransitions)
/**
* Define animation configurations.
*
Expand Down
89 changes: 55 additions & 34 deletions packages/core/src/use-map-animate-to-style.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { usePresence } from 'framer-motion'
import { useCallback, useEffect } from 'react'
import { PresenceContext, usePresence } from 'framer-motion'
import { useCallback, useContext, useEffect, useRef } from 'react'
import type { TransformsStyle } from 'react-native'
import Animated, {
useAnimatedStyle,
Expand All @@ -11,6 +11,7 @@ import Animated, {
withRepeat,
withSequence,
runOnJS,
useDerivedValue,
} from 'react-native-reanimated'
import { PackageName } from './constants/package-name'
import type { MotiProps, Transforms, TransitionConfig } from './types'
Expand Down Expand Up @@ -217,10 +218,15 @@ export default function useMapAnimateToStyle<Animate>({
}: MotiProps<Animate>) {
const isMounted = useSharedValue(false)
const [isPresent, safeToUnmount] = usePresence()
const presence = useContext(PresenceContext)

const reanimatedSafeToUnmount = useCallback(() => {
const disableInitialAnimation =
presence?.initial === false && !animateInitialState
const custom = useDerivedValue(() => presence?.custom, [presence])

const reanimatedSafeToUnmount = useRef(() => {
safeToUnmount?.()
}, [safeToUnmount])
}).current

const reanimatedOnDidAnimated = useCallback<NonNullable<typeof onDidAnimate>>(
(...args) => {
Expand All @@ -229,9 +235,6 @@ export default function useMapAnimateToStyle<Animate>({
[onDidAnimate]
)

const hasExitStyle =
typeof exit === 'object' && !!Object.keys(exit ?? {}).length

const style = useAnimatedStyle(() => {
const final = {
// initializing here fixes reanimated object.__defineProperty bug(?)
Expand All @@ -241,7 +244,13 @@ export default function useMapAnimateToStyle<Animate>({

const animateStyle = animate || {}
const initialStyle = from || {}
const exitStyle = exit || {}
let exitStyle = exit || {}
if (typeof exitStyle === 'function') {
exitStyle = exitStyle(custom.value)
}

const hasExitStyle =
typeof exitStyle === 'object' && !!Object.keys(exitStyle ?? {}).length

const isExiting = !isPresent && hasExitStyle

Expand All @@ -252,6 +261,14 @@ export default function useMapAnimateToStyle<Animate>({
mergedStyles = Object.assign({}, variantStyle, animateStyle)
}

if (!isMounted.value) {
if (!disableInitialAnimation) {
mergedStyles = initialStyle as Animate
}
} else {
mergedStyles = Object.assign({}, initialStyle, mergedStyles)
}

if (isExiting && exitStyle) {
mergedStyles = Object.assign({}, exitStyle) as any
}
Expand All @@ -275,7 +292,7 @@ export default function useMapAnimateToStyle<Animate>({
}

Object.keys(mergedStyles).forEach((key) => {
const initialValue = initialStyle[key]
// const initialValue = initialStyle[key]
const value = mergedStyles[key]

const {
Expand Down Expand Up @@ -307,29 +324,29 @@ export default function useMapAnimateToStyle<Animate>({
}
}

if (initialValue != null) {
// if we haven't mounted, or if there's no other value to use besides the initial one, use it.
if (isMounted.value === false || value == null) {
if (isTransform(key) && final.transform) {
const transform = {} as Transforms
if (isMounted.value || animateInitialState) {
transform[key] = animation(initialValue, config)
} else {
transform[key] = initialValue
}

// final.transform.push({ [key]: initialValue }) does not work!
final.transform.push(transform)
} else {
if (isMounted.value || animateInitialState) {
final[key] = animation(initialValue, config)
} else {
final[key] = initialValue
}
}
return
}
}
// if (initialValue != null) {
// // if we haven't mounted, or if there's no other value to use besides the initial one, use it.
// if (isMounted.value === false || value == null) {
// if (isTransform(key) && final.transform) {
// const transform = {} as Transforms
// if (isMounted.value || animateInitialState) {
// transform[key] = animation(initialValue, config)
// } else {
// transform[key] = initialValue
// }

// // final.transform.push({ [key]: initialValue }) does not work!
// final.transform.push(transform)
// } else {
// if (isMounted.value || animateInitialState) {
// final[key] = animation(initialValue, config)
// } else {
// final[key] = initialValue
// }
// }
// return
// }
// }

let { delayMs } = animationDelay(key, transition, defaultDelay)

Expand Down Expand Up @@ -491,13 +508,17 @@ export default function useMapAnimateToStyle<Animate>({
isMounted.value = true
}, [isMounted])

const missingExit =
typeof exit === 'function' ||
(typeof exit === 'object' && exit && Object.keys(exit).length > 0)

useEffect(
function allowUnMountIfMissingExit() {
if (!isPresent && !hasExitStyle) {
if (!isPresent && !missingExit) {
safeToUnmount?.()
}
},
[hasExitStyle, isPresent, safeToUnmount]
[exit, missingExit, isPresent, safeToUnmount]
)

return {
Expand Down
36 changes: 0 additions & 36 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11634,19 +11634,6 @@ fragment-cache@^0.2.1:
dependencies:
map-cache "^0.2.2"

framer-motion@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-3.2.1.tgz#66eeb883a0b5c425dd7767ecacdeac451c184cdb"
integrity sha512-5AWrh4JElgFAXWLqk0u8lVcdkigyuofyEy2LSsjuCxKbAb1hHqRn3PPdrV0KgPrysTHq95QO1bHFTLA7/Q8g+Q==
dependencies:
framesync "^5.0.0"
hey-listen "^1.0.8"
popmotion "^9.1.0"
style-value-types "^4.0.1"
tslib "^1.10.0"
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"

framer-motion@^3.9.1:
version "3.9.1"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-3.9.1.tgz#18ff88c090428e253084f8c3da85619dd2b0f992"
Expand All @@ -11660,11 +11647,6 @@ framer-motion@^3.9.1:
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"

framesync@5.0.0, framesync@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.0.0.tgz#7de8caedf53ac441118e79680f1beb7391c328b6"
integrity sha512-wd8t+JsQGisluSv1twiEeDv0aNGpavGb9q7xgIk9fGbcIWkNXF/KVtrjnOrCwBWJuiXxlJfNkcvGudsI32FxYA==

framesync@5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.2.0.tgz#f14480654cd05a6af4c72c9890cad93556841643"
Expand Down Expand Up @@ -17587,16 +17569,6 @@ popmotion@9.3.1:
style-value-types "4.1.1"
tslib "^1.10.0"

popmotion@^9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.1.0.tgz#4360d06bd18ce8baa8f9284ecec7d55344af6325"
integrity sha512-+J7pzzBy5kk2qsP8ilowKs/CH+HoZa3kOGEBNCleCvsPXEF3nKHdfAR3SboMyPvdpIrofaT7ZIy/xWgz446Azw==
dependencies:
framesync "5.0.0"
hey-listen "^1.0.8"
style-value-types "^4.0.1"
tslib "^1.10.0"

portfinder@^1.0.26:
version "1.0.28"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
Expand Down Expand Up @@ -20591,14 +20563,6 @@ style-value-types@4.1.1:
hey-listen "^1.0.8"
tslib "^1.10.0"

style-value-types@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-4.0.1.tgz#23f05dd03e8a850654defc22cf03ebac572aaa00"
integrity sha512-aOV/HHyynIyTmU27qfs0oAHhFde6BFIvV4+nMerE2MAPZMwYOeQk1/F3S6djxF2u4HdbiieCPs3ZzWsbNUoc9A==
dependencies:
hey-listen "^1.0.8"
tslib "^1.10.0"

styled-jsx@3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.2.1.tgz#452051fe50df5e9c7c7f3dd20fa46c3060ac65b0"
Expand Down

0 comments on commit 7fafc30

Please sign in to comment.