Bagaimana dan mengapa lebar/tinggi kanvas mengacaukan gambar gambar saya?

Saya mencoba menulis fungsi yang mengambil koordinat dan rotasi x,y, dan menyisipkan gambar yang diputar di sekitar titik pusat gambar ke dalam kanvas. Kode ini berfungsi dengan baik:

let img = new Image(),
	canvas = document.getElementById('canvas'),
	ctx = canvas.getContext('2d'),
	step = 0, 
	drawImage = (ctx, img, x, y, degrees,  w = 150, h = 150) => {
		ctx.save();
		ctx.translate(x+w/4, y+h/4);
		ctx.rotate(degrees*Math.PI/180.0);
		ctx.translate(-x-w/4, -y-h/4);
		ctx.drawImage(img, x, y, w, h);
		ctx.restore();
	},
	animate = () => {
		ctx.globalCompositeOperation = 'destination-over';
		// clear canvas
		ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); 

		drawImage(ctx, img, 0, 	0, step)
	

		step++;
		window.requestAnimationFrame(animate);
	}


img.src = "http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg";

animate();
* { margin:0; padding:0; } /* to remove the top and left whitespace */
	html, body { width:100%; height:100%; } /* just to be sure these are full screen*/
	canvas { display:block;} /* To remove the scrollbars */
<canvas id="canvas" width="300" height="300">

Namun ketika saya mencoba mengubah ukuran kanvas

canvas.width =window.innerWidth;
canvas.height = window.innerHeight;

Ini membengkokkan gambar dan juga mengubah titik rotasi, efeknya lebih jelas pada jendela yang lebih besar. Bagaimana saya bisa memiliki kanvas yang memenuhi layar dan memperlakukannya seolah-olah saya mengatur ukurannya dengan properti HTML?

let img = new Image(),
	canvas = document.getElementById('canvas'),
	ctx = canvas.getContext('2d'),
	step = 0, 
	drawImage = (ctx, img, x, y, degrees,  w = 300, h = 300) => {
		ctx.save();
		ctx.translate(x+w/4, y+h/4);
		ctx.rotate(degrees*Math.PI/180.0);
		ctx.translate(-x-w/4, -y-h/4);
		ctx.drawImage(img, x, y, w, h);
		ctx.restore();
	},
	animate = () => {
		ctx.globalCompositeOperation = 'destination-over';
		// clear canvas
		ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); 

		drawImage(ctx, img, 0, 	0, step)
	

		step++;
		window.requestAnimationFrame(animate);
	}


img.src = "http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg";
canvas.width =window.innerWidth;
canvas.height = window.innerHeight;

animate();
	* { margin:0; padding:0; } /* to remove the top and left whitespace */
	html, body { width:100%; height:100%; } /* just to be sure these are full screen*/
	canvas { display:block;} /* To remove the scrollbars */
<canvas id="canvas" width="300" height="300">


person Himmators    schedule 04.09.2019    source sumber


Jawaban (2)


Itu adalah "bug" Chrome, terkait dengan gambar svg buruk yang Anda minta untuk digambar.

SVG yang Anda gambar tidak memiliki atribut width atau height absolut, atau atribut viewBox.

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect width="150" height="150" fill="rgb(0, 255, 0)" stroke-width="1" stroke="rgb(0, 0, 0)"/>
</svg>

Artinya, ia tidak memiliki ukuran intrinsik atau rasio intrinsik apa pun. Dalam konteks HTML, browser masih dapat menyetel ukuran default, dengan menerapkan aturan yang dijelaskan dalam Elemen sebaris yang diganti.

Dengan mengikuti aturan ini, <img> yang menyimpan svg Anda dalam wadah berukuran relatif akan memiliki ukuran yang dihitung 300 x 150px (width => 300px, height => 2:1 * width => 150px). Namun, ukuran intrinsiknya adalah 0 dan Firefox akan melaporkannya melalui properti naturalXXX, sedangkan Chrome akan menyetelnya ke 300 dan 150.

onload = e => {
  const img = document.querySelector('img');
  console.log(
    'computed',
    img.width, // 300
    img.height // 150
  );
  console.log(
    'intrinsic',
    img.naturalWidth, // 0 in FF, 300 in Chrome
    img.naturalHeight // 0 in FF, 150 in Chrome
  );
}
img { border: 1px solid blue}
<img src="http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg">

