或多或少地完成了我的第一个React + Redux应用程序,我已经到了我想将动画应用于应用程序的各个部分的程度。看看现有的解决方案,我发现没有什么比我想要的更接近了。整个ReactCSSTransitionGroup
似乎都是处理动画的一种侵入性和天真的方式。动画问题会使您想要制作动画的组件流失,而您无法了解应用程序中发生的任何事情。从我最初的分析开始,我已经提出了以下关于我认为是一个好的动画API的要求:
排队动画的细节可以由现有的动画库处理,但应该可以将它与react和redux系统联系起来。
我尝试过的一种方法是创建一个像这样的装饰器函数(它是TypeScript,但我不认为这个问题太重要了):
export function slideDown<T>(Component: T) {
return class FadesUp extends React.Component<any, any> {
private options = { duration: 0.3 };
public componentWillEnter (callback) {
const el = findDOMNode(this).childNodes[0] as Element;
if (!el.classList.contains("animated-element")) {
el.classList.add("animated-element");
}
TweenLite.set(el, { y: -el.clientHeight });
TweenLite.to(el, this.options.duration, {y: 0, ease: Cubic.easeIn, onComplete: callback });
}
public componentWillLeave (callback) {
const el = findDOMNode(this).childNodes[0] as Element;
if (!el.classList.contains("animated-element")) {
el.classList.add("animated-element");
}
TweenLite.to(el, this.options.duration, {y: -el.clientHeight, ease: Cubic.easeIn, onComplete: callback});
}
public render () {
const Comp = Component as any;
return <div style={{ overflow: "hidden", padding: 5, paddingTop: 0}}><Comp ref="child" {...this.props} /></div>;
}
} as any;
}
......可以像这样应用......
@popIn
export class Term extends React.PureComponent<ITermStateProps & ITermDispatchProps, void> {
public render(): JSX.Element {
const { term, isSelected, onSelectTerm } = this.props;
return <ListItem rightIcon={<PendingReviewIndicator termId={term.id} />} style={isSelected ? { backgroundColor: "#ddd" } : {}} onClick={onSelectTerm}>{term.canonicalName}</ListItem>;
}
}
不幸的是,它需要将组件定义为类,但它确实可以声明性地将动画添加到组件而无需修改它。我喜欢这种方法,但讨厌我必须将组件包装在转换组中 - 它也不会解决任何其他要求。
我对React和Redux的内部和扩展点知之甚少,不知道如何处理这个问题。我认为thunk动作是管理动画流程的好地方,但我不想将动作组件发送到动作中。相反,我希望能够检索动作的源组件或类似的东西。另一个角度可以是一个专门的减速器,它传递动作和源组件,允许你以某种方式匹配它们并安排动画。
所以我猜我所追求的是以下一项或多项:
答案 0 :(得分:2)
我希望我理解一切正确......处理React + Redux意味着,在最好的情况下,您的组件是纯粹的功能。所以应该动画的组件应该(恕我直言)至少采用一个参数:p
,它代表动画的状态。 p
应位于[0,1]
区间内,零代表开始,1代表结束,其间的一切代表当前进度。
const Accordion = ({p}) => {
return (
…list of items, each getting p
);
}
所以问题是,在动画开始之后,如何在某个事件触发该过程之后,动画开始之前如何分派动作(什么是异步事物)。
Middleware在这里派上用场,因为它可以“处理”已分派的动作,将其转换为另一个动作,或转换为多个
//middleware/animatror.js
const animator = store => next => action => {
if (action.type === 'animator/start') {
//retrieve animation settings
const { duration, start, … } = action.payload;
animationEngine.add({
dispatch,
action: {
progress: () => { … },
start: () => { … },
end: () => { … }
}
})
} else {
return next(action);
}
}
export default animator;
因此animationEngine
是AnimatoreEngine
的实例,一个侦听window.requestAnimationFrame
事件并调度适当操作的Object。可以使用中间件的创建来实例化animationEngine
。
const createAnimationMiddleware = () => {
const animatoreEngine = new AnimatorEngine;
return const animator = store => next => action => { … }
}
export default createAnimationMiddleware;
//store.js
const animatorMiddleware = createAnimationMiddleware();
…
const store = createStore(
…,
applyMiddleware(animatorMiddleware, …)
)
基本的想法是,“吞噬”类型animator/start
或其他类型的动作,并将它们转换为一堆“子动作”,这些动作在action.payload
中配置。
在中间件中,您可以访问dispatch
和action
,因此您可以从那里调度其他操作,也可以使用progress
参数调用这些操作。
这里显示的代码远未完成,但试图弄清楚这个想法。我有构建»远程«中间件,它处理我的所有请求。如果操作类型为get:/some/path
,则它基本上触发启动操作,该操作在有效负载中定义,依此类推。在动作中它看起来像这样:
const modulesOnSuccessAction => data {
return {
type: 'modules/listing-success',
payload: { data }
}
}
const modulesgetListing = id => dispatch({
type: `get:/listing/${id}`,
payload: {
actions: {
start: () => {},
…
success: data => modulesOnSuccessAction(data)
}
}
});
export { getListing }
所以我希望我可以传输Idea,即使代码还没有准备好复制/粘贴。