如何平移画布?

时间:2015-11-25 19:34:30

标签: javascript canvas

我的代码中有这些事件监听器

canvas.addEventListener('mousemove', onMouseMove, false);
canvas.addEventListener('mousedown', onMouseDown,false);
canvas.addEventListener('mouseup', onMouseUp, false);

这些功能将帮助我平移画布。我已在onLoad中声明了一个名为panisDownmousePostion和之前鼠标位置的变量。然后在初始化函数中将panmousePospremousepos设置为包含0,0的向量

function draw() {
    context.translate(pan.getX(), pan.getY());
    topPerson.draw(context);
    console.log(pan);
}

function onMouseDown(event) {
    var x = event.offsetX;
    var y = event.offsetY;
    var mousePosition = new vector(event.offsetX, event.offsetY);

    previousMousePosition = mousePosition;

    isDown = true;

    console.log(previousMousePosition);
    console.log("onmousedown" + "X coords: " + x + ", Y coords: " + y);
}

function onMouseUp(event) {
    isDown = false;
}


function onMouseMove(event) {
    if (isDown) {
        console.log(event.offsetX);
        mousePosition = new vector(event.offsetX, event.offsetY);
        newMousePosition = mousePosition;
        console.log('mouseMove' + newMousePosition);

        var panX = newMousePosition.getX() - previousMousePosition.getX();
        var panY = newMousePosition.getY() - previousMousePosition.getY();
        console.log('onMouseMove:  ' + panX);
        pan = new vector(panX, panY);
        console.log('mouseMove' + pan);

    }
}

但它没有注册新的pan值,因此您可以尝试拖动画布。我知道我的鼠标拖动事件有效,但不是pan

2 个答案:

答案 0 :(得分:9)

回答问题

您尚未提供部分代码。具体来说,您正在创建每个事件的矢量对象可能就在那里。 (实际上你不应该每次都创建一个新对象。创建一次并更新值)

我看到的是mouseMove事件不会更新上一个鼠标位置对象,因此您只能从上次鼠标按下时进行平移。但你可能想要那个。因此,如果没有代码,我就不会知道错误,因为给出的代码是可以的。

以下是我如何做整个shabang ..

如何平移(和缩放)。

下面是使用鼠标进行平移和缩放的示例。它比标准的平移和缩放稍微复杂一点,因为我在平底锅上添加了一些平滑处并进行缩放以使其具有更好的交互感。

工作原理。

画布使用变换矩阵来变换点。这样做是为了维持这个矩阵。我称之为变形空间,真实空间。我还维护一个逆矩阵,用于将屏幕空间转换为真实空间。

演示的核心是对象displayTransform,它包含矩阵,所需的所有单个值,以及函数update()调用一帧,setHome()获取屏幕空间转换并将其应用于画布。用于清除屏幕。并setTransform()将画布设置为真实空间(缩放的平移空间)

为了平滑移动,我有一个价值x, y, ox, oy, scale,rotate的镜像。 ((ox,oy)是原点x和y)(并且是旋转工作)这些变量中的每一个都有一个前缀为d的delta和一个前缀为c的chaser。追踪值追逐所需的值。您不应该触摸追踪器值。有两个值称为dragaccel(加速的简称)drag(不是真正的模拟拖动)是增量衰减的速度。 drag>的值0.5将导致有弹性的反应。当你走向一个时,它将变得越来越有弹性。在1处,界限不会停止,超过1并且它不可用。 '加速度'变换响应鼠标移动的速度有多快。低值是慢响应,0根本没有响应,一个是即时响应。玩这些值来找到你喜欢的东西。

追踪者值的逻辑示例

var x = 100; // the value to be chased
var dx = 0; // the delta x or the change in x per frame
var cx = 0; // the chaser value. This value chases x;
var drag = 0.1;  // quick decay
var accel = 0.9; // quick follpw
// logic
dx += (x-cx)*accel; // get acceleration towards x
dx *= drag;          // apply the drag
cx += dx;           // change chaser by delta x.

转换坐标

如果您不知道事物的位置,那么无法使用缩放平移旋转画布。为此,我保留一个逆矩阵。它将屏幕x和y转换为realspace x和y。为方便起见,我将每次更新时将鼠标转换为实际空间。如果你想反向realSpace屏幕空间。它只是

var x; // real x coord (position in the zoom panned rotate space)
var y; // real y coord

