Skip to content

Instantly share code, notes, and snippets.

@cmgiven
Last active August 20, 2016 02:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cmgiven/f2100df55e076f386c13ada4988b75e9 to your computer and use it in GitHub Desktop.
Save cmgiven/f2100df55e076f386c13ada4988b75e9 to your computer and use it in GitHub Desktop.
Retina Canvas
license: mit

Block-a-Day #13. A quick demonstration of scaling a canvas for high-DPI (retina) displays.

What I Learned: I had forgotten that when setting width and height as styles with D3, you need to add the "px" or else it won't take.

What I'd Do With More Time: I was considering creating an option to simulate the effect for non-high-DPI displays, but it wasn't worth the effort, so if viewed on a 1x display, the same image will be shown on both sides.

Block-a-Day

Just what it sounds like. For fifteen days, I will make a D3.js v4 block every single day. Rules:

  1. Ideas over implementation. Do something novel, don't sweat the details.
  2. No more than two hours can be spent on coding (give or take).
  3. Every. Single. Day.

Previously

<!DOCTYPE html>
<meta charset="utf-8">
<style>
div { float: left; position: relative; }
#left { border-right: 1px solid #ccc; }
#right { border-left: 1px solid #ccc; }
span {
position: absolute;
top: 11px; left: 11px;
font-family: sans-serif;
font-size: 11px;
font-weight: 700;
}
</style>
<body>
<div id="left">
<span>Non-scaled canvas</span>
</div>
<div id="right">
<span>DPI-scaled canvas</span>
</div>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var width = 958
var height = 500
var canvases = [
canvas('#left', [width / 2, height]),
canvas('#right', [width / 2, height], [width / 2, 0]).retinaScaled()
]
function canvas(sel, size, offset) {
offset = offset || [0, 0]
var width = size[0]
var height = size[1]
var canvas = d3.select(sel).append('canvas')
.attr('width', width)
.attr('height', height)
var ctx = canvas.node().getContext('2d')
function draw(node) {
ctx.clearRect(0, 0, width, height)
var x = node.x - offset[0]
var y = node.y - offset[1]
if (x + node.width < 0 || x >= width || y + node.height < 0 || y >= height) {
return
}
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x += node.width, y)
ctx.lineTo(x, y += node.height)
ctx.lineTo(x -= node.width, y)
ctx.lineTo(x, y -= node.height)
ctx.strokeStyle = '#f00'
ctx.stroke()
ctx.font = node.height * 0.75 + 'px Georgia, serif'
ctx.textAlign = 'center'
ctx.strokeText('A', x + node.width / 2, y + node.height * 0.75)
}
draw.retinaScaled = function () {
if (!window.devicePixelRatio) { return }
canvas
.attr('width', width * window.devicePixelRatio)
.attr('height', height * window.devicePixelRatio)
.style('width', width + 'px')
.style('height', height + 'px')
ctx.scale(window.devicePixelRatio, window.devicePixelRatio)
return this
}
return draw
}
var size = 420
var velocity = 2
var angle = 0
var node = {
x: width / 2 - size / 2,
y: height / 2 - size / 2,
vx: velocity * Math.cos(angle * Math.PI / 180),
vy: velocity * Math.sin(angle * Math.PI / 180),
width: size,
height: size
}
var boxForce = boundedBox()
.bounds([[0, 0], [width, height]])
.size(function (d) { return [d.width, d.height] })
d3.forceSimulation()
.velocityDecay(0)
.alphaTarget(1)
.on('tick', ticked)
.force('box', boxForce)
.nodes([node])
function ticked() {
canvases.forEach(function (c) { c(node) })
}
function boundedBox() {
var nodes
var bounds
var size
var sizes
function force() {
var node
var size
var i = -1
while (++i < nodes.length) {
node = nodes[i]
size = sizes[i]
if (node.x + node.vx < bounds[0][0] || node.x + node.vx + size[0] > bounds[1][0]) {
node.x += node.vx
node.vx = -node.vx
}
if (node.y + node.vy < bounds[0][1] || node.y + node.vy + size[1] > bounds[1][1]) {
node.y += node.vy
node.vy = -node.vy
}
}
}
force.initialize = function (_) {
sizes = (nodes = _).map(size)
}
force.bounds = function (_) {
return (arguments.length ? (bounds = _, force) : bounds)
}
force.size = function (_) {
return (arguments.length
? (size = typeof _ === 'function' ? _ : constant(_), force)
: size)
}
return force
}
function constant(_) {
return function () { return _ }
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment