Skip to content

Repository files navigation

Enguia

Hex.pm Docs

Declarative animations for Phoenix LiveView, powered by the Web Animations API.

Enguia lets you add smooth, performant animations to your LiveView templates using a clean Elixir DSL. No CSS files. No JavaScript boilerplate. Just functions.

<.motion animate={slide_up(delay: 100)} tag="section">
  <h1>Slides in when scrolled into view</h1>
</.motion>

<.motion animate={typewriter()} tag="p">
  Hello, world!
</.motion>

Features

  • 11 motion presets — fade, slide, scale, shake, pulse, bounce
  • 4 text effects — typewriter, split words, blur in, letter spacing in
  • 4 trigger modesmount, visible (scroll), hover, click
  • Scroll reveal — re-animate every time an element enters the viewport
  • Fully composable — override duration, delay, easing, fill, repeat per call
  • ~3 KB JavaScript hook, zero runtime dependencies

Installation

Add enguia to your mix.exs:

def deps do
  [
    {:enguia, "~> 0.1"}
  ]
end

JavaScript Setup

In assets/js/app.js, import and register the hook:

import EnguiaHook from "enguia"

let liveSocket = new LiveSocket("/live", Socket, {
  hooks: { EnguiaHook }
})

The import works via the NODE_PATH that Phoenix already configures pointing to deps/. No extra setup needed.

Usage

Import everything with use Enguia in your LiveView or component module:

defmodule MyAppWeb.PageLive do
  use MyAppWeb, :live_view
  use Enguia

  def render(assigns) do
    ~H"""
    <.motion animate={fade_in()}>
      <p>Fades in on mount</p>
    </.motion>

    <.motion animate={slide_up(delay: 100)} tag="section" class="hero">
      <h1>Slides up when scrolled into view</h1>
    </.motion>

    <.motion animate={bounce()} tag="button">
      Bouncing button
    </.motion>
    """
  end
end

Or import selectively:

import Enguia.Components
import Enguia.Presets

Motion Presets

All presets accept an optional keyword list to override defaults.

Function Default trigger Description
fade_in/1 :mount Fade from 0 to 1 opacity
fade_out/1 :mount Fade from 1 to 0 opacity
slide_up/1 :visible Slide up from below
slide_down/1 :visible Slide down from above
slide_left/1 :visible Slide in from the left
slide_right/1 :visible Slide in from the right
scale_in/1 :mount Scale from 80% to 100%
scale_out/1 :mount Scale from 100% to 80%
shake/1 :mount Horizontal shake (attention grabber)
pulse/1 :mount Infinite opacity pulse
bounce/1 :mount Infinite vertical bounce
fade_in(duration: 500, delay: 100, easing: "ease-out")
slide_up(trigger: :click, repeat: 2)
pulse(repeat: :infinity)   # default for pulse

Text Effects

Import from Enguia.TextAnimations (included via use Enguia):

Function Description
typewriter/1 Reveals text character by character
split_words/1 Animates each word in with a stagger
blur_in/1 Fades text in from blurred to sharp
letter_spacing_in/1 Animates from wide letter-spacing to normal
<.motion animate={typewriter(duration: 1500)} tag="p">
  One character at a time.
</.motion>

<.motion animate={split_words(stagger: 60)} tag="h2">
  One word at a time.
</.motion>

<.motion animate={blur_in()} tag="h1">
  Fades in from blur.
</.motion>

<.motion animate={letter_spacing_in()} tag="h1">
  Tracks in from wide spacing.
</.motion>

Triggers

Trigger When it fires
:mount Immediately when the component mounts
:visible When the element enters the viewport (IntersectionObserver)
:hover On mouse enter
:click On click

Scroll Reveal

By default, :visible animations fire once. Pass scroll_reveal: true to re-animate every time the element enters the viewport:

<.motion animate={slide_up(scroll_reveal: true)}>
  Animates each time you scroll past it.
</.motion>

Options Reference

All presets accept these keyword options:

Option Type Description
duration integer Duration in milliseconds
delay integer Delay before starting, in milliseconds
easing string Any CSS easing value ("ease", "ease-out", "linear", etc.)
fill string Fill mode: "forwards", "backwards", "both", "none"
trigger atom :mount, :visible, :hover, or :click
repeat integer or :infinity Number of iterations
scroll_reveal boolean Re-animate on each viewport entry (:visible only)

Custom Animations

Build animations from scratch using %Enguia.Animation{}:

alias Enguia.Animation

anim = %Animation{
  keyframes: [
    %{"transform" => "rotate(0deg)"},
    %{"transform" => "rotate(360deg)"}
  ],
  duration: 1000,
  easing: "linear",
  repeat: :infinity,
  trigger: :mount
}

<.motion animate={anim}>...</.motion>

The <.motion> Component

Attribute Type Default Description
animate Animation.t() required The animation struct
tag string "div" HTML tag to render
class string nil CSS class
id string auto-generated Element ID

Any other attributes are passed through to the element.

License

MIT — see LICENSE for details.

About

motion lib based in elixir

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages