在旋转的CANVAS上绘制 [英] Draw on rotated CANVAS

查看:106
本文介绍了在旋转的CANVAS上绘制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个画布我可以用鼠标画画。

I have a canvas where I can draw with the mouse.

在某些时候我可以旋转缩放画布容器。

At some point I can rotate and scale the canvas container.

我想要的是能够画画,但我现在遇到的问题是鼠标坐标是旋转和缩放的。

What I would like is to be able to still draw, but the problem that I have right now is that the mouse coordinates are rotated and scaled.

取消旋转取消缩放鼠标坐标的正确方法是什么,以便正常渲染新绘图从用户的角度来看?

What would be the right way to unrotate and unscale the mouse coordinates so that the new drawings are rendered normally from the user point of view?

我尝试用 Y切换 X 坐标,但无法使数学正确。

I've tried switching X coordinates with Y, but can't get the maths right.

CODE& FIDDLE

CODE & FIDDLE

HTML

<div id="canvasDiv">
  <canvas id="canvas"></canvas>
</div>

<button>Rotate & Scale</button>

CSS

div
{
    outline: 1px solid red;
    text-align: center;
    width: 250px;
    height: 250px;
    position: relative;
}
canvas
{
    outline: 1px solid blue;
    width: 250px;
    height: 250px;
    bottom: 0;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
    z-index: 5;
}

.transformed
{
    transform: rotate(90deg) scale(1.4);
    margin-top: 75px;
    margin-left: 75px;
}

JS

isMouseDown = false;
canvas_offset = {left: 0, top: 0};
mouse = {x1: 0, y1: 0, x2: 0, y2: 0};
ppts = [];

canvas = $('#canvas').get(0);
ctx = canvas.getContext('2d');

canvas_container = $('#canvasDiv').get(0);
canvas_container_style = getComputedStyle(canvas_container);
canvas.width = parseInt(canvas_container_style.getPropertyValue('width'));
canvas.height = parseInt(canvas_container_style.getPropertyValue('height'));

var offset = $('#canvas').offset();
canvas_offset.left = offset.left;
canvas_offset.top = offset.top;

// Creating a tmp canvas
tmp_canvas = document.createElement('canvas');
tmp_ctx = tmp_canvas.getContext('2d');
tmp_canvas.id = 'tmp_canvas';
tmp_canvas.width = canvas.width;
tmp_canvas.height = canvas.height;

tmp_canvas.area = tmp_canvas.getBoundingClientRect();

canvas_container.appendChild(tmp_canvas);

$(document).on("mousedown", tmp_canvas, function(e)
{
    mouse.x1 = parseInt(e.clientX - tmp_canvas.area.left);
    mouse.y1 = parseInt((e.clientY - tmp_canvas.area.top - canvas_offset.top) + $(document).scrollTop());

    ppts.push({
            x: mouse.x1,
            y: mouse.y1,
            size: 1,
            color: "#000000"
    });

  isMouseDown = true;
});

$(document).on("mouseup", tmp_canvas, function(e)
{
    isMouseDown = false;

    ctx.drawImage(tmp_canvas, 0, 0);
    tmp_ctx.clearRect(0, 0, tmp_canvas.width, tmp_canvas.height);

    ppts = [];
});

$(document).on("mousemove", tmp_canvas, function(e)
{
    mouse.x2 = parseInt(e.clientX - tmp_canvas.area.left);
    mouse.y2 = parseInt((e.clientY - tmp_canvas.area.top - canvas_offset.top) + $(document).scrollTop());

    if(isMouseDown)
    {
        onPaint();
  }
});

