1

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

So far my image changes depending on the screen resolution, but only after refreshing the page.

enter image description here

How can I get the following scaling effect without refreshing a page? Try to resize the window out there.

HTML

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

JS

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

canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);

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);
}

/**
 * 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

2 Answers2

2

Add a call to draw on the window resize event:

window.onresize = draw;

That should do the trick.

Instead of:

drawImageProp(ctx, this, 0, 0, canvas.width, canvas.height);

Pass img in the draw function:

drawImageProp(ctx, img, 0, 0, canvas.width, canvas.height);

Then, move the width / heigth settings into the draw function, resulting in:

function draw() {
    canvas.setAttribute('width', window.innerWidth);
    canvas.setAttribute('height', window.innerHeight);
    drawImageProp(ctx, img, 0, 0, canvas.width, canvas.height);
}

You may also want to debounce the re-drawing a bit:

var timeOut;
window.onresize = function(){
    if(timeOut)
        clearTimeout(timeOut);
    timeOut = setTimeout(draw, 10);
}

This prevents the draw function from being called a couple of times per second while the window is being resized.

Here's a working example, forked from your codepen.

Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • Should be noted that scaling on canvas doesn't work. What is drawn on a canvas can't be manipulated like an . The image should be redrawn on the resized canvas. – kemicofa ghost Oct 27 '15 at 13:09
  • @Grimbode: I think I've got that covered now. – Cerbrus Oct 27 '15 at 13:17
  • Is it best practice to clean the canvas and then redraw or is redrawing good enough if it's previous drawing isn't visible anymore? What does this do with performance? – Matthijs van Hest Oct 27 '15 at 13:19
  • Setting the width / height on a canvas already clears the canvas. And setting those attributes is something that has to be done. Redrawing a image on a canvas once is relatively insignificant. – Cerbrus Oct 27 '15 at 13:23
1

Well this works.

http://codepen.io/anon/pen/KdRwVY

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

canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);

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

function draw() {
  drawImageProp(ctx, img, 0, 0, canvas.width, canvas.height);
}
window.onresize = resize;

function resize() {
  canvas.setAttribute('width', window.innerWidth);
  canvas.setAttribute('height', window.innerHeight);
  draw()
}

/**
 * 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);
}
<canvas id="canvas"></canvas>
Roger
  • 3,226
  • 1
  • 22
  • 38
  • I think you do need to keep the onload there, because you can resize the window while the image might still not be loaded.. – Matthijs van Hest Oct 27 '15 at 13:20
  • This will continuously resize (And thus, re-draw) the canvas while you're scaling the window. That's quite inefficient. – Cerbrus Oct 27 '15 at 13:58
  • @Cerbrus This option much faster, nonetheless. – Billy Logan Oct 27 '15 at 14:02
  • If the speed is a problem, you can lower the delay in my answer from `500` to, say, `10`, or maybe even lower. Then the canvas will re-draw (seemingly) immediately, without actually re-drawing 70 times over the course of the window being resized. – Cerbrus Oct 27 '15 at 14:12