F20DV-Lab3

F20DV Lab 3

← Lab 2 Lab 3 Lab 4 →

Open in GitHub Codespaces

Tutorial 9 Tutorial 10 Tutorial 11 Tutorial 12

Interactions


Progress

100%


Tutorial 9: D3 Selection and Events

Lab 3 - Tutorial 9

Exercise: Highlight and Tooltips

In this exercise, we added two simple interactions with our bar charts for basic interaction with the charts.

Firstly, we implemented a way to highlight the Bar Charts when the mouse cursor was hovered over the bars.

Secondly, we added a simple tooltip text that is displayed to show additional information about the bars when the cursor is hovered over the bars.

Code

barChart.js
export default class BarChart {

// ...

    #updateBars() {
        this.bars = this.bars
            .data(this.data, (d) => d[0])
            // ...
            .on("mouseover", (event, datum) => {
            // Highlight the bar on cursor hover
                d3.select(event.target)
                .classed("highlighted", true)
            })
            .on("mouseout", (event, datum) => {
                // Remove the highlight on cursor out
                d3.select(event.target)
                .classed("highlighted", false)
            });

        // Add tooltips
        // Adds a title element to all bars
        this.bars.selectAll("title")
            .data(d => [d])
            .join("title")
            .text(d => `${d[0]}: ${d[1]}`);
    }

// ...

}

Back To Top ↥

Tutorial 10: Linked Interactions

Lab 3 - Tutorial 10

Exercise: Linked Selection and Filters

Code

main.js
"use strict";

import BarChart from './visualizations/barChart_tut10.js';

/***** Exercise: Linked Selection and Filters *****/
let data = await d3.csv("data/movies_mock.csv", d => {
    return {
        year: +d.release_year,
        revenues: parseFloat(d.revenues),
        genre: d.genre
    }
});

let bar1 = new BarChart("#bar1", 800, 400, [10, 40, 65, 10]),
    bar2 = new BarChart("#bar2", 800, 400, [10, 40, 65, 10]),
    bar3 = new BarChart("#bar3", 800, 400, [10, 40, 65, 10]);

let sortYears = (a, b) => a[0] - b[0];
let yearRevenues = d3.flatRollup(data, v => d3.sum(v, d => d.revenues), d => d.year).sort(sortYears),
    yearCount = d3.flatRollup(data, v => v.length, d => d.year).sort(sortYears),
    genreCount = d3.flatRollup(data, v => v.length, d => d.genre);

bar1.setLabels("Year", "Total Revenues")
    .render(yearRevenues);
bar2.setLabels("Year", "Total Number of Releases")
    .render(yearCount);
bar3.setLabels("Genre", "Total Number of Releases")
    .render(genreCount);

let highlightYear = (e, d) => {
    let year = d[0];
    bar1.highlightBars([year]);
    bar2.highlightBars([year]);
}

let rmvHighlightYear = (e, d) => {
    bar1.highlightBars();
    bar2.highlightBars();
}

bar1.setBarHover(highlightYear).setBarOut(rmvHighlightYear);
bar2.setBarHover(highlightYear).setBarOut(rmvHighlightYear);

let filterGenre = (e, d) => {
    let genre = d[0];
    let filteredData = data.filter(d => d.genre === genre),
        yearRevenuesFiltered = d3.flatRollup(filteredData, v => d3.sum(v, d => d.revenues), d => d.year).sort(sortYears),
        yearCountFiltered = d3.flatRollup(filteredData, v => v.length, d => d.year).sort(sortYears);

    bar1.setLabels("Year", `Revenues: ${genre}`)
        .render(yearRevenuesFiltered);
    bar2.setLabels("Year", `Number of Releases: ${genre}`)
        .render(yearCountFiltered);
}

bar3.setBarClick(filterGenre);
barChart.js
export default class BarChart {
    // Attributes

    // ...

    // Add Object attributes for storing callback references
    barClick = () => {};
    barHover = () => {};
    barOut   = () => {};

    // ...

    #updateBars() {
        this.bars = this.bars
            // ...

        // ...

        this.#updateEvents();

        // ...
    }

    #updateEvents() {
        // Rebind these callbacks to events
        this.bars
            .on("mouseover", this.barHover)
            .on("mouseout", this.barOut)
            .on("click", (e, d) => {
                console.log(`Bar Clicked: ${d}`);
                this.barClick(e, d);
            });
    }

    // ...

    setBarClick(f = () => {}) {
        // Register new callback
        this.barClick = f;

        // Rebind callback to event
        this.#updateEvents();

        // Return this for chaining
        return this;
    }

    setBarHover(f = () => {}) {
        // Register new callback
        this.barHover = f;

        // Rebind callback to event
        this.#updateEvents();

        // Return this for chaining
        return this;
    }

    setBarOut(f = () => {}) {
        // Register new callback
        this.barOut = f;

        // Rebind callback to event
        this.#updateEvents();

        // Return this for chaining
        return this;
    }

    highlightBars(keys = []) {
        // Reset Highlight for all bars
        this.bars.classed("highlighted", false);

        // Filter bars and set new highlights
        this.bars.filter(d => keys.includes(d[0]))
            .classed("highlighted", true);

        return this; // to allow chaining
    }
}

Back To Top ↥

Tutorial 11: D3 Transitions

Lab 3 - Tutorial 11

Exercise: Let’s Make an Animated Bar Chart

Adds an animated element for the bar chart using D3 Transitions.

There is a visual feedback when bar3 is clicked, showing an animation in bar1 and bar2.

Code

barChart.js
export default class BarChart {
    // ...

    #updateBars() {
        // Bind and join rectangles to data
        this.bars = this.bars
        .data(this.data, (d) => d[0])
        .join(
            // Initial placement of new rectangles
            enter => enter.append("rect")
                    .attr("x", (d) => this.scaleX(d[0]))
                    .attr("y", (d) => this.scaleY(0)) // Aligned at Bottom
                    .attr("width", this.scaleX.bandwidth())
                    .attr("height", 0), // No height
            // Leave existing rectangles untouched
            update => update,
            exit => exit.transition().duration(300)
                    .attr("y", d => this.scaleY(0)) // Aligned at bottom
                    .attr("height", 0) // No Height
                    .remove() // Destroy rectangle when finished
        )
        .classed("bar", true);

        // Animate Placement and sizing (enter + update only)
        this.bars.transition().duration(500)
            .attr("x", (d) => this.scaleX(d[0]))
            .attr("y", (d) => this.scaleY(d[1]))
            .attr("width", this.scaleX.bandwidth())
            .attr("height", (d) => this.scaleY(0) - this.scaleY(d[1]));
    }
}

Back To Top ↥

Tutorial 12: Advanced Behaviours

Lab 3 - Tutorial 12

Exercise: D3 Behaviours Sandbox

Code

main.js



Back To Top ↥