我定义了以下结构,以便根据路径名动画布局之间的转换。
<LayoutTransition pathname={pathname}>
{pathname.includes('/registration') && <RegistrationLayout />}
{pathname.includes('/dashboard') && <DashboardLayout />}
</LayoutTransition>
RegistrationLayout
和
DashboardLayout
内部有类似的结构,但它们根据路径名显示不同的页面,而不是布局。
在我的内心
LayoutTransition
组件我有以下逻辑
useLayoutEffect(() => {
// Root paths are "/registration" "/dashboard"
const rootPathname = pathname.split('/')[1];
const rootPrevPathname = prevPathname.current.split('/')[1];
if (rootPathname !== rootPrevPathname) {
/*
* Logic that:
* 1. Animates component to 0 opacity
* 2. Sets activeChildren to null
* 3. Animates component to 1 opacity
*/
}
return () => {
// Sets activeChildren to current one
setActiveChildren(children);
};
}, [pathname]);
/* renders activeChildren || children */
一般来说,这个概念是可行的,也就是说,我在制作动画时看到我的“当前”孩子。
外面的
然后作为
activeChildren
设置为空,我在动画时看到我的“新”孩子
在里面
.
唯一的问题是好像我什么时候开始
setActiveChildren(children);
布局重新呈现,我看到闪烁,布局显示的页面返回到初始状态。
有没有一种方法可以避免这种情况,当我们在外面制作动画时,会冻结孩子,这样就不会在他们身上重新渲染了?
编辑:来自react本机项目的完整代码段
核心思想是我们订阅路由器上下文,当rootpathname更改时,我们将当前布局(子布局)移出,然后将新布局放入。
import React, { useContext, useLayoutEffect, useRef, useState } from 'react';
import { Animated } from 'react-native';
import RouterCtx from '../context/RouterCtx';
import useAnimated from '../hooks/useAnimated';
import { durationSlow, easeInQuad } from '../services/Animation';
/**
* Types
*/
interface IProps {
children: React.ReactNode;
}
/**
* Component
*/
function AnimRouteLayout({ children }: IProps) {
const { routerState } = useContext(RouterCtx);
const { rootPathname } = routerState;
const [activeChildren, setActiveChildren] = useState<React.ReactNode>(undefined);
const [pointerEvents, setPointerEvents] = useState(true);
const prevRootPathname = useRef<string | undefined>(undefined);
const [animatedValue, startAnimation] = useAnimated(1, {
duration: durationSlow,
easing: easeInQuad,
useNativeDriver: true
});
function animationLogic(finished: boolean, value: number) {
setPointerEvents(false);
if (finished) {
if (value === 0) {
setActiveChildren(undefined);
startAnimation(1, animationLogic, { delay: 150 });
}
setPointerEvents(true);
}
}
useLayoutEffect(() => {
if (prevRootPathname.current) {
if (rootPathname !== prevRootPathname.current) {
startAnimation(0, animationLogic);
}
}
return () => {
prevRootPathname.current = rootPathname;
setActiveChildren(children);
};
}, [rootPathname]);
return (
<Animated.View
pointerEvents={pointerEvents ? 'auto' : 'none'}
style={{
flex: 1,
opacity: animatedValue.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }),
transform: [
{
scale: animatedValue.interpolate({ inputRange: [0, 1], outputRange: [1.1, 1] })
}
]
}}
>
{activeChildren || children}
</Animated.View>
);
}
export default AnimRouteLayout;