HTML5 Canvas - 使用鼠标旋转锚点 [英] HTML5 Canvas - Rotate by anchor using a mouse

查看:295
本文介绍了HTML5 Canvas - 使用鼠标旋转锚点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在html5的canvas元素中开发。我有以下代码,它是可拖动和可重新调整大小的图像。我怎么能把它转动为可旋转的锚点呢?如何通过锚提供实时旋转。我看到其他代码示例,但不知道如何实现它。
取样工作:
http://jsfiddle.net/LAS8L/588/

 < canvas id =canvaswidth = 350 height = 350>< / canvas& 



Javascript

  var canvas = document.getElementById(canvas); 
var ctx = canvas.getContext(2d);

var canvasOffset = $(#canvas)。offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;

var startX;
var startY;
var isDown = false;


var pi2 = Math.PI * 2;
var resizerRadius = 8;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
x:0,
y:0
};
var imageX = 50;
var imageY = 50;
var imageWidth,imageHeight,imageRight,imageBottom;
var draggingImage = false;
var startX;
var startY;

var cx = canvas.width / 2;
var cy = canvas.height / 2;

var w;
var h;
var r = 0;



var img = new Image();
img.onload = function(){
imageWidth = img.width;
imageHeight = img.height;
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight
draw(true,false);
}
img.src =https://dl.dropboxusercontent.com/u/139992952/stackoverflow/facesSmall.png;


函数draw(withAnchors,withBorders){

//清除画布
ctx.clearRect(0,0,canvas.width,canvas 。高度);

//绘制图像
ctx.drawImage(img,0,0,img.width,img.height,imageX,imageY,imageWidth,imageHeight);

//可选地绘制可拖动的锚点
if(withAnchors){
drawDragAnchor(imageX,imageY);
drawDragAnchor(imageRight,imageY);
drawDragAnchor(imageRight,imageBottom);
drawDragAnchor(imageX,imageBottom);
}

//可选地绘制连接锚线
if(withBorders){
ctx.beginPath();
ctx.moveTo(imageX,imageY);
ctx.lineTo(imageRight,imageY);
ctx.lineTo(imageRight,imageBottom);
ctx.lineTo(imageX,imageBottom);
ctx.closePath();
ctx.stroke();
}

}

function drawDragAnchor(x,y){
ctx.beginPath();
ctx.arc(x,y,resizerRadius,0,pi2,false);
ctx.closePath();
ctx.fill();
}

function anchorHitTest(x,y){

var dx,dy;

// top-left
dx = x - imageX;
dy = y - imageY;
if(dx * dx + dy * dy <= rr){
return(0);
}
//右上
dx = x - imageRight;
dy = y - imageY;
if(dx * dx + dy * dy <= rr){
return(1);
}
//右下
dx = x - imageRight;
dy = y - imageBottom;
if(dx * dx + dy * dy <= rr){
return(2);
}
// bottom-left
dx = x - imageX;
dy = y - imageBottom;
if(dx * dx + dy * dy <= rr){
return(3);
}
return(-1);

}





