+ - 0:00:00
Notes for current slide
Notes for next slide

d3 and canvas

Kai Chang
bl.ocks.org/syntagmatic

1 / 40

Pixels
and
Subpixels

2 / 40
3 / 40
4 / 40
5 / 40
A programming language is like a natural, human language in that it favors certain metaphors, images, and ways of thinking.
Seymour Papert
7 / 40

Canvas

  • Used for drawing graphics
  • Pixel-based (bitmap of rgba image data)
  • Procedural
  • Immediate Mode

JavaScript Core Skills

  • Number, String, Boolean
  • Array [❤️,❤️,❤️]
  • Object
  • Function
  • map, filter, forEach
8 / 40
I really really like array
I like mapping them
I like reducing them
 
But I like to do that on a physical scale so i like mapping and reducing the array that's on the needle opening
 
I have a whole bunch of research about how knitting is coding

Mariko Kosaka

9 / 40

A Blank Canvas

We need a canvas element to draw on:

<canvas id='painting' width=960 height=500></canvas>

And a canvas context to render to:

<script type='text/javascript'>
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
// make art with pixels here
</script>
10 / 40

Filling Rects

<script type='text/javascript'>
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
context.fillStyle = "mediumseagreen"
context.fillRect(500,20,200,600) // x, y, width, height
context.fillStyle = "darkorchid"
context.fillRect(200,200,600,60)
context.fillStyle = "deepskyblue"
context.fillRect(400,180,60,300)
</script>
11 / 40

Filling Circles

context.fillStyle = "crimson"
context.beginPath()
context.arc(800,250,450,0,2*Math.PI)
context.fill()
context.fillStyle = "steelblue"
context.beginPath()
context.arc(50,250,50,0,2*Math.PI)
context.fill()
12 / 40

Creating words

function circle(x,y,radius,color) {
context.fillStyle = color
context.beginPath()
context.arc(x,y,radius,0,2*Math.PI)
context.fill()
}
13 / 40

Filling Circles

circle(800,250,450,"crimson")
circle(800,250,450,"steelblue")
14 / 40

Exploring Lab Color Space

15 / 40

Exploring Lab Color Space

Pick a color in Lab space

var l = 65
var a = -30
var b = 0
ctx.fillStyle = d3.lab(l,a,b)
ctx.fillRect(0, 0, canvas.width, canvas.height)
16 / 40

Exploring Lab Color Space

1-d Gradient

d3.range(0, canvas.width).forEach(function(x) {
var l = 65
var a = -100+200*x/canvas.width
var b = 0
ctx.fillStyle = d3.lab(l,a,b)
ctx.fillRect(x, 0, 1, canvas.height)
})
17 / 40

Exploring Lab Color Space

2-d Section

d3.range(0, canvas.width).forEach(function(x) {
d3.range(0, canvas.height).forEach(function(y) {
var l = 65
var a = -100+200*x/canvas.width
var b = -100+200*y/canvas.height
ctx.fillStyle = d3.lab(l,a,b)
ctx.fillRect(x, y, 1, 1)
})
})
18 / 40

Exploring Lab Color Space

Trip out with d3.timer

d3.timer(function(elapsed) {
d3.range(0, canvas.width).forEach(function(x) {
var l = 65;
var a = -100+200*x/canvas.width
var b = 100*Math.sin(elapsed/400)
ctx.fillStyle = d3.lab(l,a,b)
ctx.fillRect(x, 0, 1, canvas.height)
})
})
19 / 40

Science Satellites

d3.timer(function(elapsed) {
// run time at 151x speed
var time = new Date(now.getTime() + 150*elapsed)
data.forEach(function(d) {
plotSatellite(d, time)
})
})
function plotSatellite(d, time) {
var position = satellite.propagate(sat, time).position
var groundPosition = satellite.geodetic(position, time)
drawSat(sat, groundPosition)
}
20 / 40

Exoplanets Parallel Coordinates

21 / 40

Progressive Rendering

Once you put something down, it doesn't cost anything to keep it around.

There is no memory cost for rendering to canvas.

Render chunks of data incrementally to keep interactions snappy.

22 / 40

Particle Trails

Clearing the canvas by filling a transparent rect can be used to create a whimsical trail effect

d3.timer(function() {
context.fillStyle = 'rgba(255,255,255,0.3)';
context.fillRect(0, 0, canvas.width, canvas.height);
// redraw all the things!
})
23 / 40

Source: Developer Roadmap

24 / 40

SVG Challenges to Learning

  • Inconsistences between SVG/HTML
  • Confusion between attributes, styles and CSS
  • Path strings have their own little language
  • Managing a DOM hierarchy
  • D3 General Update pattern involves complex method chaining
    • Type of returned object shifts during pattern
    • .selectAll().data()enter()append()merge()
    • Selections saved to variables may be stale
  • Slow with large number of elements (1000+)

... but there are a ton of tutorials and examples!

25 / 40
If we have a setback or a problem, or if we behave in a way that we determine is inappropriate or immoral, we may feel stupid, embarrassed, or unlovable.

In these cases self-awareness may become burdensome. And even if nothing particularly bad is happening at the moment, self-awareness may still feel unpleasant because we have fears about what might happen to us or about mistakes that we might make in the future.

//open.lib.umn.edu/intropsyc/chapter/5-3-altering-consciousness-without-drugs/
26 / 40

Canvas Challenges

  • Drawn objects are not retained
  • Interactions with visual marks don't come for free
  • Canvas usually needs to be entirely re-rendered, even for minor changes
  • Lack of beginner examples and tutorials with D3
  • No accessibility fallback for visually impaired users
  • Looks fuzzy by default on high-density displays

... but it's an easy-to-use API that's all JavaScript!

27 / 40

28 / 40

CDC WONDER

29 / 40

Choropleths

var path = d3.geoPath()
.projection(d3.geoAlbersUsa())
.context(context);
context.strokeStyle = "#fff";
context.lineWidth = 0.3;
countGeojson.forEach(function(d) {
context.fillStyle = color(d["Crude Rate"]);
context.beginPath();
path(d);
context.fill();
context.stroke();
});
30 / 40

Retina Support

var devicePixelRatio = window.devicePixelRatio || 1
var canvas = d3.select("body").append("canvas")
.attr("width", width * devicePixelRatio)
.attr("height", height * devicePixelRatio)
.style("width", width + "px")
.style("height", height + "px").node()
var context = canvas.getContext("2d")
context.scale(devicePixelRatio, devicePixelRatio)
31 / 40

Hidden Canvas Lookups

Using getImageData to lookup data using rgb values off a second canvas.
See Yannick's blog post

32 / 40

Quadtree

  • Subdivides data in two spatial dimensions
  • Commonly used to find closest point
  • Can create the same interaction surface as Voronoi
33 / 40

d3.forceSimulation

Get the closest graph node to a position (such as the cursor):

simulation.find(d3.event.x, d3.event.y)

34 / 40

d3.path

Makes available the following canvas methods and can generate SVG path strings.

  • moveTo
  • lineTo
  • closePath
  • quadraticCurveTo
  • BezierCurveTo
  • arcTo
  • arc
  • rect
  • toString
35 / 40

Shapes

With V4, these are built on d3.path. So they support both Canvas and SVG.

  • Arcs
  • Pies
  • Lines
  • Areas
  • Curves
  • Symbols
  • Stacks
36 / 40
At age 5 children are learning to read and learning to write. They can input code and read it off the screen. And they can reason. They have the faculty of reasoning.
Mary Rose Cook
37 / 40
38 / 40
39 / 40
40 / 40

Pixels
and
Subpixels

2 / 40
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow