使用偏移原点在“画布”中缩放图像

时间:2015-07-26 03:29:54

标签: javascript html canvas image-scaling

我几天来一直在努力解决这个问题。我的问题基于您可以在此处找到的代码 - http://codepen.io/theOneWhoKnocks/pen/VLExPX。在该示例中,您将看到3个图像,第一个从[0,0]原点开始,第二个从画布中心开始,第三个我想从偏移图像的中心缩放。

基本上我希望图像向上或向下缩放,但要保持角色虹膜的中心位置。下面你会找到一段控制第三张图像渲染的代码。

function renderOffset(){
  var dims = getScaledDims();

  paintBG(ctx3);
  ctx3.drawImage(loadedImg, offsetX, offsetY, dims.width, dims.height);
  drawCenterAxis(ctx3);
}

经过大量谷歌搜索并浏览论坛后,我认为我需要利用transformMatrix,但到目前为止我没有尝试过任何工作。我期待您的任何想法或建议,并感谢您的时间。

进一步澄清

我正在创建一个图像编辑器。对于我在这里呈现的特定用例,用户已将图像移动到左侧108px&高达8px。

var offsetX = -108;
var offsetY = 8;

当用户缩放偏移图像时,我希望它在可视画布区域的中心(红色十字准线,或者在这种情况下是字符虹膜)缩放。

更新

我已经更新了codepen链接以指向最终代码。以下是添加内容列表:

  • 在接受的答案中提到的一些代码中添加。
  • 添加了拖动图像的功能。
  • 为偏移添加了视觉跟踪器。

2 个答案:

答案 0 :(得分:4)

以下是如何将指定的[eyeX,eyeY]拉到画布中心并缩放图像:

  • 通过乘以-eyeX&将眼睛拉到画布[0,0]上。 -eyeY由比例因子。
  • 通过添加画布宽度,高度的一半将眼睛推到中心画布。
  • 按比例因子缩放图像。
  • 使用context.drawImage在画布上绘制图像。

示例:

context.drawImage(

    // start with the image
    img,

    // scale the eyeX offset by the scaling factor
    // and then push the image horizontally to center canvas
    -eyeX*scale + canvas.width/2,

    // scale the eyeY offset by the scaling factor
    // and then push the image vertically to center canvas
    -eyeY*scale + canvas.height/2,

    // scale whole image by the scaling factor
    canvas.width*scale,
    canvas.height*scale
);

插图:中心眼为100%和175%

enter image description here enter image description here

以下是示例代码和演示:



var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }

var eyeX=182;
var eyeY=66;
var scale=1.00;
$myslider=$('#myslider');
$myslider.attr({min:25,max:250}).val(100);
$myslider.on('input change',function(){
  scale=parseInt($(this).val())/100;
  drawAll(eyeX,eyeY,scale);
});

var iw,ih;
var img=new Image();
img.onload=start;
img.src="https://i.stack.imgur.com/aFbEw.png";
function start(){
  iw=cw=canvas.width=img.width;
  ih=ch=canvas.height=img.height;
  drawAll(eyeX,eyeY,scale);
}

function drawAll(x,y,scale){
  ctx.clearRect(0,0,cw,ch);
  centerAndZoom(x,y,scale);
  drawCrosshairs();    
}

function centerAndZoom(x,y,scale){
  ctx.drawImage(
    img,
    -x*scale+iw/2,
    -y*scale+ih/2,
    iw*scale,
    ih*scale
  );
}

function drawCrosshairs(){
  ctx.beginPath();
  ctx.moveTo(cw/2,0);
  ctx.lineTo(cw/2,ch);
  ctx.moveTo(0,ch/2);
  ctx.lineTo(cw,ch/2);
  ctx.stroke();
}

body{ background-color: white; }
#canvas{border:1px solid red; margin:0 auto; }

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Scale:&nbsp;<input id=myslider type=range><br>
<canvas id="canvas" width=300 height=300></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:3)

技巧是理解规模改变许多变量的方式。首先,它会更改画布上可见的源图像的大小。接下来,这与缩放的所需中心点相结合会影响我们应该从图像开始绘制的位置。

