Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scaling a canvas nicely with css

Tags:

html

css

canvas

I'm trying to draw an image on a canvas, then use css to fit the canvas within a certain size. It turns out that many browsers don't scale the canvas down very nicely. Firefox on OS X seems to be one of the worst, but I haven't tested very many. Here is a minimal example of the problem:

HTML

<img>
<canvas></canvas>

CSS

img, canvas {
  width: 125px;
}

JS

var image = document.getElementsByTagName('img')[0],
    canvas = document.getElementsByTagName('canvas')[0];

image.onload = function() {
  canvas.width = image.width;
  canvas.height = image.height;

  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
}

image.src = "http://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Helvetica_Neue_typeface_weights.svg/783px-Helvetica_Neue_typeface_weights.svg.png"

Running in a codepen: http://codepen.io/ford/pen/GgMzJd

Here's the result in Firefox (screenshot from a retina display):

canvas_scaling_firefox.png

What's happening is that both the <img> and <canvas> start at the same size and are scaled down by the browser with css (the image width is 783px). Apparently, the browser does some nice smoothing/interpolation on the <img>, but not on the <canvas>.

I've tried:

  • image-rendering, but the defaults seem to already be what I want.
  • Hacky solutions like scaling the image down in steps, but this didn't help: http://codepen.io/ford/pen/emGxrd.
  • Context2D.imageSmoothingEnabled, but once again, the defaults describe what I want.

How can I make the image on the right look like the image on the left? Preferably in as little code as possible (I'd rather not implement bicubic interpolation myself, for example).

like image 551
ford Avatar asked Feb 04 '15 16:02

ford


People also ask

Can you style canvas with CSS?

Because the canvas is an HTML element, you can use CSS styles to modify its position, assign it a background color or image, add a border, and so on. In Safari and other WebKit-based browsers, you can use WebKit transitions to smoothly animate changes in CSS properties.

How do you scale a canvas size?

With the Select and Move Tool, click the canvas size label or border to select the canvas. Click the canvas border handles to resize the canvas dynamically. When you drag the border handles without using any keyboard modifiers, the canvas resizes non-uniformly. Hold Shift to constrain the resize proportions.

How do you scale in CSS?

The scale property in CSS resizes an element's width and height in proportion. So, if we have an element that's 100 pixels square, scaling it up by a value of 2 doubles the dimensions to 200 pixels square. Similarly, a scale value of . 5 decreases the dimensions in half, resulting in 50 pixels square.


2 Answers

You can fix the pixelation issue by scaling the canvas's backing store by the window.devicePixelRatio value. Unfortunately, the shoddy image filtering seems to be a browser limitation at this time, and the only reliable fix is to roll your own.

Replace your current onload with:

image.onload = function() {
  var dpr = window.devicePixelRatio;
  canvas.width = image.width * dpr;
  canvas.height = image.height * dpr;

  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
}

Results:

Results

Tested on Firefox 35.0.1 on Windows 8.1. Note that your current code doesn't handle browser zoom events, which could reintroduce pixelation. You can fix this by handling the resize event.

like image 112
mm201 Avatar answered Sep 21 '22 13:09

mm201


Canvas is not quite meant to be css zoomed : Try over-sampling : use twice the required canvas size, and css scaling will do a fine job in down-scaling the canvas.
On hi-dpi devices you should double yet another time the resolution to reach the same quality.

(even on a standard display, X4 shines a bit more).

Image, canvas 1X, 2X and 4X (Image, canvas 1X, 2X and 4X)


var $ = document.getElementById.bind(document);
var image = $('fntimg');

image.onload = function() {
  drawAllImages();
}

image.src = "http://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Helvetica_Neue_typeface_weights.svg/783px-Helvetica_Neue_typeface_weights.svg.png"

function drawAllImages() {
  drawImage(1);
  drawImage(2);
  drawImage(4);
}

function drawImage(x) {
  console.log('cv' + x + 'X');
  var canvas = $('cv' + x + 'X');
  canvas.width = x * image.width;
  canvas.height = x * image.height;
  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
}
img,
canvas {
  width: 125px;
}
<br>
<img id='fntimg'>
<canvas  id='cv1X'></canvas>
<canvas  id='cv2X'></canvas>
<canvas  id='cv4X'></canvas>
<br>
like image 38
GameAlchemist Avatar answered Sep 25 '22 13:09

GameAlchemist