// "this" is displayTransform
x -= this.cx;
y -= this.cy;    
// screenX and screen Y are the screen coordinates.
screenX = (x * this.matrix[0] + y * this.matrix[2])+this.cox;
screenY = (x * this.matrix[1] + y * this.matrix[3])+this.coy;

您可以在鼠标displayTransform.update的末尾看到它,我使用逆变换将鼠标屏幕坐标转换为真正的坐标。然后在主更新循环中,我使用鼠标真实坐标显示帮助文本。我将其留给代码的用户来创建一个可以转换任何屏幕坐标的功能。 (只需捏住转换鼠标的位置)。

<强>缩放

使用鼠标滚轮完成缩放。这提出了一些问题,当然您希望缩放以鼠标为中心。但转换实际上是相对于屏幕的左上角。为了解决这个问题,我还保留了原点x和y。这基本上浮动直到需要车轮变焦然后将其设置为鼠标实际位置,并且从左上方的鼠标距离被置于变换x和y位置。然后只需增大或减小刻度即可放大和缩小。我已将原点和偏移量保留为浮点数(不设置追踪值),这适用于当前的拖动和加速设置,但是如果您注意到它与其他设置无法正常工作,请设置cx,cy,cox ,腼腆的价值观。 (我在代码中添加了一个注释)

<强>泛

使用鼠标左键完成平移。单击并拖动以平移。这是直截了当的。我得到了最后一个鼠标位置和新的一个屏幕空间(鼠标事件给出的坐标)之间的差异。这给了我一个鼠标增量矢量。我将delta鼠标矢量转换为实际空间,并从左上角displayTransform.xdisplayTransform.y中减去该值。多数民众赞成我让追逐者x和y平稳了。

该代码段只显示一个可以平移和缩放的大图像。我检查完整的标志而不是使用onload。在加载图片时,片段只显示加载。使用requestAnimationFrame刷新主循环,首先我更新displayTransform然后在家庭空间(屏幕空间)中清除画布,然后在真实空间中显示图像。一如既往,我的战斗时间将随着时间的推移而增加更多评论,也许是一两个功能。

如果您发现追逐变量有点太多,您可以删除它们并将所有带有前缀的变量替换为未加前缀的变量。

好的希望这有帮助。还没有完成需要清理,但需要做一些真正的工作。

&#13;
&#13;
var canvas = document.getElementById("canV"); 
var ctx = canvas.getContext("2d");
var mouse = {
    x : 0,
    y : 0,
    w : 0,
    alt : false,
    shift : false,
    ctrl : false,
    buttonLastRaw : 0, // user modified value
    buttonRaw : 0,
    over : false,
    buttons : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
};
function mouseMove(event) {
    mouse.x = event.offsetX;
    mouse.y = event.offsetY;
    if (mouse.x === undefined) {
        mouse.x = event.clientX;
        mouse.y = event.clientY;
    }
    mouse.alt = event.altKey;
    mouse.shift = event.shiftKey;
    mouse.ctrl = event.ctrlKey;
    if (event.type === "mousedown") {
        event.preventDefault()
        mouse.buttonRaw |= mouse.buttons[event.which-1];
    } else if (event.type === "mouseup") {
        mouse.buttonRaw &= mouse.buttons[event.which + 2];
    } else if (event.type === "mouseout") {
        mouse.buttonRaw = 0;
        mouse.over = false;
    } else if (event.type === "mouseover") {
        mouse.over = true;
    } else if (event.type === "mousewheel") {
        event.preventDefault()
        mouse.w = event.wheelDelta;
    } else if (event.type === "DOMMouseScroll") { // FF you pedantic doffus
       mouse.w = -event.detail;
    }
  

}

function setupMouse(e) {
    e.addEventListener('mousemove', mouseMove);
    e.addEventListener('mousedown', mouseMove);
    e.addEventListener('mouseup', mouseMove);
    e.addEventListener('mouseout', mouseMove);
    e.addEventListener('mouseover', mouseMove);
    e.addEventListener('mousewheel', mouseMove);
    e.addEventListener('DOMMouseScroll', mouseMove); // fire fox
    
    e.addEventListener("contextmenu", function (e) {
        e.preventDefault();
    }, false);
}
setupMouse(canvas);


