Creating animations with Clojurescript Om

2019-01-22 09:46发布

I've been looking around for how to create animations in Om, I've tried creating a RaphaelJs component with moderate success. I get the animation I want, but for some reason Om renders multiple instances of the SVG element.

enter image description here

Looking at the animation example in the Om github folder uses setInterval to change the values you want to animate, which is less than ideal.

I'm aware of the CSSTransitionGroup addon, but it looks like you can only flip between preset animations defined in the CSS, you can't decide to do something like rendering a path and having a shape following it with randomised timings. Please feel free to correct me if you can dynamically define animations using it.

Does anyone have any good examples of performing simple animations? Just translating or rotating simple shapes would give me an idea of how to start tackling it from there.

2条回答
Explosion°爆炸
2楼-- · 2019-01-22 10:12

You can use CSSTransitionGroup to animate position/movement, orientation and other visual properties like opacity, color, borders or shadows (perhaps using keyframes) or more complex hacks. The major limitation of this approach is that it only allows you to animate mounting and unmounting of components and then only through animation defined in CSS.

If you need to animate components during their mounted lifetime or you want more fine-grained control over what you can animate, you might want to take another approach like what I do in this code.

This is how you would use CSSTransitionGroup from Om.

For this to work, you need to use the with-addons version of React (eg react-with-addons-0.12.1.js or react-with-addons-0.12.1.min.js).

(def css-trans-group (-> js/React (aget "addons") (aget "CSSTransitionGroup"))) 
(defn transition-group
  [opts component]
  (let [[group-name enter? leave?] (if (map? opts)
                                     [(:name opts) (:enter opts) (:leave opts)]
                                     [opts true true])]
    (apply
      css-trans-group
      #js {:transitionName group-name
           :transitionEnter enter?
           :transitionLeave leave?}
      component)))

Then to use it, you can do:

(transition-group "example" (when visible? (om/build my-component data)))

Now toggle visible? to animate my-component being mounted and unmounted. If you want to disable either the enter or leave animation:

(transition-group
  {:name "example"
   :enter false} ; Only animate when component gets unmounted, not when mounted
  (when visible? (om/build my-component data)))

You can also animate adding or removing from/to lists of items:

(transition-group "example" (om/build-all my-component list-of-data))

Or using map, perhaps something like:

(transition-group "example" (map #(dom/li %) list-of-data))

You also need to add the correct CSS:

.example-enter {
  opacity: 0.01;
  transition: opacity .5s ease-in;
}

.example-enter.example-enter-active {
  opacity: 1;
}

.example-leave {
  opacity: 1;
  transition: opacity .5s ease-in;
}

.example-leave.example-leave-active {
  opacity: 0.01;
}

Note that unless you disable one of the animations, you need to include both in the CSS. For example, if you leave out the leave animation, then your component may not get unmounted as React will hang waiting for the animation to complete. Simple fix is to disable it using {:leave false} or to include the leave animation in your CSS.

One other gotcha to be aware of: this will only animate child components if the transition group is mounted before the children. If the children and the transition group are mounted at the same time, then they won't be animated. This can be a bit awkward sometimes. For example, the above code snippets would not animate without the (when visible? ...) as without toggling, the child would be mounted at the same time as the transition group. Also, the build-all example below works best if list-of-data is not prepopulated but instead populated after mounting. For this reason, CSSTransitionGroups work best for code that switches between views/components or lists of data that gets modified by the user, but doesn't work for animating initial display of components on page load.

Perhaps something like:

(transition-group "view-selection"
  (condp = current-view
    "home" (om/build home-page data)
    "blog" (om/build blog-page data)
    "about" (om/build about-page data)
    :else (om/build error-404-page data)))

-

Finally, if you do not wish to use a helper function, you can use css-trans-group directly:

    (css-trans-group
      #js {:transitionName "example"}
      (when visible? (om/build my-component data)))))

Or, if using a lists of child components (eg through map or build-all):

    (apply
      css-trans-group
      #js {:transitionName "example"}
      (om/build-all my-component list-of-data))))

I have not yet used the low-level TransitionGroup API. More information can be found on the React CSSTransitionGroup page.

查看更多
【Aperson】
3楼-- · 2019-01-22 10:19

Care to look into a ClojureScript port of https://github.com/chenglou/react-tween-state or https://github.com/chenglou/react-state-stream?

TransitionGroup provides nothing but helpers for hooking onto some lifecycle events. It theoretically has nothing to do with animation. If you want an actual animation API, give a look at the two things I made above. The readmes should provide enough information for the rest.

查看更多
登录 后发表回答