Simulation Background-Size: Cover in Canvas

Simulation background-size: cover in canvas

It's a bit more complicated to get a cover functionality, though here is one solution for this:

Updated 2016-04-03 to address special cases. Also see @Yousef's comment below.

/**
* By Ken Fyrstenberg Nilsen
*
* 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 = typeof offsetX === "number" ? offsetX : 0.5;
offsetY = typeof offsetY === "number" ? 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 (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated
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);
}

Now you can call it like this:

drawImageProp(ctx, image, 0, 0, width, height);

and it will scale the image proportionally to fit inside in that container.

Use the two last parameters to offset the image:

var offsetX = 0.5;   // center x
var offsetY = 0.5; // center y
drawImageProp(ctx, image, 0, 0, width, height, offsetX, offsetY);

Hope this helps!

Adjust canvas image size like 'background-size: cover' and responsive

The canvas width / height attributes do not reflect it's actual size in relation to it being sized with CSS. Given your code, cw/ch are fixed at 300/150. So the whole calculation is based on incorrect values.

You need to use values that actually reflect it's visible size. Like clientWidth / clientHeight.

A very simple solution is to update the canvas width/height attributes before using them for any calculations. E.g.:

canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;

Full example:

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');

// only load the image once
const img = new Promise(r => {
const img = new Image();

img.src = 'https://source.unsplash.com/WLUHO9A_xik/1600x900';
img.onload = () => r(img);
});

const draw = async () => {
// resize the canvas to match it's visible size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;

const loaded = await img;
const iw = loaded.width;
const ih = loaded.height;
const cw = canvas.width;
const ch = canvas.height;
const f = Math.max(cw/iw, ch/ih);

ctx.setTransform(
/* scale x */ f,
/* skew x */ 0,
/* skew y */ 0,
/* scale y */ f,
/* translate x */ (cw - f * iw) / 2,
/* translate y */ (ch - f * ih) / 2,
);

ctx.drawImage(loaded, 0, 0);
};

window.addEventListener('load', draw);
window.addEventListener('resize', draw);
.--dimensions {
width: 90%;
height: 300px;
}

.--border {
border: 3px solid #333;
}

canvas {
display: block;
margin: 0 auto;
}

#test {
background: url(https://source.unsplash.com/WLUHO9A_xik/1600x900) no-repeat center center;
background-size: cover;
margin: 1rem auto 0;
}
<canvas id="canvas" class="--dimensions --border"></canvas>
<div id="test" class="--dimensions --border"></div>

Getting ugly image while simulating cover in canvas

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.

Scaling an image to fit on canvas

Provide the source image (img) size as the first rectangle:

ctx.drawImage(img, 0, 0, img.width,    img.height,     // source rectangle
0, 0, canvas.width, canvas.height); // destination rectangle

The second rectangle will be the destination size (what source rectangle will be scaled to).

Update 2016/6: For aspect ratio and positioning (ala CSS' "cover" method), check out:

Simulation background-size: cover in canvas

Make canvas stretch to fit image

In order to get dynamically your canvas dimensions you could have the following componentWillReceiveProps() method:

componentWillReceiveProps(nextProps) {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();

img.onload = function () {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
img.src = URL.createObjectURL(nextProps.image)
}

How to crop images when placing them on an HTML5 canvas?

Your nested for loops execute img.onload and img.src = 'https://source.unsplash.com/random'; 25 times.

let ii = 0;
for (let xCell = 0; xCell < 5; xCell++) {
for (let yCell = 0; yCell < 5; yCell++) {
console.log(++ii + ": ", xCell, yCell);
}
}

Display the Full Image Size (100% Width/Height) inside a HTML5 Canvas of a certain Width/Height using JavaScript, maybe CSS

var canvas = document.getElementById('my_canvas');  var ctx = canvas.getContext('2d');  var imageObj = new Image();  ctx.canvas.width = window.innerWidth;  ctx.canvas.height = window.innerHeight;  imageObj.onload = function() {    ctx.drawImage(imageObj, 0, 0,window.innerWidth,window.innerHeight);  };  imageObj.src = 'https://www.google.com/images/srpr/logo4w.png';
<canvas id="my_canvas" style="border:2px solid;"></canvas>

background-size:cover equivalent

Here is a solution but similar to @Moob.

div{
width: 100%;
height: 200px;
position: relative;
overflow: hidden;
}
img {
display: block;
width: 100%;
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
}

Working jsfiddle



Related Topics



Leave a reply



Submit