Two ways to get 1px wide sharp and crisp lines for retina displays:
- Scale the canvas to twice the size (
context.scale(2,2);) and then draw a 1px wide line like so:
- Use all device pixels and draw the image twice the size. This is the approach I focus on in this blog post.
Here is how to draw super sharp lines on an HTML5 canvas for retina displays:
Step 1: Work with all available device pixels
height attributes must match the actual available device pixels (
window.devicePixelRatio). If you want to draw a full width line on an iPhone, it has to be 750 pixels long and not only 375.
But the CSS style of the canvas is only half of that. Check the JsFiddle below to see how I set up the canvas, its style attributes and its container element.
Step 2: Offset path coordinates by 0.5 pixels
context.beginPath(); context.moveTo(x1 + 0.5, y1 + 0.5); context.lineTo(x2 + 0.5, y2 + 0.5); context.closePath();
It makes sense to wrap your drawing tasks in a small function like
drawLine which automatically converts coordinates to half-pixels.
<div id="container" style="border: 1px dotted red;"> </div> <script> let container = document.getElementById("container"), canvas = document.createElement("canvas"), context = canvas.getContext("2d"); // round pixelRatio, because older devices have a pixelRatio of 1.5. Treat them as @2x devices let pixelRatio = Math.round(window.devicePixelRatio) || 1; container.appendChild(canvas); // we're working with all available pixels, i. e. 750x1334 on an iPhone 7 let width = 375 * pixelRatio; let height = 667 * pixelRatio; // here's the magic: the actual canvas has the same pixels as the device (iPhone 7 in this case) canvas.width = width; canvas.height = height; // however, the canvas dimensions are set to the pixels we're working with in the browser. canvas.style.width = Math.round(width / pixelRatio) + "px"; canvas.style.height = Math.round(height / pixelRatio) + "px"; // also, the container should have the same size as the canvas container.style.width = canvas.style.width; container.style.height = canvas.style.height; // now let's draw stuff context.clearRect(0, 0, width, height); context.save(); let x1 = 10, y1 = 10, x2 = width - 10, y2 = 10; // an actual 1px hairline context.lineWidth = 1; context.strokeStyle = "#444"; // place the path // Important: always offset by half a pixel to make lines super crisp context.beginPath(); context.moveTo(x1 + 0.5, y1 + 0.5); context.lineTo(x2 + 0.5, y2 + 0.5); context.closePath(); context.stroke(); context.restore(); </script>