函数desenhe(){
ctx.clearRect(0, canvas.width;
drawRotationHandle(true);
drawRect();
}


function drawRect(){
ctx.save();
ctx.translate(cx,cy);
ctx.rotate(r);
ctx.drawImage(img,0,0,img.width,img.height,-imageWidth,-imageheight,w,h);
// ctx.fillStyle =yellow;
// ctx.fillRect(-w / 2,-h / 2,w,h);
ctx.restore();
}


function drawRotationHandle(withFill){
ctx.save();
ctx.translate(cx,cy);
ctx.rotate(r);
ctx.beginPath();
ctx.moveTo(0,-1);
ctx.lineTo(imageWidth + 20,-1);
ctx.lineTo(imageWidth + 20,-7);
ctx.lineTo(imageWidth + 30,-7);
ctx.lineTo(imageWidth + 30,7);
ctx.lineTo(imageWidth + 20,7);
ctx.lineTo(imageWidth + 20,1);
ctx.lineTo(0,1);
ctx.closePath();
if(withFill){
ctx.fillStyle =blue;
ctx.fill();
}
ctx.restore();
}




function hitImage(x,y){
return(x> imageX&& x& imageX + imageWidth& y> imageY&& y< imageY + imageHeight);
}


function handleMouseDown(e){
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
draggingResizer = anchorHitTest(startX,startY);
draggingImage = draggingResizer< 0&& hitImage(startX,startY);
}

function handleMouseUp(e){
draggingResizer = -1;
draggingImage = false;
draw(true,false);
}

function handleMouseOut(e){
handleMouseUp(e);
}

function handleMouseMove(e){

if(draggingResizer> -1){

mouseX = parseInt clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);

//调整图片大小
switch(draggingResizer){
case 0:
// top-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageY = mouseY;
imageHeight = imageBottom - mouseY;
break;
case 1:
//右上
imageY = mouseY;
imageWidth = mouseX - imageX;
imageHeight = imageBottom - mouseY;
break;
case 2:
//右下
imageWidth = mouseX - imageX;
imageHeight = mouseY - imageY;
break;
case 3:
// bottom-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageHeight = mouseY - imageY;
break;
}

if(imageWidth< 25){imageWidth = 25;}
if(imageHeight< 25){imageHeight = 25;}

/ /设置图像的右下方
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;

//使用调整大小的锚重绘图像
draw(true,true);

} else if(draggingImage){

imageClick = false;

mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);

//将图像移动最近拖动的量
var dx = mouseX - startX;
var dy = mouseY - startY;
imageX + = dx;
imageY + = dy;
imageRight + = dx;
imageBottom + = dy;
//下一次复位startXY
startX = mouseX;
startY = mouseY;

//用边框重绘图片
draw(false,true);

}


}


$(#canvas)。mousedown(function(e){
handleMouseDown(e);
});
$(#canvas)。mousemove(function(e){
handleMouseMove(e);
});
$(#canvas)mouseup(function(e){
handleMouseUp(e);
});
$(#canvas)。mouseout(function(e){
handleMouseOut(e);
});

我想这样工作: http://jsfiddle.net/m1erickson/QqwKR/ 但我不知道如何合并代码。有人可以帮助我吗?

解决方案

这里是一个函数将设置转换为缩放旋转和翻译矩形或任何东西。

  function setTransform(ctx,x,y,scale,rotate){
var xdx = Math.cos )* scale; //创建x轴
var xdy = Math.sin(rotate)* scale;
ctx.setTransform(xdx,xdy, - xdy,xdx,x,y);
}

之后设置画布变换。



例如绘制画布中心的双倍刻度和45度旋转

  setTransform(ctx,canvas.width / 2,canvas.height / 2,2,Math.PI / 4); 
ctx.strokeRect(-100,-100,200,200);

你会注意到坐标是负的。这是因为我想让框的中心在画布的中心。

  ctx.strokeRect(0,0,200,200); 

或右下角

  ctx.strokeRect(-200,-200,200,200); 

返回到中心框可以移动到任何位置

  setTransform(
ctx,
Math.random()* canvas.width,// random x pos
Math.random()* canvas.height,// random y pos
Math.random()* 10 + 0.1,//随机缩放
Math.random()* Math.PI * 2 //随机旋转
);

转换是随机的,但我不需要关心我仍然可以在本地坐标绘制框。

  ctx.strokeRect(-100,-100,200,200); 

这将是绘制变换的地方。



使用 ctx.setTransform 可以节省使用保存和恢复的麻烦。



在任何阶段您都需要返回默认转换

  ctx.setTransform(1,0,0,1,0,0 ); 

接下来面对的问题是鼠标坐标是布局坐标,局部空间。您需要将鼠标坐标转换为本地对象坐标。



这是通过将鼠标坐标乘以矩阵的逆矩阵来完成的。这里有一些数学。

  function getMouseLocal(mousex,mouseY,x,y,scale,rot){
var xdx = Math.cos(rotate)* scale; //创建x轴
var xdy = Math.sin(rotate)* scale;

//获取两个轴的叉积
var cross = xdx * xdx - xdy * -xdy;
//或
var cross = Math.pow(xdx,2)+ Math.pow(xdy,2);

//然后创建倒置轴
var ixdx = xdx / cross; // create inverted x axis
var ixdy = -xdy / cross;
var iydx = xdy / cross; // create inverted y axis
var iydy = xdx / cross;

//现在从鼠标coords中删除原点
mouseX - = x;
mouseY - = y;

//乘以invers矩阵
var localMouseX = mouseX * ixdx + mouseY * iydx;
var localMouseY = mouseX * ixdy + mouseY * iydy;

//并返回结果
return {x:localMouseX,y:localMouseY};
}

