基于操作的React / Redux动画 [英] React/Redux animations based on actions

查看:86
本文介绍了基于操作的React / Redux动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

或多或少地完成了我的第一个React + Redux应用程序,我已经到了我想将动画应用于应用程序的各个部分的程度。看看现有的解决方案,我发现没有什么比我想要的更接近了。整个 ReactCSSTransitionGroup 似乎都是处理动画的一种侵入性和天真的方式。动画问题会使您想要制作动画的组件流失,而您无法了解应用程序中发生的任何事情。从我最初的分析中我得出了以下关于我认为好的动画API的要求:

Having more or less completed my first React+Redux application, I have come to the point where I would like to apply animations to various parts of the application. Looking at existing solutions, I find that there is nothing even close to what I would like. The whole ReactCSSTransitionGroup seems to be both an intrusive and naive way to handle animations. Animation concerns bleed out of the component you want to animate and you have no way to know anything about what happens in the application. From my initial analysis I have come up with the following requirements for what I would consider a good animation API:


  • 父组件应该是无知的子组件如何淡入/淡出(可能除了交错的动画之外)。

  • 动画应与React集成,以便允许组件淡出(或以其他方式完成动画在删除它们之前。

  • 应该可以在不修改组件的情况下将动画应用于组件。可以将组件设置为与动画兼容,但不应该有与动画相关的道具,状态,上下文或组件,动画也不应该指示如何创建组件。

  • 应该可以根据动作和应用程序状态执行动画 - 换句话说,当我拥有所发生事件的完整语义上下文时。例如,我可能会在创建事物时淡入组件,但不会在页面加载项目时淡入。或者,我可以根据用户的设置选择合适的淡出动画。

  • 应该可以为组件排队或组合动画。

  • 应该可以使用父组件将动画排入队列。例如,如果一个组件有两个子组件,并且一个组件在打开之前首先触发另一个组件关闭。

  • The parent component should be ignorant of how the child component fades in/out (maybe with the exception of staggered animations).
  • The animations should integrate with React such that components are allowed to fade out (or otherwise complete their animations) before they are removed.
  • It should be possible to apply an animation to a component without modifying the component. It is ok that the component is styled such as to be compatible with the animation, but there should be no props, state, contexts or components related to the animation, nor should the animation dictate how the component is created.
  • It should be possible to perform an animation based on an action and the application state - in other words, when I have the full semantic context of what happened. For example, I might fade in a component when a thing has been created, but not when the page loads with the item in it. Alternatively, I might select the proper fade out animation, based on the user's settings.
  • It should be possible to either enqueue or combine an animation for a component.
  • It should be possible to enqueue an animation with a parent component. For example, if a component has two sub components and opening one would first trigger the other to close before opening itself.

具体情况排队动画可以由现有的动画库处理,但应该可以将它与react和redux系统联系起来。

The specifics of enqueuing animations can be handled by an existing animation library, but it should be possible to tie it in with the react and redux system.

我尝试过的一种方法是创建一个像这样的装饰器函数(它是TypeScript,但我不认为这个问题太重要了):

One approach I have tried out is to create a decorator function like this (it is TypeScript, but I don't think that matters too much with regards to the problem):

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;
}

...可以像这样应用......

...which can be applied like this...

@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>;
    }
}

不幸的是,它需要将组件定义为类,但它确实可以声明性地将动画添加到组件而无需修改它。我喜欢这种方法,但讨厌我必须将组件包装在一个转换组中 - 它也没有解决任何其他要求。

Unfortunately it requires the component to be defined as a class, but it does make it possible to declaratively add an animation to a component without modifying it. I like this approach but hate that I have to wrap the component in a transition group - nor does it address any of the other requirements.

我不太了解React和Redux的内部和扩展点很好地了解如何处理这个问题。我认为thunk动作是管理动画流程的好地方,但我不想将动作组件发送到动作中。相反,我希望能够检索动作的源组件或类似的东西。另一个角度可以是一个专门的减速器,它同时传递动作和源组件,允许你以某种方式匹配它们并安排动画。

I don't know enough about the internal and extension points of React and Redux to have a good idea how to approach this. I figured thunk actions would be a good place to manage the animation flows but I don't want to send the action components into the actions. Rather, I would like to be able to retrieve the source component for an action or something like that. Another angle could be a specialized reducer which passes in both the action and the source component, allowing you to match them somehow and schedule animations.

所以我猜我是什么以下是以下一项或多项:

So I guess what I'm after is one or more of the following:


  • 挂钩到React和/或Redux的方法,最好不要破坏性能或违反基本假设图书馆。

  • 是否有任何现有的库可以解决部分或全部这些问题,并且很容易集成到应用程序中。

  • 通过使用普通动画工具或集成到正常构建块中来实现所有或大部分目标的技术或方法。

推荐答案

我希望我理解一切正确......处理React + Redux意味着,在最好的情况下,你的组件是纯粹的功能。所以应该动画的组件应该(恕我直言)至少采用一个参数: p ,它代表动画的状态。 p 应该在 [0,1] 的区间内,零代表开始,1代表结束,一切当前进展之间。

I hope I understood everything right… Dealing with React + Redux means, that in best case your components are pure functional. So a component that should be animated should (IMHO) at least take one parameter: p, which represents the state of the animation. p should be in the interval [0,1] and zero stands for the start, 1 for the end and everything in between for the current progress.

const Accordion = ({p}) => {
  return (
    …list of items, each getting p
  );
}

所以问题是,如何随时间推送行动(什么是<在动画开始之后,动画结束,在某个事件触发了该过程之后,强>异步事物)。

So the question is, how to dispatch actions over time (what is an asynchronous thing), after the animation started, until the animation is over, after a certain event triggered that process.

中间件在这里派上用场,因为它可以处理已发送的操作,将它们转换为另一个,或者成多个

Middleware comes in handy here, since it can »process« dispatched actions, transform them into another, or into multiple

//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 是<的实例code> AnimatoreEngine ,一个侦听 window.requestAnimationFrame 事件并调度相应Actions的Object。可以使用中间件的创建来实例化 animationEngine

Whereby the animationEngine is an Instance of AnimatoreEngine, an Object that listens to the window.requestAnimationFrame event and dispatches appropriate Actions. The creation of the middleware can be used the instantiate the 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 中配置。

The basic Idea is, to »swallow« actions of type animator/start, or something else, and transform them into a bunch of »subactions«, which are configured in the action.payload.

在中间件中,您可以访问 dispatch 操作,这样您就可以从那里调度其他操作,并且可以使用 progress 参数调用这些参数。

Within the middleware, you can access dispatch and the action, so you can dispatch other actions from there, and those can be called with a progress parameter as well.

此处显示的代码远未完成,但是试图找出这个想法。我有构建»远程«中间件,它处理我的所有请求。如果操作类型是 get:/ some / path ,它基本上会触发一个启动操作,该操作在有效负载中定义,依此类推。在动作中它看起来像这样:

The code showed here is far from complete, but tried to figure out the idea. I have build »remote« middleware, which handles all my request like that. If the actions type is get:/some/path, it basically triggers a start action, which is defined in the payload and so on. In the action it looks like so:

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,即使代码还没有准备好复制/粘贴。

So I hope I could transport the Idea, even if the code is not ready for Copy/Paste.

这篇关于基于操作的React / Redux动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