web-dev-qa-db-fra.com

Taille de l'arrière-plan de la simulation: couverture en toile

Je dessine des images sur toile comme ceci:

ctx.drawImage(data[i].image, data[i].pos.x, data[i].pos.y, data[i].pos.w, data[i].pos.h);

le fait est que l'image est étirée et je ne veux pas ça. Comment puis-je simuler la propriété CSS

background-size: cover

tout en dessinant une image dans des cavas.

http://www.w3schools.com/cssref/playit.asp?filename=playcss_background-size&preval=cover

voir la différence entre 100% 100% (ce que j'ai actuellement) et cover (mon objectif).

33
Deepsy

Il est un peu plus compliqué d'obtenir une fonctionnalité de couverture, mais voici une solution pour cela:

Démo

Mise à jour du 2016-04-03 pour traiter des cas spéciaux. Voir également le commentaire de @ Yousef ci-dessous.

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

Maintenant, vous pouvez l'appeler comme ceci:

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

et il redimensionnera l’image proportionnellement pour qu’il tienne à l’intérieur de ce conteneur.

Utilisez les deux derniers paramètres pour décaler l'image:

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

J'espère que cela t'aides!

81
user1693593

Si vous cherchez une solution plus simple qui fonctionnera dans la plupart des cas, et qui inclut également des fonctionnalités de type css contain, essayez ceci:

function fit(contains) {
  return (parentWidth, parentHeight, childWidth, childHeight, scale = 1, offsetX = 0.5, offsetY = 0.5) => {
    const childRatio = childWidth / childHeight
    const parentRatio = parentWidth / parentHeight
    let width = parentWidth * scale
    let height = parentHeight * scale

    if (contains ? (childRatio > parentRatio) : (childRatio < parentRatio)) {
      height = width / childRatio
    } else {
      width = height * childRatio
    }

    return {
      width,
      height,
      offsetX: (parentWidth - width) * offsetX,
      offsetY: (parentHeight - height) * offsetY
    }
  }
}

export const contain = fit(true)
export const cover = fit(false)

version légèrement modifiée de échelle intrinsèque pour inclure l'échelle et le décalage

Usage:

import {cover, contain} from './intrinsic-scale'

const {
  offsetX, 
  offsetY, 
  width, 
  height
} = cover(parentWidth, parentHeight, imageWidth, imageHeight)

// or...

const {
  offsetX, 
  offsetY, 
  width, 
  height
} = contain(parentWidth, parentHeight, imageWidth, imageHeight)

ctx.drawImage(image, offsetX, offsetY, width, height)
3
daviestar