reagent

2024-04-19T17:33:01.398079Z

i'm considering adding animations to my card game, but i'm not sure where to begin. currently, the client gets a snapshot of the game state when the game starts, and then gets diffs as players take actions. these diffs are merged into the game state. the game state is rendered with normal reagent hiccup functions, using cursors to keep updates scoped to the specific keys on game state. i'd like to add animations for card movement, as small as a gentle "bump up" when a card leaves the hand and a "drop down" where the card lands on the play area. this is hard to do when i'm just mapping over cards in hand, calling [render-card ...] on each. is there a way to "watch" when a react/reagant node is created or deleted?

✅ 1
p-himik 2024-04-19T17:35:41.443549Z

You can do it with plain CSS, e.g. https://thinkdobecreate.com/articles/css-animating-newly-added-element/

2024-04-19T17:39:46.719769Z

oh that's cool! is there a way to do the reverse, when it's removed?

p-himik 2024-04-19T17:44:20.930329Z

To a degree - you can add a particular class, e.g. removing, that triggers the removal animation, and then actually remove the item in the transitionend event handler that you attach to the same element that you're animating.

p-himik 2024-04-19T17:45:06.358339Z

But that'll probably require also altering the state of your app a bit so it can handle "removed from the business part of the data but not removed from the rendering data" kind of thing.

👍 1
2024-04-19T17:45:14.978449Z

how do i do that when i'm relying on the ratom to handle those removals?

p-himik 2024-04-19T17:45:45.491039Z

So a ratom stores a collection of currently visible items?

2024-04-19T17:47:26.408459Z

yeah i have the entire game state in a single ratom and a bunch of cursors to render specific parts (hand, play area, etc). then i just rely on the reactivity to update things when i merge in each diff

p-himik 2024-04-19T17:57:09.349169Z

Something like this, haven't tested.

(defn item [{:keys [data on-removing-start on-removing-end]}]
  (r/with-let [on-transition-end (r/atom nil)]
    [:div {:class             (when @on-transition-end :removing)
           :on-transition-end @on-transition-end}
     data
     [:button {:type     :button
               :on-click (fn [_]
                           (on-removing-start)
                           (reset! on-transition-end (fn [_] (on-removing-end))))}
      "Remove"]]))

(defn the-list []
  (r/with-let [items-data (r/atom [...])
               ;; Use it for business logic.
               ;; Or make that logic aware of the `:removed?` flag.
               not-removed-items (r/reaction (filterv (complement :removed?)
                                                      @items-data))]
    (into [:div]
          (map-indexed (fn [idx data]
                         [item {:data              data
                                :on-removing-start #(swap! items-data assoc-in [idx :removed?] true)
                                :on-removing-end   #(swap! items-data remove-idx-from-vec idx)}]))
          items-data)))

2024-04-19T18:20:45.217329Z

hmmm! that's clever

2024-04-19T18:20:47.502439Z

i'll try that out

2024-04-19T18:20:49.326939Z

thank you

👍 1