Sekarang, saat Anda menggambar gambar ini di kanvas, tidak ada aturan CSS yang menentukan bagaimana ukuran yang dihitung ini harus dihitung, dan UA tidak menyetujui apa yang harus terjadi saat Anda menggambar gambar ini di kanvas.
Firefox tidak akan menggambarnya (ingat, mereka menyetel naturalXXX ke 0. Chrome akan menggunakan beberapa heuristik ajaib untuk menggambarnya sebaik yang menurutnya harus digambar. Dan itulah yang saya sebut bug.
Jika HTMLImageElement digunakan di drawImage tidak memiliki srcset (yaitu kepadatannya adalah 1) drawImage(img, x, y) seharusnya menghasilkan hasil yang sama seperti drawImage(img, x, y, img.naturalWidth, img.naturalHeight).
Namun, mengingat keajaiban yang mereka gunakan untuk gambar-gambar ini tanpa ukuran intrinsik, bukan itu yang terjadi.

Jadi Anda melihatnya terdistorsi di Chrome karena Anda memintanya digambar sebagai persegi panjang 300 x 300.

const img = new Image(),
  canvas = document.getElementById('canvas'),
  ctx = canvas.getContext('2d'),
  draw = async () => {
    const w = img.naturalWidth,
          h = img.naturalHeight;
    console.log('reported img size', w, h);
    ctx.fillRect(0, 0, w, h);
    await wait(2000);
    console.log('drawImage with reported size');
    ctx.drawImage(img, 0, 0, w, h);    
    await wait(2000);
    console.log('default drawImage');   
    ctx.drawImage(img, 0, 0);
  };


img.src = "http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

img.onload = draw;

function wait(ms) {
  return new Promise(res => setTimeout(res, ms));
}
<canvas id="canvas" width="300" height="300">

Jadi Anda dapat mencoba mengukur rasio yang Anda perlukan untuk membuatnya drawImage(img, x, y, w, h) sama seperti ketika hanya menggunakan 3 argumen, tetapi yang terbaik mungkin adalah menggunakan svg yang benar, dengan set lebar dan tinggi absolut, yang akan membuatnya berfungsi di setiap browser :

let img = new Image(),
  canvas = document.getElementById('canvas'),
  ctx = canvas.getContext('2d'),
  step = 0,
  drawImage = (ctx, img, x, y, degrees, w = 300, h = 300) => {
    ctx.save();
    ctx.translate(x + w / 4, y + h / 4);
    ctx.rotate(degrees * Math.PI / 180.0);
    ctx.translate(-x - w / 4, -y - h / 4);
    ctx.drawImage(img, x, y, w, h);
    ctx.restore();
  },
  animate = () => {
    ctx.globalCompositeOperation = 'destination-over';
    // clear canvas
    ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);

    drawImage(ctx, img, 0, 0, step)

    step++;
    window.requestAnimationFrame(animate);
  }


canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

img.onload = animate;

fetch("https://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg")
  .then(resp => resp.text())
  .then(markup => {
    const mime = 'image/svg+xml';
    const doc = new DOMParser().parseFromString(markup, mime);
    doc.documentElement.setAttribute('width', 150);
    doc.documentElement.setAttribute('height', 150);
    img.src = URL.createObjectURL(
      new Blob( [new XMLSerializer().serializeToString(doc)], { type: mime } )
    );
  })
  .catch(console.error);
* { margin:0; padding:0; } /* to remove the top and left whitespace */
html, body { width:100%; height:100%; } /* just to be sure these are full screen*/
canvas { display:block;} /* To remove the scrollbars */
<canvas id="canvas" width="300" height="300">

person Kaiido    schedule 05.09.2019
comment
Bisakah Anda mempertimbangkan untuk melihat di sini? Tampaknya ini sesuai dengan keinginan Anda, dan pertanyaannya adalah mendapatkan banyak penayangan meskipun sudah tidak aktif selama beberapa waktu, jadi orang mungkin akan menghargai jawaban yang bagus. Saya mencoba melihat sendiri standarnya tetapi saya tidak dapat memasukkan bagian-bagian tersebut ke dalam jawaban yang kohesif. Saya akan memberikan hadiah. - person Snow; 25.09.2019

Cobalah untuk berubah

canvas.width =window.innerWidth;
canvas.height = window.innerHeight;

Dengan

canvas.style.width = "100%";
canvas.style.height = "100%";

let img = new Image(),
	canvas = document.getElementById('canvas'),
	ctx = canvas.getContext('2d'),
	step = 0, 
	drawImage = (ctx, img, x, y, degrees,  w = 300, h = 300) => {
		ctx.save();
		ctx.translate(x+w/4, y+h/4);
		ctx.rotate(degrees*Math.PI/180.0);
		ctx.translate(-x-w/4, -y-h/4);
		ctx.drawImage(img, x, y, w, h);
		ctx.restore();
	},
	animate = () => {
		ctx.globalCompositeOperation = 'destination-over';
		// clear canvas
		ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); 

		drawImage(ctx, img, 0, 	0, step)
	

		step++;
		window.requestAnimationFrame(animate);
	}


img.src = "http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg";
canvas.style.width = "100%";
canvas.style.height = "100%";


animate();
* { margin:0; padding:0; } /* to remove the top and left whitespace */
	html, body { width:100%; height:100%; } /* just to be sure these are full screen*/
	canvas { display:block;} /* To remove the scrollbars */
<canvas id="canvas">

person Musevarg    schedule 04.09.2019
comment
Ini tidak berhasil untuk saya, browser apa yang Anda gunakan? - person Himmators; 05.09.2019