benlime

benlime

A library for high performance LiveView animations

In the last couple of days I was playing around with LiveView, function components and animations. I ended up with a little prototype which allows you to control animations directly from the server.

The key difference is, that the actual animations will happen in a very performant way directly on the client. So there is nothing like “send every animation frame over the wire happening”.

What do you think about that? I’m thinking to turn this into a library.

Here a little demo video: https://cleanshot-cloud-fra.accelerator.net/media/26144/JoVUJgqmm21U6URHXo5RBP8haH0NZOZQayGHEMYL.mp4

Most Liked

benlime

benlime

Elixir Desktop seems really nice. I’d love to give it a try sometime! It’s absolutely awesome that LiveView can be used to create native applications.

Meanwhile I got seamless page transitions running. It’s as easy as just defining an exit prop to the motion component. There are three awesome things happening with it.

  1. Automatically animate exits triggered from the server. This happens for example by a conditional render based on a prop. (e.g. @is_visible or something like this).
  2. Trigger exits manually by e.g. binding it. phx-click={LiveMotion.JS.hide(to: "#main-content").
  3. Automagically handle page transitions triggered by e.g. live_redirect.

Can’t wait until I can ship the first version of the library.

EDIT: If you like to see it live, you can test it on https://benvp.co (which is my little page :wink:)

Here is a quick demo:
https://cleanshot-cloud-fra.accelerator.net/media/26144/yvBCYQ2B1940j2e20mhHJO12vauM1cfJo5gJgRP0.mp4

benlime

benlime

Exactly, the animations fully happen on the client. The only thing which is sent over the wire are the animation props animate and transition. There is also a prop initial which allows you to set an initial state (think of enter animations where you need the state to be something else on initial mount).

This also works flawlessly when re-triggering animations. When you hammer the button in the video, it will just animate in the other direction from the exact point when you clicked at it. You can even do complex animations with multiple steps by just providing e.g. animate={[x: [0, 200, 100, 50, 100]}.

The function component itself currently is extremely simple. I’ll just paste it.

def motion(assigns) do
    rest = assigns_to_attributes(assigns, [:animate, :transition, :initial])

    initial =
      case assigns[:initial] do
        nil ->
          nil

        initial ->
          (initial || assigns.animate)
          |> LiveMotion.Style.create_styles()
          |> LiveMotion.Style.to_style_string()
      end

    assigns =
      assigns
      |> assign_new(:animate, fn -> [] end)
      |> assign_new(:transition, fn -> [] end)
      |> assign(:style, initial)
      |> assign(:rest, rest)

    ~H"""
    <div
      id={@id}
      phx-hook="Motion"
      data-motion={LiveMotion.animate(@animate, @transition)}
      style={@style}
      {@rest}
    >
      <%= render_slot(@inner_block) %>
    </div>
    """
  end

There is a phx-hook involved which utilities a tiny JS library (motion.dev) that performs animations using the browser WebAnimations API.

I took heavy inspiration in the API from framer-motion (Motion: JavaScript & React animation library). I really like their api design and the paradigm for declaring animations. Still, I have to implement a few features and figure out things, like:

  • Exit/Unmount animations (Presence).
  • Declaring animations for pure client side interactions. My idea here is currently a thin wrapper around LiveView.JS.
  • Dependant animations (layouts).
  • Scroll animations
  • Gestures, …

A lot of ideas, but I think in general most of it is somewhat possible.

benlime

benlime

I’m pretty amazed what you can do with LiveView. I just played around with declaring fully client-side interactions using a thin wrapper around LiveView.JS.

Toggle animations declared on the server, performed on the client without a round-trip. And you can even mix and match animations being updated from the server and the animations already running locally :exploding_head:.

When clicking the square, the server updates the LiveView state, therefore updating the animation (again, it just sends one single declaration, nothing more).
When clicking the “Toggle me” button, no round-trip is made.

Hammering both just smoothly animates. Feels magical to me.

https://cleanshot-cloud-fra.accelerator.net/media/26144/p5RbCZhU7yRjtyBAb6E9U0LCNjzeJgBvGjP4Tqc7.mp4

Where Next?

Popular in RFCs Top

simulacre7
Hi everyone, I’m building Aethrion, an early alpha Elixir runtime for persistent AI characters. Repo: GitHub - simulacre7/aethrion: A s...
New
senconscious
EctoJuno is a package that provides parsing, validation and applying sorting parameters for your ecto queries on models. Currently sortin...
New
bennydreamtech23
So recently I have been looking for ways to improve my workflow in elixir and I saw all the commands I need to run to setup a project and...
New
GenericJam
Plugin System for Mob Mob is growing by leaps and bounds! I realize now we will not be able to accommodate all the functionality people ...
New
borodark
Hey folks, I’m building Zed (not editor) a declarative BEAM deploy tool for FreeBSD and illumos that uses ZFS user properties as the stat...
New
ca1989
Hi all, this week I played a bit with Phoenix and Elm. I ended up with this POC: https://github.com/carlotm/elmex The repository cont...
New
tmbb
I’ve started working on a toy project to compile extended POSIX-compatible regular expressions into NimbleParsec combinators. These combn...
New
jarlah
Hi! I have recently created, after having tried to get in touch with the creator of excontainers for quite some time, a new library call...
New
bortzmeyer
Not a replacement for serious authoritative DNS servers, of course, but useful for some uses (such as returning the IP address of the DNS...
New
sodapopcan
EDIT: I forgot to link the repo, ha: GitHub - sodapopcan/vials: Tweak your mix generators · GitHub This project is not quite ready for p...
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New

We're in Beta

About us Mission Statement