How to animate a dom tree in re-frame without recreating that whole tree?

79 Views Asked by At

In my re-frame app, I have a non-trivial svg that is animated. Currently, every animation "frame" means recreating the whole svg, although only the style="display: none" of one element is set to style="display: inherit" to make it visible, and another element is hidden in the same fashion.

I would like to change this so the svg tree gets created only once, and the animation is done by changing the style of the elements only (I guess that could be done either by changing the inline style of elements, or by using a separate that gets changed per frame).

What is the best way to do this?

N.B. I already have two separate subscriptions: One that only triggers when the svg needs to change (which is rare), and one that gets triggered for every animation "frame" (which is frequent).

1

There are 1 best solutions below

0
On

I'm now using an interceptor to do the job. Whenever an event of interest is fired, this interceptor will swap in a different style element in the html's head.

(ns myapp.myfile
  (:require
   [goog.dom :as gdom]
   [garden.core :as garden]))
; ...
(def highlighter
  (re-frame.core/->interceptor
   :id      :highlighter
   :before  (fn [context]
              (let [[_ event-arg] (re-frame.core/get-coeffect context :event)]
                (let [old-style (.getElementById js/document "highlighter-style-id")
                      css (garden/css [(str "#highlight-" elem-idx) {:opacity 0}])
                      new-style (gdom/createDom "style"
                                                (clj->js {:type "text/css"
                                                          :id "highlighter-id"})
                                                css)]
                  (if old-style
                    (gdom/replaceNode new-style old-style)
                    (gdom/appendChild (.-head js/document) new-style))
                  ))
              context)))

I'm not so sure this is an idiomatic way to do it in re-frame, but it works, in that my heavy DOM creation function now only needs to run on a rare event, and the more frequent event now only causes a <style> swap. The app got noticeably snappier.