5

I'm trying to display the image using cover simulation in canvas. I've found some cool answer on how to do it.

The thing is when I do it with a large picture, it's being displayed ugly. How to fix that?

Here's my Codepen

HTML

<canvas id="canvas"></canvas>

CSS

    canvas {
      width: 100%;
      height: 100vh;
    }

JS

var ctx = canvas.getContext('2d'),
    img = new Image;

img.onload = draw;
img.src = 'https://upload.wikimedia.org/wikipedia/commons/0/0f/2010-02-19_3000x2000_chicago_skyline.jpg';

function draw() {
    drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height);
    //drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height, 0.5, 0.5);
}

/**
 * By Ken Fyrstenberg
 *
 * drawImageProp(context, image [, x, y, width, height [,offsetX, offsetY]])
 *
 * If image and context are only arguments rectangle will equal canvas
*/
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {

    if (arguments.length === 2) {
        x = y = 0;
        w = ctx.canvas.width;
        h = ctx.canvas.height;
    }

    /// default offset is center
    offsetX = offsetX ? offsetX : 0.5;
    offsetY = offsetY ? offsetY : 0.5;

    /// keep bounds [0.0, 1.0]
    if (offsetX < 0) offsetX = 0;
    if (offsetY < 0) offsetY = 0;
    if (offsetX > 1) offsetX = 1;
    if (offsetY > 1) offsetY = 1;

    var iw = img.width,
        ih = img.height,
        r = Math.min(w / iw, h / ih),
        nw = iw * r,   /// new prop. width
        nh = ih * r,   /// new prop. height
        cx, cy, cw, ch, ar = 1;

    /// decide which gap to fill    
    if (nw < w) ar = w / nw;
    if (nh < h) ar = h / nh;
    nw *= ar;
    nh *= ar;

    /// calc source rectangle
    cw = iw / (nw / w);
    ch = ih / (nh / h);

    cx = (iw - cw) * offsetX;
    cy = (ih - ch) * offsetY;

    /// make sure source rectangle is valid
    if (cx < 0) cx = 0;
    if (cy < 0) cy = 0;
    if (cw > iw) cw = iw;
    if (ch > ih) ch = ih;

    /// fill image in dest. rectangle
    ctx.drawImage(img, cx, cy, cw, ch,  x, y, w, h);
}
Community
  • 1
  • 1
Billy Logan
  • 2,470
  • 6
  • 27
  • 45
  • In the first palce, what's the reason you're wanting to display an image in canvas and not just with basic html/css? Like: `background-size: cover;` Examples of this can be found [here](https://css-tricks.com/perfect-full-page-background-image/) and much more.. – Matthijs van Hest Oct 27 '15 at 10:57

2 Answers2

3

To accomplish this you could use several techniques like HTML/CSS, CSS Only, Jquery or JS/Canvas. For more on this look here.

You do not have to set the width and height of your canvas in HTML like David Skx mentioned. You do have to erase your CSS, remove it completely.

In your JS you should set your canvas size (just define it in 1 place, don't let different languages interfere):

var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth; 
canvas.height = window.innerHeight;

Window means the whole browser, Use pixels here if you want to limit it to just a canvas and not the whole background

That's all.

Community
  • 1
  • 1
Matthijs van Hest
  • 769
  • 1
  • 6
  • 12
  • It doesn't have to resize itself proportionally. – Billy Logan Oct 27 '15 at 11:25
  • just paste `canvas.width = window.innerWidth; canvas.height = window.innerHeight;` on line 3 of your JS and you're done, unless you want a specificly sized canvas,, like 500×900 you use `canvas.width = 500; canvas.height = 900;` And don't forget to remove your css.. – Matthijs van Hest Oct 27 '15 at 11:28
2

You have to specify the width and height in pixels directly on the <canvas>-Element, else it will distort it:

<canvas id="canvas" width="500" height="500"></canvas>

Use JavaScript to measure the window width and height and set it dynamically. Something like:

var canvas = document.getElementById('canvas');
canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);

UPDATE: As Matthijs van Hest pointed out, the width and height attributes on the <canvas>-element are just optional.

misantronic
  • 1,132
  • 2
  • 10
  • 23
  • 1
    You don't have to specify width and height, it can only help preventing the browser view from tripping that split second from creating the DOM to executing the JS. Further more; your JS should be shorter and more readable like in my answer.. keep it neath – Matthijs van Hest Oct 27 '15 at 11:09
  • Of course your right. I just wanted to point out that if a width and height property is used, it has to be absolute pixels. – misantronic Oct 27 '15 at 11:17
  • Well, your thing did 'work', but you just didn't mention it all, and your code could be improved, that's why I made my own answer to the question. And however it is good to point out things, never forget that the main goal is to answer a question <3 – Matthijs van Hest Oct 27 '15 at 11:25