如果比例为1.0,则显示的源图像的像素数等于dst画布所具有的像素数。即,如果画布是150x150,我们可以看到150x150的输入像素。但是,如果比例是2.0,那么我们希望画出2倍大小的东西。这意味着我们只希望在dst画布的150x150像素上显示src图像的75x75像素。同样,如果我们希望以0.5的比例绘制,我们应该期望看到在dst画布的150x150中显示的src图像的300x300像素。也许你现在可以看到比例尺和画布尺寸之间的关系。

考虑到这一点,我们可以设置确定我们希望看到多少src图像。这很简单:

var srcWidth = canvas.width / scale;
var srcHeight = canvas.height / scale;

现在我们知道将显示多少图像,我们可以设置确定图像在哪里开始绘制。由于我们有一个指定的缩放中心点,我们知道这一点应该始终保持在画布的中心。

如果我们从等式中删除缩放,并使用前面的数字,我们可以看到我们想要显示src图像的150x150像素,并且我们需要开始在我们的中心左上方和左侧绘制75个像素 - 点。这样做将绘制150x150像素的源图像,并将我们的中心点放在画布中间。

如果我们重新考虑缩放比例,我们知道我们并不总是会绘制src图像的150x150像素,这意味着我们不能盲目地开始75像素左上方和我们的中心点 - 我们将不得不缩放这75个像素。由于这75个像素等于我们将要显示的图像部分宽度的一半和高度的一半,我们可以通过将srcWidth和srcHeight除以2来计算开始绘制图像的点。然后从中心点减去这个值。

这样做会给我们以下表达式:

ctx.drawImage(image, imgCenterX-(srcWidth/2), imgCenterY-(srcHeight/2), srcWidth, srcHeight, 0,0, canvas.width, canvas.height);

当我将这两者放在一个有效的样本中时,我最终得到了这个:

"use strict";

var imgOriginX = 182, imgOriginY = 66;

function byId(id,parent){return (parent == undefined ? document : parent).getElementById(id);}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
    var targetCanvas = byId('canvas3');
    var srcImage = byId('img1');

    drawImageScaled(targetCanvas, srcImage, imgOriginX, imgOriginY)
    drawCrosshair( byId('canvas3') );

    byId('scaleSlider').addEventListener('input', onScaleSliderChange, false);
}

/*
    code for scaling an image about an arbitrary point
*/

// canvas - target canvas element
// image - target canvas element
// imgCenterX - x coord of point of scaling centre-point (unit: pixels)
// imgCenterY - y coord of point of scaling centre-point (unit: pixels)
// scale - 1.0 = 100%
function drawImageScaled(canvas, image, imgCenterX, imgCenterY, scale)
{
    if (scale === undefined)
        scale = 1.0;

    var ctx = canvas.getContext('2d');
    ctx.clearRect(0,0,canvas.width,canvas.height);

    var srcWidth = canvas.width / scale;
    var srcHeight = canvas.height / scale;
    ctx.drawImage(image, imgCenterX-(srcWidth/2), imgCenterY-(srcHeight/2), srcWidth, srcHeight, 0,0, canvas.width, canvas.height);
}

function drawCrosshair(canvas)
{
    var ctx = canvas.getContext('2d');
    var width, height;
    width = canvas.width;
    height = canvas.height;
        ctx.save();
    ctx.beginPath();
        ctx.moveTo(width/2, 0);
        ctx.lineTo(width/2, height);
        ctx.moveTo(0, height/2);
        ctx.lineTo(width, height/2);
    ctx.closePath();
    ctx.strokeStyle = "red";
    ctx.stroke();
    ctx.restore();
}

function onScaleSliderChange(evt)
{
    var curValue = this.value;
    var scale = curValue / 100;
    var tgt, src;

    tgt = byId('canvas3');
    src = byId('img1');
    drawImageScaled(tgt, src, imgOriginX, imgOriginY, scale);
    drawCrosshair(tgt);
}
input[type=range]
{
    width: 18px;
    height: 122px;
    -webkit-appearance: slider-vertical;
}
canvas
{
    border: solid 1px #888;
}

img{ display:none;}
<img id='img1' src='https://i.stack.imgur.com/aFbEw.png'/>
<hr>
<canvas id='canvas3' width=150 height=150>Canvas not supported. :(</canvas>
<input id='scaleSlider' type="range" class="scale-slider js-scaleSlider" min="0" max="200" value="100" orient="vertical"/>