// terms.
// Real space, real, r (prefix) refers to the transformed canvas space.
// c (prefix), chase is the value that chases a requiered value
var displayTransform = {
    x:0,
    y:0,
    ox:0,
    oy:0,
    scale:1,
    rotate:0,
    cx:0,  // chase values Hold the actual display
    cy:0,
    cox:0,
    coy:0,
    cscale:1,
    crotate:0,
    dx:0,  // deltat values
    dy:0,
    dox:0,
    doy:0,
    dscale:1,
    drotate:0,
    drag:0.1,  // drag for movements
    accel:0.7, // acceleration
    matrix:[0,0,0,0,0,0], // main matrix
    invMatrix:[0,0,0,0,0,0], // invers matrix;
    mouseX:0,
    mouseY:0,
    ctx:ctx,
    setTransform:function(){
        var m = this.matrix;
        var i = 0;
        this.ctx.setTransform(m[i++],m[i++],m[i++],m[i++],m[i++],m[i++]);
    },
    setHome:function(){
        this.ctx.setTransform(1,0,0,1,0,0);
        
    },
    update:function(){
        // smooth all movement out. drag and accel control how this moves
        // acceleration 
        this.dx += (this.x-this.cx)*this.accel;
        this.dy += (this.y-this.cy)*this.accel;
        this.dox += (this.ox-this.cox)*this.accel;
        this.doy += (this.oy-this.coy)*this.accel;
        this.dscale += (this.scale-this.cscale)*this.accel;
        this.drotate += (this.rotate-this.crotate)*this.accel;
        // drag
        this.dx *= this.drag;
        this.dy *= this.drag;
        this.dox *= this.drag;
        this.doy *= this.drag;
        this.dscale *= this.drag;
        this.drotate *= this.drag;
        // set the chase values. Chase chases the requiered values
        this.cx += this.dx;
        this.cy += this.dy;
        this.cox += this.dox;
        this.coy += this.doy;
        this.cscale += this.dscale;
        this.crotate += this.drotate;
        
        // create the display matrix
        this.matrix[0] = Math.cos(this.crotate)*this.cscale;
        this.matrix[1] = Math.sin(this.crotate)*this.cscale;
        this.matrix[2] =  - this.matrix[1];
        this.matrix[3] = this.matrix[0];

        // set the coords relative to the origin
        this.matrix[4] = -(this.cx * this.matrix[0] + this.cy * this.matrix[2])+this.cox;
        this.matrix[5] = -(this.cx * this.matrix[1] + this.cy * this.matrix[3])+this.coy;        


        // create invers matrix
        var det = (this.matrix[0] * this.matrix[3] - this.matrix[1] * this.matrix[2]);
        this.invMatrix[0] = this.matrix[3] / det;
        this.invMatrix[1] =  - this.matrix[1] / det;
        this.invMatrix[2] =  - this.matrix[2] / det;
        this.invMatrix[3] = this.matrix[0] / det;
        
        // check for mouse. Do controls and get real position of mouse.
        if(mouse !== undefined){  // if there is a mouse get the real cavas coordinates of the mouse
            if(mouse.oldX !== undefined && (mouse.buttonRaw & 1)===1){ // check if panning (middle button)
                var mdx = mouse.x-mouse.oldX; // get the mouse movement
                var mdy = mouse.y-mouse.oldY;
                // get the movement in real space
                var mrx = (mdx * this.invMatrix[0] + mdy * this.invMatrix[2]);
                var mry = (mdx * this.invMatrix[1] + mdy * this.invMatrix[3]);   
                this.x -= mrx;
                this.y -= mry;
            }
            // do the zoom with mouse wheel
            if(mouse.w !== undefined && mouse.w !== 0){
                this.ox = mouse.x;
                this.oy = mouse.y;
                this.x = this.mouseX;
                this.y = this.mouseY;
                /* Special note from answer */
                // comment out the following is you change drag and accel
                // and the zoom does not feel right (lagging and not 
                // zooming around the mouse 
                /*
                this.cox = mouse.x;
                this.coy = mouse.y;
                this.cx = this.mouseX;
                this.cy = this.mouseY;
                */
                if(mouse.w > 0){ // zoom in
                    this.scale *= 1.1;
                    mouse.w -= 20;
                    if(mouse.w < 0){
                        mouse.w = 0;
                    }
                }
                if(mouse.w < 0){ // zoom out
                    this.scale *= 1/1.1;
                    mouse.w += 20;
                    if(mouse.w > 0){
                        mouse.w = 0;
                    }
                }

            }
            // get the real mouse position 
            var screenX = (mouse.x - this.cox);
            var screenY = (mouse.y - this.coy);
            this.mouseX = this.cx + (screenX * this.invMatrix[0] + screenY * this.invMatrix[2]);
            this.mouseY = this.cy + (screenX * this.invMatrix[1] + screenY * this.invMatrix[3]);            
            mouse.rx = this.mouseX;  // add the coordinates to the mouse. r is for real
            mouse.ry = this.mouseY;
            // save old mouse position
            mouse.oldX = mouse.x;
            mouse.oldY = mouse.y;
        }
        
    }
}
// image to show
var img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/e/e5/Fiat_500_in_Emilia-Romagna.jpg"
// set up font
ctx.font = "14px verdana";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// timer for stuff
var timer =0;
function update(){
    timer += 1; // update timere
    // update the transform
    displayTransform.update();
    // set home transform to clear the screem
    displayTransform.setHome();
    ctx.clearRect(0,0,canvas.width,canvas.height);
    // if the image loaded show it
    if(img.complete){
        displayTransform.setTransform();
        ctx.drawImage(img,0,0);
        ctx.fillStyle = "white";
        if(Math.floor(timer/100)%2 === 0){
            ctx.fillText("Left but to pan",mouse.rx,mouse.ry);
        }else{
            ctx.fillText("Wheel to zoom",mouse.rx,mouse.ry);
        }
    }else{
        // waiting for image to load
        displayTransform.setTransform();
        ctx.fillText("Loading image...",100,100);
        
    }
    if(mouse.buttonRaw === 4){ // right click to return to homw
         displayTransform.x = 0;
         displayTransform.y = 0;
         displayTransform.scale = 1;
         displayTransform.rotate = 0;
         displayTransform.ox = 0;
         displayTransform.oy = 0;
     }
    // reaquest next frame
    requestAnimationFrame(update);
}
update(); // start it happening
&#13;
.canC { width:400px;  height:400px;}
div {
  font-size:x-small;
}
&#13;
<div>Wait for image to load and use <b>left click</b> drag to pan, and <b>mouse wheel</b> to zoom in and out. <b>Right click</b> to return to home scale and pan. Image is 4000 by 2000 plus so give it time if you have a slow conection. Not the tha help text follows the mouse in real space. Image from wiki commons</div>
<canvas class="canC" id="canV" width=400 height=400></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:6)

