block by nbremer 6177a48b75a47c745707efae5e60d6f4

Canvas tutorial - Normal level - Solution

Full Screen

The solution to the exercise from my canvas tutorial where an SVG based scatterplot had to be turned into a canvas based one.

Built with blockbuilder.org

index.html

<!DOCTYPE html>

<head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
    <script>
        ////////////// Create the canvas //////////////
      
        let width = 1000,
            height = 800

        //Create the canvas
        let canvas = d3.select("body")
            .append("canvas")
            .attr("width", width)
            .attr("height", height)

        //Get the 2D context from the canvas element
        let ctx = canvas.node().getContext("2d")

        ////////////// Create scales //////////////

        const x_scale = d3.scaleLinear() //rating
            .range([0, width])

        const y_scale = d3.scaleLog() //budget
            .domain([500, 500e6]) //in dollars
            .range([height, 0])

        const r_scale = d3.scaleSqrt() //profit ratio = revenue / budget
            .domain([0, 6])
            .range([0, 10])

        //Make the movies that have the highest profit ratio less visible
        //to balance their increased size
        const opacity_scale = d3.scaleLinear() //profit ratio = revenue / budget
            .domain([0, 100])
            .range([0.2, 0.01])
            .clamp(true)

        //A linear color scale to give an idea about when a movie was made
        const color_scale = d3.scaleSequential(d3.interpolateViridis) //release year

        ////////////// Read in the data //////////////

        //Data based on dataset from 
        //https://data.world/popculture/imdb-5000-movie-dataset
        d3.csv("imdb-movies.csv", function (error, data) {
            if (error) throw error

            ////////////// Final data prep //////////////

            //Make the number columns actually numeric
            data.forEach(d => {
                d.release_year = +d.release_year
                d.budget = +d.budget
                d.revenue = +d.revenue
                d.rating = +d.rating
                d.num_voted_users = +d.num_voted_users
                d.profit_ratio = +d.profit_ratio
            })

            ////////////// Adjust scales //////////////

            //Set the range for the scales now that the data is read in
            x_scale.domain(d3.extent(data, d => d.rating)).nice()
            color_scale.domain(d3.extent(data, d => d.release_year))

            ////////////// Create circles //////////////

            data.forEach(d => {
                //Set the color and opacity
                ctx.fillStyle = color_scale(d.release_year)
                ctx.globalAlpha = opacity_scale(d.profit_ratio)

                //Draw the circle
                ctx.beginPath()
                ctx.arc(x_scale(d.rating), y_scale(d.budget), r_scale(d.profit_ratio), 0, 2*Math.PI)
                ctx.closePath()
                ctx.fill()
            })

        })//d3.csv

    </script>
</body>