The solution to the exercise (medium level) from my canvas tutorial where an SVG based scatterplot had to be turned into a canvas based one.
Built with blockbuilder.org
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: 'Roboto', sans-serif;
}
svg, canvas {
position: absolute;
top: 0;
left: 0;
}
.chart-title {
font-size: 16px;
text-anchor: middle;
letter-spacing: 1.3px;
}
.axis .title {
font-size: 12px;
font-weight: 500;
text-anchor: middle;
text-transform: uppercase;
letter-spacing: 1.3px;
fill: #6b6b6b;
}
.axis text {
font-family: 'Roboto', sans-serif;
font-size: 11px;
fill: #919191;
}
.axis path {
display: none;
}
.axis line {
stroke: #a5a5a5;
}
</style>
<!-- Google fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500" rel="stylesheet">
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
////////////// Create the SVG and canvas //////////////
let margin = {
top: 40,
right: 10,
bottom: 60,
left: 100
}
let width = 1000 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom
//Create the canvas
let canvas = d3.select("body")
.append("canvas")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
//Get the 2D context from the canvas element
let ctx = canvas.node().getContext("2d")
ctx.translate(margin.left, margin.top)
//Create the SVG container
let svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
////////////// 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()
})
////////////// Create axes //////////////
let x_axis = svg.append("g") //x scale - rating
.attr("class", "axis x")
.attr("transform", "translate(0 " + height + ")")
.call(d3.axisBottom(x_scale))
let y_axis = svg.append("g") //y scale - budget
.attr("class", "axis y")
.call(d3.axisLeft(y_scale)
.ticks(5)
.tickFormat(d3.format("$,.0s"))
)
////////////// Create titles //////////////
//Add chart title
svg.append("text")
.attr("class", "chart-title")
.attr("x", width / 2)
.attr("y", -10)
.text("comparing budgets versus ratings for nearly 5000 movies across the last 90 years")
//Add x title
x_axis.append("text")
.attr("class", "title")
.attr("x", width / 2)
.attr("y", 40)
.text("Rating")
//Add y title
y_axis.append("text")
.attr("class", "title")
.attr("x", 0)
.attr("y", 50)
.text("Budget")
})//d3.csv
</script>
</body>