繁体   English   中英

Canvas drawImage:从“适合” - 到“填充” - 样式设置线性插值时的比例问题

[英]Canvas drawImage: Proportion Issue when linearly interpolating from "fit"- to "fill"-style settings

我有一个 canvas,我希望能够绘制从“适合”(如 CSS“包含”)到“填充”(如 CSS)的不同尺寸的图像。 我使用具有不同源和目标属性的 drawImage() 来进行拟合和填充。 这两个极端都按预期完美地工作,但在图像比例之间,图像看起来很平坦。 我使用线性插值来计算源属性和目标属性之间的值。

我无法理解为什么会这样。

“适合/包含”属性:

ctx.drawImage(
    img, // image
    0, // source x
    0, // source y
    img.width, // source width
    img.height, // source height
    (canvas.width - canvas.height * imageAspect) / 2, // destination x
    0, // destination y
    canvas.height * imageAspect, // destination width
    canvas.height // destination height
)

“填充/覆盖”属性:

ctx.drawImage(
    img, // image
    0, // source x
    (image.height - img.width / canvasAspect) / 2, // source y
    img.width, // source width
    img.width / canvasAspect, // source height
    0, // destination x
    0, // destination y
    canvas.width, // destination width
    canvas.height // destination height
)

这些都很好,但是所有值的线性插值得到了错误的图像比例。 这是一个没有按预期工作的快速演示,我对插值进行了动画处理,以便您可以更清楚地看到压扁的效果:

代码笔

期望的结果是在 0(适合)和 1(填充)之间的每一步中保持图像的比例正确。 我在这里想念什么?

编辑:最简单的解决方案是始终获取完整的源图像(不使用 sX、sY、sWidth 和 sHeight 裁剪它),然后在图像大于 canvas 时在 canvas 上使用负坐标值绘制目标。 这是有效的,但它不是所需的行为。 因为进一步我需要能够仅绘制到 canvas 中的某个子矩形,在那里会看到重叠(“负值”)。 我不想在矩形之外绘制。 我很确定这只是一个需要解决的小数学问题。

对我来说,“编辑”中的解决方案是 go 的方式。
如果稍后您想将图像剪辑到比 canvas 更小的矩形中,请使用clip()方法:

 const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d"); canvas.width = 800; canvas.height = 150; let step = 1; let direction = 1; // control the clipping rect position with the mouse const mouse = {x: 400, y: 75}; onmousemove = (evt) => { const rect = canvas.getBoundingClientRect(); mouse.x = evt.clientX - rect.left; mouse.y = evt.clientY - rect.top; }; function getBetweenValue(from, to, stop) { return from + (to - from) * stop; } const image = new Image(); image.src = "https://w7.pngwing.com/pngs/660/154/png-transparent-perspective-grid-geometry-grid-perspective-grid-geometric-grid-grid.png"; let imageAspect = 0; let canvasAspect = canvas.width / canvas.height; let source; let containDestination; let coverDestination; function draw(image) { ctx.save(); ctx.clearRect(0, 0, canvas.width, canvas.height); // Clip the context in a sub-rectangle ctx.beginPath(); ctx.rect(mouse.x - 150, mouse.y - 50, 300, 100); ctx.stroke(); ctx.clip(); // Since our image scales from the middle of the canvas, // set the context's origin there, that makes our BBox values simpler ctx.translate(canvas.width / 2, canvas.height / 2); ctx.drawImage( image, source.x, source.y, source.width, source.height, getBetweenValue(containDestination.x, coverDestination.x, step), getBetweenValue(containDestination.y, coverDestination.y, step), getBetweenValue(containDestination.width, coverDestination.width, step), getBetweenValue(containDestination.height, coverDestination.height, step) ); ctx.restore(); // remove clip & transform } image.addEventListener("load", () => { imageAspect = image.width / image.height; source = { x: 0, y: 0, width: image.width, height: image.height }; containDestination = { x: -(canvas.height * imageAspect) / 2, y: -(canvas.height / 2), width: canvas.height * imageAspect, height: canvas.height }; coverDestination = { x: -image.width / 2, y: -image.height / 2, width: image.width, height: image.height }; raf(); }); function raf() { draw(image); step +=.005 * direction; if (step > 1 || step < 0) { direction *= -1; } window.requestAnimationFrame(raf); }
 canvas { border:1px solid red; } img { max-width:30em; height:auto; }
 Use your mouse to move the clipping rectangle<br> <canvas></canvas><br><br> Original image proportions:<br> <img src="https://w7.pngwing.com/pngs/660/154/png-transparent-perspective-grid-geometry-grid-perspective-grid-geometric-grid-grid.png" alt="">

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM