Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sliders

Sliders provide interactive controls to animate through different data states or parameters. They are different from range sliders and are typically used for animating through time series data or changing plot parameters dynamically.

Data sliders support various customization options including:

  • Positioning: Control x, y coordinates and anchors
  • Styling: Background color, border color, width, and font
  • Behavior: Active step highlighting, step execution control
  • Dimensions: Length, width, and orientation (horizontal/vertical)
  • Steps: Multiple steps with different data states or parameters

Customizing a Simple Data Slider

Sliders are perfect for stepping through sequential data, like time series. This example creates a slider to show the population of different animals for four consecutive years. It shows how to use the slider API to customize slider features.

#![allow(unused)]
fn main() {
/// Display a bar chart with a data slider to animate through different years,
/// showing how to customize the slider.
fn bar_chart_with_slider_customization(show: bool, file_name: &str) {
    type BarType = Bar<&'static str, i32>;
    let mut plot = Plot::new();
    plot.add_trace(
        BarType::new(vec!["Giraffes", "Orangutans", "Monkeys"], vec![20, 14, 23])
            .name("2019")
            .visible(Visible::True),
    );
    plot.add_trace(
        BarType::new(vec!["Giraffes", "Orangutans", "Monkeys"], vec![25, 18, 28])
            .name("2020")
            .visible(Visible::False),
    );
    plot.add_trace(
        BarType::new(vec!["Giraffes", "Orangutans", "Monkeys"], vec![22, 16, 25])
            .name("2021")
            .visible(Visible::False),
    );
    plot.add_trace(
        BarType::new(vec!["Giraffes", "Orangutans", "Monkeys"], vec![28, 20, 30])
            .name("2022")
            .visible(Visible::False),
    );
    let slider_steps = vec![
        SliderStepBuilder::new()
            .label("2019")
            .value("2019")
            .push_restyle(BarType::modify_visible(vec![
                Visible::True,
                Visible::False,
                Visible::False,
                Visible::False,
            ]))
            .build()
            .unwrap(),
        SliderStepBuilder::new()
            .label("2020")
            .value("2020")
            .push_restyle(BarType::modify_visible(vec![
                Visible::False,
                Visible::True,
                Visible::False,
                Visible::False,
            ]))
            .build()
            .unwrap(),
        SliderStepBuilder::new()
            .label("2021")
            .value("2021")
            .push_restyle(BarType::modify_visible(vec![
                Visible::False,
                Visible::False,
                Visible::True,
                Visible::False,
            ]))
            .build()
            .unwrap(),
        SliderStepBuilder::new()
            .label("2022")
            .value("2022")
            .push_restyle(BarType::modify_visible(vec![
                Visible::False,
                Visible::False,
                Visible::False,
                Visible::True,
            ]))
            .build()
            .unwrap(),
    ];

    plot.set_layout(
        Layout::new()
            .title("Animal Population by Year (Custom Slider Fields)")
            .sliders(vec![Slider::new()
                .active(0)
                .steps(slider_steps)
                .x(0.2)
                .x_anchor(Anchor::Left)
                .y(-0.6)
                .y_anchor(Anchor::Bottom)
                .length(0.8)
                .background_color("#f8fafc")
                .border_color("#bec8d9")
                .border_width(2)
                .tick_color("#e74c3c")
                .tick_length(15)
                .tick_width(4)
                .current_value(
                    SliderCurrentValue::new()
                        .prefix("Year: ")
                        .suffix(" (selected)")
                        .visible(true)
                        .x_anchor(SliderCurrentValueXAnchor::Center)
                        .font(Font::new().size(16).color("#2c3e50"))
                        .offset(5),
                )]),
    );
    let path = write_example_to_html(&plot, file_name);
    if show {
        plot.show_html(path);
    }
}
}

Slider for Parameter Control

Sliders aren't just for data; they can also be used to control parameters in a function. Here, a slider modifies the frequency of a sinusoidal wave, updating the plot in real-time.

#![allow(unused)]
fn main() {
// Sinusoidal Wave with Slider Control
fn sinusoidal_slider_example(show: bool, file_name: &str) {
    use ndarray::Array;

    let mut plot = Plot::new();
    let num_steps = 51; // 0..=50 for frequencies 0.0 to 5.0 in 0.1 steps

    // Add traces, one for each slider step (frequency parameter)
    for step in 0..=num_steps {
        let frequency = step as f64 / 10.0; // 0.0, 0.1, ..., 5.0
        let x: Vec<f64> = Array::linspace(0.0, 10.0, 1001).into_raw_vec().to_vec();
        let y: Vec<f64> = x.iter().map(|&x_val| (frequency * x_val).sin()).collect();
        let trace = Scatter::new(x, y)
            .visible(if step == 10 {
                Visible::True
            } else {
                Visible::False
            }) // Make 10th trace visible
            .line(plotly::common::Line::new().color("#00CED1").width(6.0))
            .name(format!("ν = {frequency:.1}"));
        plot.add_trace(trace);
    }

    // Create slider steps
    let mut steps = Vec::new();
    for i in 0..num_steps {
        let frequency = i as f64 / 10.0;
        let mut visible = vec![Visible::False; num_steps];
        visible[i] = Visible::True;
        let step = SliderStepBuilder::new()
            .label(format!("step-{i}"))
            .value(format!("{frequency:.1}"))
            .push_restyle(Scatter::<f64, f64>::modify_visible(visible))
            .push_relayout(Layout::modify_title(format!(
                "Slider switched to step: {i}"
            )))
            .build()
            .unwrap();
        steps.push(step);
    }
    let layout = Layout::new()
        .title(Title::with_text("Simple Slider Control"))
        .sliders(vec![Slider::new()
            .active(10)
            .pad(Pad::new(50, 0, 0))
            .steps(steps)]);
    plot.set_layout(layout);
    let path = write_example_to_html(&plot, file_name);
    if show {
        plot.show_html(path);
    }
}
}

