2016-09-23 119 views
1

我有一个canvas,我可以用鼠标画画。在旋转的帆布上画画

在某个点上,我可以旋转和缩放画布容器。

我想要的是仍然可以绘制,但我现在有的问题是鼠标坐标旋转和缩放。

什么将unrotate正道unscale鼠标坐标,使新的图纸从用户的角度来看,通常呈现?

我试过切换X坐标与Y,但不能得到正确的数学。

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/

+0

您需要将相同的转换应用于应用于画布的鼠标位置。如果将画布旋转90度,则需要计算补偿的鼠标位置,该位置也旋转了90度。如果画布缩放了1.4,那么鼠标位置需要缩放1.4等。 – jered

+2

您需要应用逆变换矩阵。请参阅此答案。 http://stackoverflow.com/a/34598847/3877726 – Blindman67

+0

@ Blindman67这看起来很有希望!我会测试一下,并回来一段时间 –

回答

0

终于我有这个工作!

所有学分都去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;} 

DEMO

https://jsfiddle.net/mgf8uz7s/