var onPaint = function()
{
    // Saving all the points in an array
    ppts.push({
        x: mouse.x2,
        y: mouse.y2,
        size: 1,
        color: "#000000"
    });

    if(ppts.length < 3)
    {
        var b = ppts[0];
        tmp_ctx.beginPath();
        //ctx.moveTo(b.x, b.y);
        //ctx.lineTo(b.x+50, b.y+50);
        tmp_ctx.arc(b.x, b.y, tmp_ctx.lineWidth / 2, 0, Math.PI * 2, !0);
        tmp_ctx.fill();
        tmp_ctx.closePath();

        return;
    }

    // Tmp canvas is always cleared up before drawing.
    tmp_ctx.clearRect(0, 0, tmp_canvas.width, tmp_canvas.height);

    tmp_ctx.beginPath();
    tmp_ctx.moveTo(ppts[0].x, ppts[0].y);

    for(var i = 1; i < ppts.length - 2; i++)
    {
        var c = (ppts[i].x + ppts[i + 1].x) / 2;
        var d = (ppts[i].y + ppts[i + 1].y) / 2;

        tmp_ctx.lineWidth = ppts[i].size;
        tmp_ctx.strokeStyle = ppts[i].color;

        tmp_ctx.quadraticCurveTo(ppts[i].x, ppts[i].y, c, d);
    }

    // For the last 2 points
    tmp_ctx.quadraticCurveTo(
        ppts[i].x,
        ppts[i].y,
        ppts[i + 1].x,
        ppts[i + 1].y
    );

    tmp_ctx.stroke();
};

$("button").click(function()
{
    $("div").addClass("transformed");
});

FIDDLE
https://jsfiddle.net/h0cuycp4/

推荐答案

最后我有了这个工作!!

Finally I've got this working!!

所有学分转到 Blindman67 ,I已经修改了他的代码以使其适应我的需要。

All credits goes to Blindman67, I've modified his code to adapt it to my needs.

HTML

<div id="main"></div>
<br>
<span id="screen" style="border: 2px solid red;"></span>
<span id="world" style="border: 2px solid blue;"></span>
<button id="btnRotate">ROTATE!</button>

JS

var canvas = null;
var ctx = null;
var gridStart = null;
var gridEnd = null;
var gridStepMajor = null;
var gridStepMinor = null;
var minorCol = null;
var majorCol = null;
var minorWidth = null;
var majorWidth = null;

var scale = 1;
var rotation = 45;

    var painting = false,
    lastX = 0,
    lastY = 0,
    lineThickness = 1;

var matrix = [1, 0, 0, 1, 0, 0];      // normal matrix
var invMatrix = [1, 0, 0, 1];   // inverse matrix

function createMatrix(x, y, scale, rotation)
{
    rotation = rotation * (Math.PI / 180);

    var m = matrix; // just to make it easier to type and read
    var im = invMatrix; // just to make it easier to type and read

    // create the scale and rotation part of the matrix
    m[3] =   m[0] = Math.cos(rotation) * scale;
    m[2] = -(m[1] = Math.sin(rotation) * scale);

    // translation
    m[4] = x;
    m[5] = y;

    // calculate the inverse transformation
    // first get the cross product of x axis and y axis
    cross = m[0] * m[3] - m[1] * m[2];

    // now get the inverted axies
    im[0] =  m[3] / cross;
    im[1] = -m[1] / cross;
    im[2] = -m[2] / cross;
    im[3] =  m[0] / cross;
}

// function to transform to world space
function toWorld(x,y)
{
    var xx, yy, m;
    m = invMatrix;
    xx = x - matrix[4];
    yy = y - matrix[5];
    return{
       x:   parseInt(xx * m[0] + yy * m[2], 10) ,
       y:   parseInt(xx * m[1] + yy * m[3], 10)
    }
}
//----------------------------------------------------------------------------
var mouseWorldPos = toWorld(0, 0);

