Cause
Certaines images sont tout simplement très difficile de sous-échantillonnage et d' interpolation comme celui-ci avec des courbes lorsque vous voulez aller de grande taille pour une petite.
Les navigateurs semblent généralement l'utilisation de bi-linéaire (2x2 échantillonnage) interpolation avec la toile élément non bi-cubique (4x4 d'échantillonnage) pour (probablement) des raisons de performances.
Si l'étape est trop énorme, alors il n'y a simplement pas assez de pixels pour l'échantillon de ce qui se reflète dans le résultat.
À partir d'un signal/DSP point de vue on pourrait voir cela comme un filtre passe-bas de la valeur de seuil trop élevé, ce qui peut entraîner aliasing si il y a beaucoup de hautes fréquences (les détails) dans le signal.
Solution
La solution est d'utiliser l'étape-vers le bas pour obtenir un bon résultat. Étape-vers le bas signifie que vous réduire la taille des mesures pour permettre l'limité d'interpolation de gamme pour couvrir suffisamment de pixels pour l'échantillonnage.
Cela permettra à de bons résultats également avec la bi-interpolation linéaire (en fait, il se comporte un peu comme bi-cubique lors de cette opération) et la charge est minime, car il y a moins de pixels de l'échantillon dans chaque étape.
L'idéal est d'aller à la moitié de la résolution , à chaque étape, jusqu'à ce que vous définissez la taille de la cible (merci à Joe Mabel de mentionner cela!).
MODIFIÉ VIOLON
L'aide directe de la mise à l'échelle comme dans la question d'origine:
L'aide de l'étape vers le bas, comme indiqué ci-dessous:
Dans ce cas, vous aurez besoin à l'étape vers le bas, en 3 étapes:
Dans l'étape 1, nous avons réduit l'image à moitié en utilisant un écran de toile:
/// step 1 - create off-screen canvas
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = img.width * 0.5;
oc.height = img.height * 0.5;
octx.drawImage(img, 0, 0, oc.width, oc.height);
Étape 2 réutilise le hors de l'écran en toile et dessine l'image réduite de moitié à nouveau:
/// step 2
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
Et nous attirons une fois de plus à main de toile, de nouveau réduit à la moitié , mais à la taille finale:
/// step 3
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
Astuce:
Vous pouvez calculer le nombre total d'étapes, à l'aide de cette formule (il comprend l'étape finale pour définir la taille de la cible):
steps = Math.ceil(Math.log(sourceWidth / targetWidth) / Math.log(2))