现在在本地空间中有鼠标坐标。如果你需要知道鼠标是否在框中你

  setTransform(ctx,100,100,2,Math.PI / 4); 
ctx.strokeRect(-100,-100,200,200);
var localMouse = getMouseLocal(mouseX,mouseY,100,100,2,Math.PI / 4);
if(localMouse.x> -100&& localMouse.x< -100 + 200&&& localMouse.y> -100&& localMouse.y< -100 + 200 ){
//鼠标在框内
}

您需要什么。



UPDATE



x和y。下面是缩放X轴和Y轴的修改函数

  // sx和sy是scale x和y 
函数setTransform(ctx,x,y,sx,sy,rotate){
var xdx = Math.cos(rotate); //创建x轴
var xdy = Math.sin(rotate);
ctx.setTransform(xdx * sx,xdy * sx, - xdy * sy,xdx * sy,x,y);
}

将鼠标移动到本地

  function getMouseLocal(mousex,mouseY,x,y,sx,sy,rot){
var xdx = Math.cos(rotate); //创建x轴
var xdy = Math.sin(rotate);

//获取两个轴的叉积
var cross = xdx * sx * xdx * sy - xdy * sx * -xdy * sy;
//或
//此快捷方式现在不工作。
// var cross = Math.pow(xdx,2)+ Math.pow(xdy,2);

//然后创建倒置轴
var ixdx =(xdx * sy)/ cross; // create inverted x axis
var ixdy =(-xdy * sx)/ cross;
var iydx =(xdy * sy)/ cross; // create inverted y axis
var iydy =(xdx * sx)/ cross;

//现在从鼠标coords中删除原点
mouseX - = x;
mouseY - = y;

//乘以invers矩阵
var localMouseX = mouseX * ixdx + mouseY * iydx;
var localMouseY = mouseX * ixdy + mouseY * iydy;

//并返回结果
return {x:localMouseX,y:localMouseY};
}


I am developing in html5's canvas element. I have the follwing code and it is draggable and resizable image. How can I turn it to rotatable by a anchor too? How can I provide live rotating by an anchor. I saw other codes samples, but do not know how to implement it. Sampler Working: http://jsfiddle.net/LAS8L/588/

<canvas id="canvas" width=350 height=350></canvas>

Javascript

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;

var startX;
var startY;
var isDown = false;


var pi2 = Math.PI * 2;
var resizerRadius = 8;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
    x: 0,
    y: 0
};
var imageX = 50;
var imageY = 50;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;

var cx = canvas.width / 2;
var cy = canvas.height / 2;

var w;
var h;
var r = 0;



var img = new Image();
img.onload = function () {
    imageWidth = img.width;
    imageHeight = img.height;
    imageRight = imageX + imageWidth;
    imageBottom = imageY + imageHeight
    draw(true, false);
}
img.src = "https://dl.dropboxusercontent.com/u/139992952/stackoverflow/facesSmall.png";


function draw(withAnchors, withBorders) {

    // clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // draw the image
    ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);

    // optionally draw the draggable anchors
    if (withAnchors) {
        drawDragAnchor(imageX, imageY);
        drawDragAnchor(imageRight, imageY);
        drawDragAnchor(imageRight, imageBottom);
        drawDragAnchor(imageX, imageBottom);
    }

    // optionally draw the connecting anchor lines
    if (withBorders) {
        ctx.beginPath();
        ctx.moveTo(imageX, imageY);
        ctx.lineTo(imageRight, imageY);
        ctx.lineTo(imageRight, imageBottom);
        ctx.lineTo(imageX, imageBottom);
        ctx.closePath();
        ctx.stroke();
    }

}

function drawDragAnchor(x, y) {
    ctx.beginPath();
    ctx.arc(x, y, resizerRadius, 0, pi2, false);
    ctx.closePath();
    ctx.fill();
}

