Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib raxol playground demos easing_demo.ex
Raw

lib/raxol/playground/demos/easing_demo.ex

defmodule Raxol.Playground.Demos.EasingDemo do
@moduledoc "Playground demo: animated easing function showcase."
use Raxol.Core.Runtime.Application
alias Raxol.Animation.Easing
@easings [
:linear,
:ease_in_quad,
:ease_out_quad,
:ease_in_out_cubic,
:ease_in_elastic,
:ease_out_bounce,
:ease_in_out_back,
:ease_in_expo
]
@plot_width 30
@plot_height 10
@cycle_ticks 40
@tick_interval_ms 50
@impl true
def init(_context) do
%{
easing_index: 0,
progress: 0.0,
tick: 0
}
end
@impl true
def update(message, model) do
case message do
key_match(:up) ->
{select_easing(model, -1), []}
key_match(:down) ->
{select_easing(model, 1), []}
key_match(:left) ->
{select_easing(model, -1), []}
key_match(:right) ->
{select_easing(model, 1), []}
key_match("r") ->
{%{model | progress: 0.0, tick: 0}, []}
:tick ->
{advance_tick(model), []}
_ ->
{model, []}
end
end
@impl true
def view(model) do
easing = current_easing(model)
label = easing |> to_string() |> String.replace("_", " ")
eased = Easing.calculate_value(easing, model.progress)
marker_x = round(model.progress * @plot_width)
marker_y = round(eased * @plot_height)
column style: %{gap: 0} do
[
text("Easing Demo", style: [:bold]),
text("Function: #{label}", fg: :cyan),
text(
"Progress: #{Float.round(model.progress, 2)} Value: #{Float.round(eased, 2)}",
style: [:dim]
),
text(""),
render_plot(easing, marker_x, marker_y),
render_marker_bar(marker_x),
text(""),
easing_list(model.easing_index),
text("[arrows] change [r] reset", style: [:dim])
]
end
end
@impl true
def subscribe(_model) do
[subscribe_interval(@tick_interval_ms, :tick)]
end
defp select_easing(model, delta) do
max_idx = length(@easings) - 1
idx = Raxol.Core.Utils.Math.clamp(model.easing_index + delta, 0, max_idx)
%{model | easing_index: idx, progress: 0.0, tick: 0}
end
defp advance_tick(model) do
new_tick = model.tick + 1
progress = rem(new_tick, @cycle_ticks) / @cycle_ticks
%{model | tick: new_tick, progress: progress}
end
defp current_easing(model) do
Enum.at(@easings, model.easing_index)
end
defp render_plot(easing, marker_x, _marker_y) do
lines =
for row <- @plot_height..0//-1 do
for col <- 0..@plot_width do
plot_char(easing, col, row, marker_x)
end
|> Enum.join()
end
column style: %{gap: 0} do
Enum.map(lines, fn line -> text(line, fg: :green) end)
end
end
defp plot_char(easing, col, row, marker_x) do
val = Easing.calculate_value(easing, col / @plot_width)
plot_y = Float.round(val * @plot_height, 0) |> trunc()
cond do
col == marker_x and plot_y == row -> "@"
plot_y == row and within_range?(val) -> "*"
row == 0 -> "-"
col == 0 -> "|"
true -> " "
end
end
defp render_marker_bar(marker_x) do
bar =
for x <- 0..@plot_width do
if x == marker_x, do: "^", else: "-"
end
text(Enum.join(bar), fg: :yellow)
end
defp within_range?(val), do: val >= 0.0 and val <= 1.0
defp easing_list(active_idx) do
items =
@easings
|> Enum.with_index()
|> Enum.map(fn {easing, idx} ->
label = easing |> to_string() |> String.replace("_", " ")
if idx == active_idx do
text(" > #{label}", style: [:bold], fg: :cyan)
else
text(" #{label}", style: [:dim])
end
end)
column style: %{gap: 0} do
items
end
end
end