这是一个简单(注释)的平移代码示例

它通过累积水平(和垂直)拖动鼠标的净量来工作然后它重绘所有内容,但是被那些累积的水平&amp;垂直距离。

示例代码和演示:

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

// mouse drag related variables
var isDown=false;
var startX,startY;

// the accumulated horizontal(X) & vertical(Y) panning the user has done in total
var netPanningX=0;
var netPanningY=0;

// just for demo: display the accumulated panning
var $results=$('#results');

// draw the numbered horizontal & vertical reference lines
for(var x=0;x<100;x++){ ctx.fillText(x,x*20,ch/2); }
for(var y=-50;y<50;y++){ ctx.fillText(y,cw/2,y*20); }

// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});

function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // calc the starting mouse X,Y for the drag
  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);

  // set the isDragging flag
  isDown=true;
}

function handleMouseUp(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // clear the isDragging flag
  isDown=false;
}

function handleMouseOut(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // clear the isDragging flag
  isDown=false;
}

function handleMouseMove(e){

  // only do this code if the mouse is being dragged
  if(!isDown){return;}
  
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  // get the current mouse position
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // dx & dy are the distance the mouse has moved since
  // the last mousemove event
  var dx=mouseX-startX;
  var dy=mouseY-startY;

  // reset the vars for next mousemove
  startX=mouseX;
  startY=mouseY;

  // accumulate the net panning done
  netPanningX+=dx;
  netPanningY+=dy;
  $results.text('Net change in panning: x:'+netPanningX+'px, y:'+netPanningY+'px'); 

  // display the horizontal & vertical reference lines
  // The horizontal line is offset leftward or rightward by netPanningX
  // The vertical line is offset upward or downward by netPanningY
  ctx.clearRect(0,0,cw,ch);
  for(var x=-50;x<50;x++){ ctx.fillText(x,x*20+netPanningX,ch/2); }
  for(var y=-50;y<50;y++){ ctx.fillText(y,cw/2,y*20+netPanningY); }

}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4 id=results>Drag the mouse to see net panning in x,y directions</h4>
<canvas id="canvas" width=300 height=150></canvas>