Powered by AppSignal & Oban Pro

Multiple Trace Filled-Area Animation

10_multiple_trace_filled_area.livemd

Multiple Trace Filled-Area Animation

Mix.install([
  {:plotly_ex, "~> 0.3"},
  {:kino, "~> 0.18"}
])

Section

Animate multiple filled area traces simultaneously. Each frame's data list has one entry per trace (index-matched). Using stackgroup: "g1" stacks the areas.

alias Plotly.{Figure, Scatter}

x = Enum.to_list(0..30)
n_frames = 30

frames =
  for i <- 1..n_frames do
    phase = i * 0.25
    y1 = Enum.map(x, fn xi -> abs(:math.sin(xi * 0.4 + phase)) * 3 + 1 end)
    y2 = Enum.map(x, fn xi -> abs(:math.cos(xi * 0.3 + phase)) * 2 + 1 end)
    y3 = Enum.map(x, fn xi -> abs(:math.sin(xi * 0.5 + phase * 1.5)) * 1.5 + 0.5 end)
    %{name: "f#{i}", data: [%{y: y1}, %{y: y2}, %{y: y3}]}
  end

mk_trace = fn init_y, name, color ->
  Scatter.new(
    x: x, y: init_y, mode: "lines", name: name,
    fill: "tonexty", fillcolor: "rgba(#{color}, 0.4)",
    line: %{color: "rgba(#{color}, 0.9)"},
    stackgroup: "g1"
  )
end

Figure.new(
  data: [
    mk_trace.(Enum.map(x, fn xi -> abs(:math.sin(xi * 0.4)) * 3 + 1 end), "Series A", "100, 149, 237"),
    mk_trace.(Enum.map(x, fn xi -> abs(:math.cos(xi * 0.3)) * 2 + 1 end), "Series B", "255, 127, 80"),
    mk_trace.(Enum.map(x, fn xi -> abs(:math.sin(xi * 0.5)) * 1.5 + 0.5 end), "Series C", "60, 179, 113")
  ],
  layout: %{
    title: "Multiple Trace Stacked Filled-Area Animation",
    xaxis: %{range: [0, 30], autorange: false},
    yaxis: %{autorange: false, range: [0, 15]},
    updatemenus: [%{
      type: "buttons",
      showactive: false,
      buttons: [%{
        label: "▶ Play",
        method: "animate",
        args: [nil, %{frame: %{duration: 80, redraw: false}, transition: %{duration: 40}}]
      }]
    }]
  },
  frames: frames
)
|> Plotly.show()