Advanced Slider: GDP vs. Life Expectancy

This example, based on the Python Plotly Gapminder dataset, demonstrates a more complex use case. A slider is used to animate a bubble chart showing the relationship between GDP per capita and life expectancy across different continents over several decades. See https://plotly.com/python/sliders/

#![allow(unused)]
fn main() {
// GDP per Capita/Life Expectancy Slider (matches second plot in https://plotly.com/python/sliders/)
fn gdp_life_expectancy_slider_example(show: bool, file_name: &str) {
    let data = load_gapminder_data();

    // Get unique years and sort them
    let years: Vec<i32> = data
        .iter()
        .map(|d| d.year)
        .collect::<std::collections::HashSet<_>>()
        .into_iter()
        .sorted()
        .collect();

    // Create color mapping for continents to match the Python plotly example
    let continent_colors = HashMap::from([
        ("Asia".to_string(), "rgb(99, 110, 250)"),
        ("Europe".to_string(), "rgb(239, 85, 59)"),
        ("Africa".to_string(), "rgb(0, 204, 150)"),
        ("Americas".to_string(), "rgb(171, 99, 250)"),
        ("Oceania".to_string(), "rgb(255, 161, 90)"),
    ]);
    let continents: Vec<String> = continent_colors.keys().cloned().sorted().collect();

    let mut plot = Plot::new();

    // Create a trace for each continent for each year
    for &year in &years {
        for continent in &continents {
            let records: Vec<&GapminderData> = data
                .iter()
                .filter(|d| d.continent == *continent && d.year == year)
                .collect();

            if !records.is_empty() {
                let x: Vec<f64> = records.iter().map(|r| r.gdp_per_cap).collect();
                let y: Vec<f64> = records.iter().map(|r| r.life_exp).collect();
                let size: Vec<f64> = records.iter().map(|r| r.pop).collect();
                let hover: Vec<String> = records.iter().map(|r| r.country.clone()).collect();

                let trace = Scatter::new(x, y)
                    .name(continent)
                    .mode(Mode::Markers)
                    .hover_text_array(hover)
                    .visible(if year == years[0] {
                        Visible::True
                    } else {
                        Visible::False
                    })
                    .marker(
                        plotly::common::Marker::new()
                            .color(*continent_colors.get(continent).unwrap())
                            .size_array(size.into_iter().map(|s| s as usize).collect())
                            .size_mode(plotly::common::SizeMode::Area)
                            .size_ref(200000)
                            .size_min(4),
                    );
                plot.add_trace(trace);
            }
        }
    }

    // Create slider steps for each year
    let steps: Vec<SliderStep> = years
        .iter()
        .enumerate()
        .map(|(i, &year)| {
            let mut visible = vec![Visible::False; years.len() * continents.len()];
            let start = i * continents.len();
            let end = start + continents.len();
            visible[start..end].fill(Visible::True);

            SliderStepBuilder::new()
                .label(year.to_string())
                .value(year)
                .push_restyle(Scatter::<f64, f64>::modify_visible(visible))
                .push_relayout(Layout::modify_title(format!(
                    "GDP vs. Life Expectancy ({year})"
                )))
                .build()
                .unwrap()
        })
        .collect();

    let layout = Layout::new()
        .title(Title::with_text(format!(
            "GDP vs. Life Expectancy ({})",
            years[0]
        )))
        .x_axis(
            Axis::new()
                .title(Title::with_text("gdpPercap"))
                .type_(plotly::layout::AxisType::Log),
        )
        .y_axis(
            Axis::new()
                .title(Title::with_text("lifeExp"))
                .range(vec![30.0, 85.0]), // Fixed range for Life Expectancy
        )
        .sliders(vec![Slider::new().active(0).steps(steps).current_value(
            SliderCurrentValue::new()
                .visible(true)
                .prefix("Year: ")
                .x_anchor(SliderCurrentValueXAnchor::Right)
                .font(Font::new().size(20).color("rgb(102, 102, 102)")),
        )]);
    plot.set_layout(layout);
    let path = write_example_to_html(&plot, file_name);
    if show {
        plot.show_html(path);
    }
}
}