function draw()
{
    gridStart = 0;
    gridEnd = canvas.width;
    gridStepMajor = canvas.width / 10;
    gridStepMinor = canvas.width / 20;
    minorCol = "#999";
    majorCol = "#000";
    minorWidth = 1;
    majorWidth = 3;

    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.strokeStyle = majorCol ;
    ctx.lineWidth = majorWidth;

    for(i = gridStart; i <= gridEnd; i+= gridStepMajor)
    {
        ctx.moveTo(gridStart, i);
        ctx.lineTo(gridEnd, i);
        ctx.moveTo(i, gridStart);
        ctx.lineTo(i, gridEnd);
    }
    ctx.stroke();

    ctx.strokeStyle = minorCol;
    ctx.lineWidth = minorWidth;
    for(i = gridStart+gridStepMinor; i < gridEnd; i+= gridStepMinor)
    {
        ctx.moveTo(gridStart, i);
        ctx.lineTo(gridEnd, i);
        ctx.moveTo(i, gridStart);
        ctx.lineTo(i, gridEnd);
    }
    ctx.stroke();

    ctx.fillStyle = "red";
    ctx.strokeStyle = "red";
    ctx.lineWidth = 4;
    ctx.beginPath();
    ctx.arc(50, 50, 6, 0, Math.PI*2);
    ctx.fill();
}

function demo()
{
    canvas = document.createElement("canvas");
    canvas.width = 500;
    canvas.height = 300;
    canvas.ctx = canvas.getContext("2d");

    ctx = canvas.ctx;

    $("#main").append(canvas);

    draw();
}

var timer = 0;
var timerStep = 0.5;
var seconds = 15;

function rotate()
{
    timer += timerStep;

    var cw = canvas.width / 2;
    var ch = canvas.height / 2;

    ctx.setTransform(1, 0, 0, 1, 0, 0);  // reset the transform so we can clear
    ctx.clearRect(0, 0, canvas.width, canvas.height);  // clear the canvas

    createMatrix(cw, ch -50, scale, timer);

    var m = matrix;
    ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);

    draw();

    if(timer <= rotation )
    {
        requestAnimationFrame(rotate);
    }
}

$(document).ready(function()
{
    demo();

    $(canvas).mousedown(function(e)
    {
        painting = true;
        ctx.fillStyle = "#0000FF";

        mouseWorldPos = toWorld(e.pageX, e.pageY);

        lastX = mouseWorldPos.x;
        lastY = mouseWorldPos.y;
    });

    $(canvas).mousemove(function(e)
    {
        $("#screen").text("X: " + e.pageX + " - Y:" + e.pageY);

        mouseWorldPos = toWorld(e.pageX, e.pageY);
        $("#world").text("X: " + mouseWorldPos.x + " - Y:" + mouseWorldPos.y);

        if (painting)
        {
            mouseX = mouseWorldPos.x;
            mouseY = mouseWorldPos.y;

            // find all points between
            var x1 = mouseX,
                x2 = lastX,
                y1 = mouseY,
                y2 = lastY;


            var steep = (Math.abs(y2 - y1) > Math.abs(x2 - x1));
            if (steep){
                var x = x1;
                x1 = y1;
                y1 = x;

                var y = y2;
                y2 = x2;
                x2 = y;
            }
            if (x1 > x2) {
                var x = x1;
                x1 = x2;
                x2 = x;

                var y = y1;
                y1 = y2;
                y2 = y;
            }

            var dx = x2 - x1,
                dy = Math.abs(y2 - y1),
                error = 0,
                de = dy / dx,
                yStep = -1,
                y = y1;

            if (y1 < y2) {
                yStep = 1;
            }

            lineThickness = 5 - Math.sqrt((x2 - x1) *(x2-x1) + (y2 - y1) * (y2-y1))/10;
            if(lineThickness < 1){
                lineThickness = 1;
            }

            for (var x = x1; x < x2; x++) {
                if (steep) {
                    ctx.fillRect(y, x, lineThickness , lineThickness );
                } else {
                    ctx.fillRect(x, y, lineThickness , lineThickness );
                }

                error += de;
                if (error >= 0.5) {
                    y += yStep;
                    error -= 1.0;
                }
            }

            lastX = mouseX;
            lastY = mouseY;
        }
    });

    $(canvas).mouseup(function(e)
    {
        painting = false;
    });

    $("#btnRotate").click(function()
    {
        rotate();
    });
});

CSS

#main{outline: 1px solid orange; display: inline-block; position: relative;}
span{position: relative;}

DEMO

https://jsfiddle.net/mgf8uz7s/

这篇关于在旋转的CANVAS上绘制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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