Powered by AppSignal & Oban Pro

Add a Play Button to Control a Slider

11_play_button_control_slider.livemd

Add a Play Button to Control a Slider

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

Section

A Play button (method: "animate") combined with layout.sliders gives users both manual scrubbing (drag the slider) and automatic playback (click Play). This is the same pattern as notebooks/animations/08_animating_with_slider.livemd — see that notebook for the full slider animation reference.

Here we show the minimal pattern: Play/Pause buttons + a year slider.

alias Plotly.{Figure, Scatter}

years = 2010..2023
n = Enum.count(years)

:rand.seed(:exsplus, {7, 8, 9})
# Simulated annual temperature anomaly
base_anomaly = 0.5
temps = Enum.map(years, fn y -> base_anomaly + (y - 2010) * 0.025 + (:rand.normal() * 0.15) end)

frames =
  Enum.with_index(years, fn year, i ->
    %{
      name: "#{year}",
      data: [%{
        x: Enum.slice(Enum.to_list(years), 0, i + 1),
        y: Enum.slice(temps, 0, i + 1)
      }],
      layout: %{title: %{text: "Temperature Anomaly through #{year}"}}
    }
  end)

slider_steps =
  Enum.map(years, fn year ->
    %{
      label: "#{year}",
      method: "animate",
      args: [["#{year}"], %{mode: "immediate", frame: %{duration: 400}, transition: %{duration: 200}}]
    }
  end)

Figure.new(
  data: [Scatter.new(
    x: [Enum.at(Enum.to_list(years), 0)],
    y: [Enum.at(temps, 0)],
    mode: "lines+markers",
    line: %{color: "tomato", width: 2},
    marker: %{size: 8}
  )],
  layout: %{
    title: "Temperature Anomaly through 2010",
    xaxis: %{range: [2009, 2024], autorange: false, title: "Year"},
    yaxis: %{range: [0.2, 1.2], autorange: false, title: "°C anomaly"},
    updatemenus: [%{
      type: "buttons",
      showactive: false,
      y: 0,
      x: 0.5,
      xanchor: "center",
      yanchor: "top",
      pad: %{t: 60},
      buttons: [
        %{label: "▶ Play", method: "animate",
          args: [nil, %{frame: %{duration: 500}, transition: %{duration: 200}, fromcurrent: true}]},
        %{label: "⏸ Pause", method: "animate",
          args: [[nil], %{mode: "immediate", frame: %{duration: 0}}]}
      ]
    }],
    sliders: [%{
      steps: slider_steps,
      active: 0,
      currentvalue: %{prefix: "Year: ", visible: true, xanchor: "center"},
      pad: %{t: 50},
      x: 0.05,
      len: 0.9
    }]
  },
  frames: frames
)
|> Plotly.show()