Draw on rotated CANVAS

I have canvaswhere I can draw with the mouse.

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

I would like to still be able to draw, but the problem I have right now is that the coordinates of the mouse rotate and scale.

What will be the correct way to expand and reveal the coordinates of the mouse so that new drawings are usually displayed from the point of view of the user?

I tried switching the coordinates Xwith Y, but can't get the math correctly.

CODE and 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/

+1
1

, !!

Blindman67, , .

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

https://jsfiddle.net/mgf8uz7s/

0

Source: https://habr.com/ru/post/1016080/


All Articles