index.html
<!DOCTYPE html>
<html>
<head>
<title>canvas</title>
<meta charset="utf-8">
<style>
body {
font-family: 'Futura';
font-weight: normal;
color: #080808;
font-size: 20px;
}
h1, h2, h3, h4 {
font-family: 'Futura';
font-weight: normal;
}
h2 {
font-size: 30px !important;;
}
.bottom h2 {
margin-bottom: 0px;
}
.remark-code, .remark-code-line {
font-family: 'Ubuntu Mono', Monaco, Menlo, monospace;
}
.remark-slide-content {
background-size: cover;
padding: 1em 3em !important;
}
.inverse {
background-color: #1c1e19;
color: #f4f3f2;
}
.inverse h1 {
text-shadow: 0 0 8px #555;
}
.inverse h1, .inverse h2 {
color: #f4f3f2;
}
a {
color: #762a83;
text-decoration: none;
}
code.remark-code {
background: rgba(255,255,255,0.9) !important;
border-radius: 3px;
font-size: 20px;
}
blockquote {
font-size: 1.6em;
margin: 1em 0;
}
.hljs-attr {
color: #e6550d !important;
}
.remark-slide-number {
font-size: 13px;
}
.nobg {
background: transparent;
}
/* Two-column layout */
.left-column {
color: #777;
width: 20%;
height: 92%;
float: left;
font-size: 18px;
}
.right-column {
width: 76%;
float: right;
}
</style>
</head>
<body>
<textarea id="source">
class: center, middle
# canvas
<span style="color: #777;">an intro to web graphics with html5</span>
Kai Chang<br/>
<a href="//bl.ocks.org/syntagmatic">bl.ocks.org/syntagmatic</a><br/>
---
class: bottom
background-image: url(julia.png)
## <a href="//bl.ocks.org/syntagmatic/3736720/">Julia Set</a>
---
class: bottom
<a href="https://archive.nytimes.com/www.nytimes.com/interactive/2012/11/11/sunday-review/counties-moving.html"><img src="nyt-wind.gif"/ width="90%" ></a>
---
## Exoplanets
<a href="https://bl.ocks.org/syntagmatic/482706e0638c67836d94b20f0cb37122/"><img src="exoplanets.png"/ width="110%" style="margin-left: -7%;"></a>
Extrasolar planets, their discovery method and characteristics in parallel coordinates.
Data from the NASA Exoplanet Archive.
---
background-image: url(monitor-small.jpg)
---
background-image: url(monitor-big.png)
---
background-image: url(monitor-big-dark.png)
class: middle, inverse
<blockquote>The electric light is pure information.<br/>It is a medium without a message, as it were, unless it is used to spell out some verbal ad or name.
</blockquote>
Marshall McLuhan
---
background-image: url(eye-cells.jpg)
background-size: contain
---
class: bottom, right
background-image: url(eye-brain.jpg)
background-size: contain
<a href="//open.lib.umn.edu/intropsyc/">open.lib.umn.edu/intropsyc</a>
---
class: middle, inverse
<blockquote>The canvas is the door to another dimension.<br/>The paintbrush is the key.
</blockquote>
Luhraw
---
class: middle, inverse
<blockquote>The <span style="color: #00E218;">HTML5</span> canvas is the door to another dimension.<br/><span style="text-decoration: line-through; color: #888;">The paintbrush</span> <span style="color: #00E218;">Javascript</span> is the key.
</blockquote>
Kai
---
## Canvas
* Used for drawing graphics in the browser
* Pixel-based (bitmap of rgba image data)
* Procedural, immediate Mode
* This talk is about the 2D Context
* There is also a 3D WebGL Context
## JavaScript
* Call methods on the Canvas context
* Load, parse, draw data
* Data structures: Arrays, Objects, Strings, Numbers
* Functions for data transformation, drawing subroutines
## Applications
* Generative art
* Data visualization
* Web-based games
* Drawing and image editing apps
---
## Can I use?
<a href="https://caniuse.com/#feat=canvas"><img src="caniuse.png" width=800/></a>
---
.left-column[
## A Blank Canvas
]
.right-column[
We need a canvas element to draw on:
```html
<canvas id='painting' width=960 height=500></canvas>
```
And a canvas context to render to:
```html
<script type='text/javascript'>
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
</script>
```
]
---
background-image: url(rects.png)
.left-column[
## Rectangles
Rect arguments:
* x
* y
* width
* height
]
.right-column[
```html
<script type='text/javascript'>
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
context.fillStyle = "mediumseagreen"
context.fillRect(500,20,200,600)
context.fillStyle = "darkorchid"
context.fillRect(200,200,600,60)
context.fillStyle = "deepskyblue"
context.fillRect(400,180,60,300)
</script>
```
]
---
.left-column[
## Lines
Move a cursor around with moveTo and lineTo
]
.right-column[
```javascript
context.strokeStyle = "#f5b"
context.lineWidth = 3
context.beginPath()
context.moveTo(150,250)
context.lineTo(200,100)
context.lineTo(250,175)
context.lineTo(300,100)
context.lineTo(350,250)
context.stroke()
```
<img src="lines.png" width=200 />
]
---
.left-column[
## Lines
Close shapes with closePath()
]
.right-column[
```javascript
context.strokeStyle = "#5738FF"
context.lineWidth = 3
context.beginPath()
context.moveTo(150,250)
context.lineTo(200,100)
context.lineTo(250,175)
context.lineTo(300,100)
context.lineTo(350,250)
context.closePath()
context.stroke()
```
<img src="lines-closed.png" width=200 />
]
---
.left-column[
## Shapes
The same as lines, just using fill instead of stroke
]
.right-column[
```javascript
context.fillStyle = "#15673D"
context.beginPath()
context.moveTo(150,250)
context.lineTo(200,100)
context.lineTo(250,175)
context.lineTo(300,100)
context.lineTo(350,250)
context.closePath()
context.fill()
```
<img src="lines-filled.png" width=200 />
]
---
background-image: url(circs.png)
.left-column[
## Circles
Arc arguments:
* x
* y
* r
* start angle
* end angle
]
.right-column[
```javascript
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()
```
]
---
.left-column[
## Creating words
]
.right-column[
```javascript
function circle(x,y,radius,color) {
context.fillStyle = color
context.beginPath()
context.arc(x,y,radius,0,2*Math.PI)
context.fill()
}
```
]
---
background-image: url(circs.png)
.left-column[
## Creating words
Filling circles (again)
]
.right-column[
```javascript
circle(800,250,450,"crimson")
circle(800,250,450,"steelblue")
function circle(x,y,radius,color) {
context.fillStyle = color
context.beginPath()
context.arc(x,y,radius,0,2*Math.PI)
context.fill()
}
```
]
---
.left-column[
## Animation
<img src="canvas-animation.gif" width=140 />
]
.right-column[
```html
<script src="https://d3js.org/d3.v4.js"></script>
<script>
d3.timer(function() {
})
</script>
```
]
---
.left-column[
## Animation
<img src="canvas-animation.gif" width=140 />
]
.right-column[
```javascript
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
// center the animation
context.translate(canvas.width/2, canvas.height/2)
d3.timer(function(t) {
var x = Math.cos(t/2000*Math.PI) * 120
var y = Math.sin(t/2000*Math.PI) * 120
var color = "hsl(" + (Math.floor(t/30)%360) + ",80%,60%)"
circle(x,y,40,color)
})
function circle(x,y,radius,color) {
context.fillStyle = color
context.beginPath()
context.arc(x,y,radius,0,2*Math.PI)
context.fill()
}
```
]
---
.left-column[
## Animation
Use clearRect to erase previously drawn areas of the canvas
<img src="canvas-animation-2.gif" width=140 />
]
.right-column[
```javascript
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
// center the animation
context.translate(canvas.width/2, canvas.height/2)
d3.timer(function(t) {
context.clearRect(-canvas.width/2,
-canvas.height/2,
canvas.width,
canvas.height)
var x = Math.cos(t/2000*Math.PI) * 120
var y = Math.sin(t/2000*Math.PI) * 120
var color = "hsl(" + (Math.floor(t/30)%360) + ",80%,60%)"
circle(x,y,40,color)
})
function circle(x,y,radius,color) {
context.fillStyle = color
context.beginPath()
context.arc(x,y,radius,0,2*Math.PI)
context.fill()
}
```
]
---
background-image: url(boids.gif)
.left-column[
## Particle Trails
Clearing the canvas by filling a transparent rect can be used to create a whimsical trail effect
]
.right-column[
```javascript
d3.timer(function() {
context.fillStyle = 'rgba(255,255,255,0.3)'
context.fillRect(0, 0, canvas.width, canvas.height)
// redraw all the things!
})
```
]
---
.left-column[
## That's right, Particle Trails!
And torus wrapping!
]
.right-column[
<a href="trails.html"><img src="trails.gif" width=360 /></a>
<a href="trails-neon.html"><img src="trails-neon.gif" width=360 /></a>
<a href="compositing.html"><img src="bubbles.gif" width=360 /></a>
<a href="compositing-2.html"><img src="bubbles-dark.gif" width=360 /></a>
]
---
.left-column[
## Bar Chart
<div style="height:17px"> </div>
Set up the canvas
<div style="height:36px"> </div>
Make some data
<div style="height:15px"> </div>
Color pink
<div style="height:15px"> </div>
Draw the bars
<div style="height:38px"> </div>
These "magic numbers" shape and scale the chart
<img src="barchart.png" width=100 />
]
.right-column[
```html
<canvas id='painting' width=100 height=500></canvas>
<script>
var canvas = document.getElementById('painting')
var context = canvas.getContext('2d')
var data = [1, 1, 2, 3, 5, 8, 13, 21, 34]
context.fillStyle = "#f5b"
data.forEach(drawBar)
function drawBar(d,i) {
var x = 10*i
var y = 190
var width = 8
var height = -5*d
context.fillRect(x,y,width,height)
}
</script>
```
]
---
class: center
.left-column[
## Compositing
Changes the way colors combine when overlapping shapes are rendered.
]
.right-column[
<img src="source-over.gif" width=130/>
```javascript
context.globalCompositeOperation = "source-over" // default
```
<img src="multiply.gif" width=130/>
```javascript
context.globalCompositeOperation = "multiply"
```
<img src="screen.gif" width=130/>
```javascript
context.globalCompositeOperation = "screen"
```
]
---
class: bottom
background-image: url(infant-mortality.png)
background-size: contain
<a href="//bl.ocks.org/syntagmatic/623a3221d3e694f85967d83082fd4a77">SVG version</a> ·
<a href="//bl.ocks.org/syntagmatic/cb9a7441f79908c630207383b194a701">Canvas version</a>
---
background-image: url(choro.png)
background-size: contain
## Mapmaking
```javascript
var projection = d3.geoAlbersUsa()
var path = d3.geoPath()
.projection(projection)
.context(context)
var color = d3.scaleThreshold()
.range(["#fde0dd", "#fcc5c0",
"#fa9fb5", "#f768a1",
"#dd3497", "#ae017e",
"#7a0177","#49006a"])
.domain([250, 500, 750, 1000, 1250, 1500, 1750, 2000])
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()
})
```
---
background-image: url(science-sats.gif)
# Science Satellites
```javascript
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)
}
```
Note: this bit is pseudo-code.
<a href="https://bl.ocks.org/syntagmatic/e494e2c3d7c797e41e48838f005f731f">Click here for a live version</a>.
---
## Places to Learn More
<a href="https://www.w3schools.com/html/html5_canvas.asp">w3Schools Canvas Tutorial</a>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API">MDN Canvas API</a>
<a href="https://flaviocopes.com/canvas/">The HTML5 Canvas Guide</a>
<a href="https://joshondesign.com/p/books/canvasdeepdive/toc.html">Josh on Design Canvas Deep Dive</a>
<a href="https://www.visualcinnamon.com/2015/11/learnings-from-a-d3-js-addict-on-starting-with-canvas.html">Learnings from a d3.js addict starting with canvas</a>
---
class: center, middle
## Thank you Øredev
Now go draw some rects!
</textarea>
<script src="remark-latest.min.js">
</script>
<script>
var slideshow = remark.create({
highlightStyle: 'color-brewer',
ratio: "16:10"
});
</script>
<script src="https://d3js.org/d3.v4.js"></script>
</body>
</html>