function anchorHitTest(x, y) {

    var dx, dy;

    // top-left
    dx = x - imageX;
    dy = y - imageY;
    if (dx * dx + dy * dy <= rr) {
        return (0);
    }
    // top-right
    dx = x - imageRight;
    dy = y - imageY;
    if (dx * dx + dy * dy <= rr) {
        return (1);
    }
    // bottom-right
    dx = x - imageRight;
    dy = y - imageBottom;
    if (dx * dx + dy * dy <= rr) {
        return (2);
    }
    // bottom-left
    dx = x - imageX;
    dy = y - imageBottom;
    if (dx * dx + dy * dy <= rr) {
        return (3);
    }
    return (-1);

}





function desenhe() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawRotationHandle(true);
    drawRect();
}


function drawRect() {
    ctx.save();
    ctx.translate(cx, cy);
    ctx.rotate(r);
    ctx.drawImage(img, 0, 0, img.width, img.height, -imageWidth, -imageheight, w, h);
    //    ctx.fillStyle="yellow";
    //    ctx.fillRect(-w/2,-h/2,w,h);
    ctx.restore();
}


function drawRotationHandle(withFill) {
    ctx.save();
    ctx.translate(cx, cy);
    ctx.rotate(r);
    ctx.beginPath();
    ctx.moveTo(0, -1);
    ctx.lineTo(imageWidth + 20, -1);
    ctx.lineTo(imageWidth + 20, -7);
    ctx.lineTo(imageWidth + 30, -7);
    ctx.lineTo(imageWidth + 30, 7);
    ctx.lineTo(imageWidth + 20, 7);
    ctx.lineTo(imageWidth + 20, 1);
    ctx.lineTo(0, 1);
    ctx.closePath();
    if (withFill) {
        ctx.fillStyle = "blue";
        ctx.fill();
    }
    ctx.restore();
}




function hitImage(x, y) {
    return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}


function handleMouseDown(e) {
    startX = parseInt(e.clientX - offsetX);
    startY = parseInt(e.clientY - offsetY);
    draggingResizer = anchorHitTest(startX, startY);
    draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}

function handleMouseUp(e) {
    draggingResizer = -1;
    draggingImage = false;
    draw(true, false);
}

function handleMouseOut(e) {
    handleMouseUp(e);
}

function handleMouseMove(e) {

    if (draggingResizer > -1) {

        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);

        // resize the image
        switch (draggingResizer) {
            case 0:
                //top-left
                imageX = mouseX;
                imageWidth = imageRight - mouseX;
                imageY = mouseY;
                imageHeight = imageBottom - mouseY;
                break;
            case 1:
                //top-right
                imageY = mouseY;
                imageWidth = mouseX - imageX;
                imageHeight = imageBottom - mouseY;
                break;
            case 2:
                //bottom-right
                imageWidth = mouseX - imageX;
                imageHeight = mouseY - imageY;
                break;
            case 3:
                //bottom-left
                imageX = mouseX;
                imageWidth = imageRight - mouseX;
                imageHeight = mouseY - imageY;
                break;
        }

        if(imageWidth<25){imageWidth=25;}
        if(imageHeight<25){imageHeight=25;}

        // set the image right and bottom
        imageRight = imageX + imageWidth;
        imageBottom = imageY + imageHeight;

        // redraw the image with resizing anchors
        draw(true, true);

    } else if (draggingImage) {

        imageClick = false;

        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);

        // move the image by the amount of the latest drag
        var dx = mouseX - startX;
        var dy = mouseY - startY;
        imageX += dx;
        imageY += dy;
        imageRight += dx;
        imageBottom += dy;
        // reset the startXY for next time
        startX = mouseX;
        startY = mouseY;

        // redraw the image with border
        draw(false, true);

    }


}


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

I would like to work this way: http://jsfiddle.net/m1erickson/QqwKR/ but I don't know how merge the code. Can somebody help me?

解决方案

Here is a function the will set the transform to scale rotate and translate an rectangle or anything.

function setTransform(ctx,x,y,scale,rotate){
    var xdx = Math.cos(rotate) * scale;  // create the x axis
    var xdy = Math.sin(rotate) * scale;
    ctx.setTransform(xdx, xdy, - xdy, xdx, x, y);
}

The canvas transform is set after that. Now just draw the object in its local space coords.

Eg draw at center of canvas double scale and 45deg rotation

