javascript - HTML5 Canvas - 使用鼠标通过 anchor 旋转

标签 javascript html canvas html5-canvas

我正在开发 html5 的 Canvas 元素。我有以下代码,它是可拖动和可调整大小的图像。我怎样才能把它变成可由 anchor 旋转的呢?我怎样才能通过 anchor 提供实时旋转。我看到其他代码示例,但不知道如何实现。 采样器工作: 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);
});

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

最佳答案

这是一个函数,它将设置变换以缩放旋转和平移矩形或任何东西。

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);
}

之后设置 Canvas 变换。现在只需在其局部空间坐标中绘制对象即可。

例如在 Canvas 中心绘制双比例和 45 度旋转

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

您会注意到坐标是负数。这是因为我希望盒子的中心位于 Canvas 的中心。如果我想让盒子的左上角居中

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,   // random scale
    Math.random() * Math.PI * 2 // random rotation
);

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

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

它将绘制在变换设置的位置。

使用 ctx.setTransform 省去使用保存和恢复的麻烦。

如果在任何阶段你需要返回到默认转换

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

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

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

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};
}

现在您在本地空间中有了鼠标坐标。如果你需要查明鼠标是否在你的盒子里

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
}

这应该能满足您的需求。

更新

我忘了你想同时缩放 x 和 y..所以下面是缩放 X 和 Y 轴的修改函数

// 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);
}

然后把鼠标放到本地

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};
}

关于javascript - HTML5 Canvas - 使用鼠标通过 anchor 旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35526999/

相关文章:

javascript - dispatchEvent 导致错误 failed to execute 'dispatchEvent' on 'EventTarget' : parameter 1 is not of type 'Event'

javascript - 如何在 Javascript 中将变量作为数组名称传递到函数中

javascript - 如何设置 Google Chrome 语音输入控件的麦克风样式

javascript - 如何使用 Javascript 返回 CSS 颜色元素的值?

javascript - 创建 Canvas 对象数组

javascript - 安全地将数据从 Facebook JavaScript SDK 方法 FB.login 传输到我的服务器端代码

javascript - Wordpress 外部 api get 请求的延迟加载

html - 我的位置 my::after 内容如何始终位于 <a> 的内端?

javascript - 根据类将 Canvas 图像绑定(bind)到事件

javascript - 如何将表单的结果转换为图像