【问题标题】:How and why is canvas width/height messing with my image drawing?画布宽度/高度如何以及为什么会影响我的图像绘制?
【发布时间】:2020-01-07 18:39:05
【问题描述】:

我正在尝试编写一个函数,该函数采用 x,y 坐标和旋转,并将围绕图像中心点旋转的图像插入到画布中。此代码工作正常:

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">

但是当我尝试调整画布大小时

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

这会扭曲图像并改变旋转点,在更大的窗口上效果更清晰。我怎样才能拥有一个填满屏幕的画布并将其视为我使用 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">

【问题讨论】:

    标签: javascript canvas


    【解决方案1】:

    这是一个 Chrome“错误”,与您要求它绘制的坏 svg 图像有关。

    您绘制的 svg 没有绝对的 widthheight 属性,也没有 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>
    

    这意味着它没有固有大小,也没有任何固有比率。在 HTML 上下文中,浏览器仍然可以通过应用Inline, replaced elements 中描述的规则来设置默认大小。

    通过遵循这些规则,将您的 svg 保存在相对大小的容器中的 &lt;img&gt; 将具有 计算大小 300 x 150px (width =&gt; 300px, @987654329 @)。 但是,它的内部大小是0,Firefox 会通过naturalXXX 属性报告这一点,而Chrome 会将这些设置为300150

    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}
    &lt;img src="http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg"&gt;

    现在,当您要在画布上绘制此图像时,没有此类 CSS 规则来规定应如何计算此计算大小,并且 UA 不同意您在画布上绘制此图像时应该发生什么。
    Firefox 根本不会绘制它(记住,他们将naturalXXX 设置为0。 Chrome 将使用一些神奇的启发式方法来绘制它认为应该绘制的最好的。这就是我所说的错误。
    如果drawImage 中使用的 HTMLImageElement 没有srcset(即其密度为1),drawImage(img, x, y) 应该产生与drawImage(img, x, y, img.naturalWidth, img.naturalHeight) 相同的结果。
    然而,考虑到他们对这些没有内在尺寸的图像使用的魔法,这不是发生的事情。

    所以你看到它在 Chrome 中被扭曲了,因为你要求它被绘制成一个 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));
    }
    &lt;canvas id="canvas" width="300" height="300"&gt;

    因此,您可以尝试测量所需的比率,尽管drawImage(img, x, y, w, h) 与仅使用 3 个参数时相同,但最好的方法可能是使用正确的 svg,设置绝对宽度和高度,这将使它适用于所有浏览器:

    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 */
    &lt;canvas id="canvas" width="300" height="300"&gt;

    【讨论】:

    • 你可以考虑看看here吗?它似乎在你的小巷里,尽管已经有一段时间不活跃,但这个问题得到了大量的观点,所以人们可能会喜欢一个好的答案。我尝试自己查看标准,但我无法将这些部分完全融入一个有凝聚力的答案。我会奖励赏金。
    【解决方案2】:

    尝试改变

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

    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 */
    &lt;canvas id="canvas"&gt;

    【讨论】:

    • 这对我不起作用,你用的是什么浏览器?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-14
    • 1970-01-01
    • 1970-01-01
    • 2017-08-10
    • 2013-07-02
    • 1970-01-01
    • 2011-08-07
    相关资源
    最近更新 更多