setTransform(ctx,canvas.width/2,canvas.height/2,2,Math.PI /4);
ctx.strokeRect(-100,-100,200,200);  

You will notice that the coordinates are negative. This is because I want the center of the box to be at the center of the canvas. If I wanted the top left of the box to be at the center

ctx.strokeRect(0,0,200,200);  

or the bottom right

ctx.strokeRect(-200,-200,200,200);  

Back to the centered box I can move it anywhere

setTransform(
    ctx,
    Math.random() * canvas.width, // random x pos
    Math.random() * canvas.height, // random y pos
    Math.random() * 10 + 0.1,   // random scale
    Math.random() * Math.PI * 2 // random rotation
);

The transform is random but I do not need to care I can still draw the box in local coordinates.

ctx.strokeRect(-100,-100,200,200);  

And it will be draw where ever the transform has set it to.

Using ctx.setTransform saves the hassle of using save and restore.

If at any stage you need to return to the default transform

ctx.setTransform(1,0,0,1,0,0);

The problem you will next face is that the mouse coordinates are in canvas coordinates while the object is in its local space. You need to convert the mouse coordinates into the local object coordinates.

This is done by multiplying the mouse coordinates by the inverse of the matrix. A bit of maths here.

function getMouseLocal(mousex,mouseY,x,y,scale,rot){
    var xdx = Math.cos(rotate) * scale; // create the x axis
    var xdy = Math.sin(rotate) * scale;

    // get the cross product of the two axies     
    var cross = xdx * xdx - xdy * -xdy;
    // or
    var cross = Math.pow(xdx,2) + Math.pow(xdy,2);

    // then create the inverted axies 
    var ixdx = xdx / cross;   // create inverted x axis
    var ixdy = -xdy / cross;
    var iydx = xdy / cross;   // create inverted y axis
    var iydy = xdx / cross;

    // now remove the origin from the mouse coords
    mouseX -= x;
    mouseY -= y;

    // multiply by the invers matrix    
    var localMouseX = mouseX * ixdx + mouseY * iydx;
    var localMouseY = mouseX * ixdy + mouseY * iydy;

    // and return the result
    return {x : localMouseX, y : localMouseY};
}

Now you have the mouse coordinates in the local space. If you need to find out if the mouse is inside the box you

setTransform(ctx,100,100,2,Math.PI/4);
ctx.strokeRect(-100,-100,200,200);
var localMouse= getMouseLocal(mouseX,mouseY,100,100,2,Math.PI/4);
if(localMouse.x > -100 && localMouse.x < -100 + 200 && localMouse.y > -100 && localMouse.y < -100 + 200){
    // mouse is inside the box
}

That should give you what you need.

UPDATE

I forgot you want to scale both x and y.. So below are the modified functions for scaling both X and Y axies

// sx and sy are scale x and y
function setTransform(ctx,x,y,sx,sy,rotate){
    var xdx = Math.cos(rotate);  // create the x axis
    var xdy = Math.sin(rotate);
    ctx.setTransform(xdx * sx, xdy * sx, - xdy * sy, xdx * sy, x, y);
}

And getting the mouse to local

function getMouseLocal(mousex,mouseY,x,y,sx,sy,rot){
    var xdx = Math.cos(rotate); // create the x axis
    var xdy = Math.sin(rotate);

    // get the cross product of the two axies     
    var cross = xdx * sx * xdx * sy - xdy *sx * -xdy * sy;
    // or
    // this shortcut does not work now.
    // var cross = Math.pow(xdx,2) + Math.pow(xdy,2);

    // then create the inverted axies 
    var ixdx = (xdx * sy) / cross;   // create inverted x axis
    var ixdy = (-xdy * sx) / cross;
    var iydx = (xdy * sy) / cross;   // create inverted y axis
    var iydy = (xdx * sx) / cross;

    // now remove the origin from the mouse coords
    mouseX -= x;
    mouseY -= y;

    // multiply by the invers matrix    
    var localMouseX = mouseX * ixdx + mouseY * iydx;
    var localMouseY = mouseX * ixdy + mouseY * iydy;

    // and return the result
    return {x : localMouseX, y : localMouseY};
}

这篇关于HTML5 Canvas - 使用鼠标旋转锚点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