如何使用鼠标在旋转 canvas 上绘图?

How to Draw on rotated canvas using mouse?

我想在 img 上绘制它的工作正常但是当我旋转图像时它的绘图轴完全改变并且它没有在正确的区域绘制

https://codepen.io/ali-shahzil/project/editor/AddPLW

var canvas;
var ctx;
var SCALE_MIN = 1,
  SCALE_MAX = 25;
var currScale = 0; // def pic width=600px, 100px=1scale unit
var xscale = 1.0;
var scaleFactor = 1.00;
var painting = false,
  mark = true,
  lastX = 0,
  lastY = 0,
  lineThickness = 0.3,
  width = 600,
  height = 600;

var img = new Image();
img.src = 'img.JPG';

img.onload = function() {

  canvas = document.getElementById("canvas1"),
    ctx = canvas.getContext("2d");
  canvas.height = height;
  canvas.width = width;
  ctx.drawImage(img, 5, 40, canvas.width, canvas.height);

  canvas = document.getElementById("canvas2"),
    ctx = canvas.getContext("2d");
  canvas.height = height;
  canvas.width = width;
  ctx.drawImage(img, 5, 40, canvas.width, canvas.height);

  canvas = ctx = ''; //reset

}

function doMarking() {
  var checkBox = document.getElementById("mark");

  if (checkBox.checked == true) {
    mark = true;
    if (canvas != null)
      canvas.style.cursor = "pointer";
    //currImgId = '';
  } else {
    mark = false;
    if (canvas != null);
    canvas.style.cursor = "";
  }

  lastX = 0,
    lastY = 0,
    painting = false;

}

function mouseDown(e) {

  if (!mark)
    return;
  painting = true;
  ctx.fillStyle = "#ffffff";
  lastX = e.pageX - (e.target).offsetLeft;
  lastY = e.pageY - (e.target).offsetTop;
  //Calculating the scale how much it increase
  var rect = canvas.getBoundingClientRect(); // abs. size of element
  scaleX = canvas.width / rect.width; // relationship bitmap vs. element for X
  scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
  lastX = lastX * scaleX;
  lastY = lastY * scaleY;

  //console.log('Before lasX=' + lastX + ' lastY=' + lastY+',currScale='+currScale);

  //lastX=transformSimple(lastX);
  // lastY=transformSimple(lastY);

  //console.log('After lasX=' + lastX + ' lastY=' + lastY+', currScale='+currScale);
  //console.log('offleft=' + (e.target).offsetLeft + ', offsetTop=' + (e.target).offsetTop);
  // console.log('e=' + e);
}
/*
        canvas1.onmousedown=function (e) {
            console.log('mousedown2 id=' + e);
            if (!mark)
                return;
            painting = true;
            ctx.fillStyle = "#ffffff";
            lastX = e.pageX - this.offsetLeft;
            lastY = e.pageY - this.offsetTop;
    
            console.log('lasX=' + lastX + ' lastY=' + lastY);
        }
        */
function mouseUp(e) {

  if (!mark)
    return;

  painting = false;
}

function mouseMove(e) {
  if (!mark)
    return;
  if (painting) {
    mouseX = e.pageX - (e.target).offsetLeft;
    mouseY = e.pageY - (e.target).offsetTop;
    //Calculating the scale how much it increase
    var rect = canvas.getBoundingClientRect(); // abs. size of element
    scaleX = canvas.width / rect.width; // relationship bitmap vs. element for X
    scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
    mouseX = mouseX * scaleX;
    mouseY = mouseY * scaleY;

    //    mouseX=transformSimple(mouseX);
    //    mouseY=transformSimple(mouseY);

    //console.log('mx=' + mouseX + ', my=' + mouseY);
    // 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) {

        // translate(y,x);

        ctx.fillRect(y, x, lineThickness, lineThickness);
        //ctx.fillRect(transformSimple(y), transformSimple(x), lineThickness, lineThickness);


      } else {
        ctx.fillRect(x, y, lineThickness, lineThickness);


      }

      error += de;
      if (error >= 0.5) {
        y += yStep;
        error -= 1.0;
      }
      //  console.log('x=' + x + ', y=' + y );
    }



    lastX = mouseX;
    lastY = mouseY;

  }
}

/*
       canvas.addEventListener('click', function (event) {
    
           if (mark)
               return;
           //get the img of convas
           
       });*/
//-------------- img zooms



function zoomin_canvas() {

  if (canvas != null) {

    var currWidth = canvas.clientWidth;
    //console.log('zoomin currWidth='+currWidth);
    if (currWidth >= 1500) return false;
    else {
      canvas.style.width = (currWidth + 100) + "px";


      //if (currScale < SCALE_MAX)
      //  currScale++;


      //console.log('scale=' + currScale);
    }
  }
}


function zoomout_canvas() {
  if (canvas != null) {
    var currWidth = canvas.clientWidth;

    if (currWidth <= 100) return false;
    else {
      canvas.style.width = (currWidth - 100) + "px";

      //if (currScale > SCALE_MIN)
      //  currScale--;


      //console.log('scale=' + currScale);
    }
  }
}

var currImgId = null;

function selectImg(e) {

  if (currImgId != null) {
    document.getElementById(currImgId).style.border = "none";

  }

  e.target.style.border = "2px solid orange";
  currImgId = (e.target).getAttribute('id');

  if (typeof canvas !== 'undefined') {
    // the variable is defined

    canvas = e.target;
    ctx = canvas.getContext("2d");
  }
  //ctx.drawImage(img, 5, 40, canvas.width, canvas.height);

}

function rotate() {

  if (currImgId != null) {
    document.getElementById(currImgId).setAttribute("class", "rotated-image");
  }
}
var degrees = 0;

function rotateRight() {
  console.log('currimgid=' + currImgId);
  var img = document.getElementById(currImgId);
  degrees = parseInt(img.getAttribute("rotate"));
  degrees = (degrees + 90) % 360;
  img.style.setProperty('-ms-transform', 'rotate(' + degrees + 'deg)');
  img.style.setProperty('-webkit-transform', 'rotate(' + degrees + 'deg)');
  img.style.setProperty('transform', 'rotate(' + degrees + 'deg)');
  img.setAttribute("rotate", degrees);
}

function rotateLeft() {
  var img = document.getElementById(currImgId);
  degrees = parseInt(img.getAttribute("rotate"));
  degrees = (degrees - 90) % 360;
  img.style.setProperty('-ms-transform', 'rotate(' + degrees + 'deg)');
  img.style.setProperty('-webkit-transform', 'rotate(' + degrees + 'deg)');
  img.style.setProperty('transform', 'rotate(' + degrees + 'deg)');
  img.setAttribute("rotate", degrees);
}

function translate(X, Y) {

  console.log('untransformed x=' + X + ', y=' + Y);
  // const point = {x: 0, y: 0};
  const matrix = ctx.getTransform();
  const transformedPoint = {
    x: matrix.a * X + matrix.c * Y + matrix.e,
    y: matrix.b * X + matrix.d * Y + matrix.f,
  };
  console.log('transformed x=' + transformedPoint.x + ', y=' + transformedPoint.y);

}

function translateSimple(X, Y) {

  //console.log('scalefactor='+scaleFactor);
  console.log('untransformed x=' + X + ', y=' + Y);

  if (scaleFactor >= 1.0)
    console.log('transformed x=' + X / scaleFactor + ', y=' + Y / scaleFactor);
  else
    console.log('transformed x=' + X * scaleFactor + ', y=' + Y * scaleFactor);

}

function transformSimple(a) {

  //return (parseInt(a/(scaleFactor*scaleFactor)));

  if (currScale == 0)
    return (a);
  else

    return (a - 16 * (currScale));

}

function draw() {

  for (var x = 100; x < 102; x++)
    ctx.fillRect(100, x, 4.9, 4.9);

}
.main_bottom {
  background-color: #e8e9eb;
  display: flex;
  align-items: center;
  justify-content: space-around;
  border: 10px solid #e8e9eb;
  border-top: 30px solid #e8e9eb;
  height: 90vh;
}

form {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row;
  margin-bottom: 5px;
}

.scrollit {
  /*overflow-y: auto;*/
  /* overflow-y: scroll;*/
  height: 300px;
  overflow-x: hidden;
  overflow-y: auto;
}

.first {
  display: flex;
  flex-direction: row;
  visibility: hidden;
}

.submit {
  display: flex;
  flex-direction: row;
}

img {
  width: 100%;
  max-width: 800px;
  height: auto;
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.fix {
  height: 300px;
  margin-top: 200px;
}

body,
html {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#container {
  width: 100%;
  height: 100%;
}

#left_panel {
  display: flex;
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 700px;
  background-color: white;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
  flex-direction: column;
  overflow: scroll;
}

#right_panel {
  display: flex;
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 700px;
  margin-right: 15px;
  background-color: white;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
  flex-direction: column;
  overflow: scroll;
}

#drag {
  position: absolute;
  left: -4px;
  top: 0;
  bottom: 0;
  width: 8px;
  cursor: w-resize;
}


/*img zooms*/

#navbar {
  overflow: hidden;
  background-color: #099;
  position: fixed;
  top: 0;
  width: 100%;
  padding-top: 3px;
  padding-bottom: 3px;
  padding-left: 20px;
}

#navbar a {
  float: left;
  display: block;
  color: #666;
  text-align: center;
  padding-right: 20px;
  text-decoration: none;
  font-size: 17px;
}

#navbar a:hover {
  background-color: #ddd;
  color: black;
}

#navbar a.active {
  background-color: #4caf50;
  color: white;
}

.main {
  padding: 16px;
  margin-top: 30px;
  width: 100%;
  height: 100vh;
  overflow: auto;
  cursor: grab;
  cursor: -o-grab;
  cursor: -moz-grab;
  cursor: -webkit-grab;
}

.main img {
  height: auto;
  width: auto;
}

.button {
  width: 300px;
  height: 60px;
}


/*---- toggle switch*/

.switch {
  position: relative;
  display: inline-block;
  width: 30px;
  height: 17px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  -webkit-transition: 0.4s;
  transition: 0.4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 13px;
  width: 13px;
  left: 4px;
  bottom: 2px;
  background-color: white;
  -webkit-transition: 0.4s;
  transition: 0.4s;
}

input:checked+.slider {
  background-color: #2196f3;
}

input:focus+.slider {
  box-shadow: 0 0 1px #2196f3;
}

input:checked+.slider:before {
  -webkit-transform: translateX(13px);
  -ms-transform: translateX(13px);
  transform: translateX(13px);
}


/* Rounded sliders */

.slider.round {
  border-radius: 17px;
}

.slider.round:before {
  border-radius: 50%;
}

.both {
  margin-top: 50px;
  display: flex;
  justify-content: center;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="./styles.css">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

  <title>Order by Picture</title>
</head>


<body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
  <div id="navbar">
    <button type="button" onclick="zoomin_canvas()">+</button>
    <button type="button" onclick="zoomout_canvas()">-</button>
    <button id="rotateRight" onclick="rotateRight()">Right</button>
    <button id="rotateLeft" onclick="rotateLeft()">Left</button>
    <button id="Button1" onclick="draw()">draw</button
     <label> Marking</label>
     <label class="switch">
        
  <input type="checkbox" checked onclick="doMarking()" id="mark">
  <span class="slider round"></span>
</label>
  </div>

<div class="both">
<div class="canvas1">
<canvas id="canvas1" rotate="0" onclick="selectImg(event)" onmousedown="mouseDown(event)" onmouseup="mouseUp(event)" onmousemove="mouseMove(event)">
Your browser does not support the HTML5 canvas tag.
</canvas>
</div>
<div class="canvas2">
 <canvas id="canvas2" rotate="0" onclick="selectImg(event)" onmousedown="mouseDown(event)" onmouseup="mouseUp(event)" onmousemove="mouseMove(event)">
Your browser does not support the HTML5 canvas tag.
</canvas>
</div>
</div>
    </body>
   


    </html>

不要旋转 canvas

在 canvas 上绘制旋转图像,而不是旋转 canvas。

当你在 canvas 上绘制图像时,旋转它,然后你可以使用正常的鼠标坐标在它上面绘制。

在 canvas

上旋转图像

下面的代码将绘制旋转任意量的图像。如果需要,图像将按比例缩小以确保它适合 canvas。该功能将图像旋转任何你想要的角度。

// ctx is canvas 2D context
// deg in degrees rotated CW from 3 O-clock
// img to render. NOTE image must be loaded first
function drawRotatedImage(ctx, deg, img) { 
    const w = img.naturalWidth;
    const h = img.naturalHeight;
    const cw = ctx.canvas.width;
    const ch = ctx.canvas.height;
    
    // convert deg to radians
    const rad = deg * Math.PI / 180;
    
    // Get vector for rotated xAxis ax, ay. With aax, aay in first quadrant
    const ax = Math.cos(rad), aax =  Math.abs(ax);
    const ay = Math.sin(rad), aay =  Math.abs(ay);

    // get the rotated width and height of image
    const tw = aax * w + aay * h;
    const th = aay * w + aax * h;
    
    // get scale so that image fits the canvas. Dont enlarge only reduce if to big
    const scale = Math.min(1, cw / tw, ch / th);

    // set canvas transform to center of canvas, rotated and scaled to fit
    ctx.setTransform(ax * scale, ay * scale, -ay * scale, ax * scale, cw / 2, ch / 2);
    
    // draw image on canvas offset by half its width and height
    ctx.drawImage(img, -w / 2, -h / 2);
    
    // restore canvas transform to default
    ctx.setTransform(1, 0, 0, 1, 0, 0); 
}

例子

示例使用上述函数将图像渲染到 canvas,然后使用鼠标在图像上绘制。点击旋转旋转图像。

const ctx = canvas.getContext("2d");
const mouse = {x: 0, y: 0, b: false, ox: 0, oy: 0};
var rot = 90;
const img = new Image;
img.src = "https://i.stack.imgur.com/C7qq2.png?s=420&g=1";
img.onload = () => rotImage(ctx, rot, img);
resize();
addEventListener("resize", resize);
rotBtn.addEventListener("click", () => img.complete && rotImage(ctx, rot += 90, img));
addEventListener("mousemove", mouseEvent);
addEventListener("mousedown", mouseEvent);
addEventListener("mouseup", mouseEvent);
addEventListener("mouseout", mouseEvent);
function resize() {
    canvas.width = innerWidth;
    canvas.height = innerHeight;
    ctx.lineWidth = 3;
    ctx.lineCap = "round";
    ctx.strokeStyle = "#00F";
    img.complete && rotImage(ctx, rot, img);
}
function mouseEvent(e) {
    mouse.ox = mouse.x;
    mouse.oy = mouse.y;
    mouse.x = e.pageX;
    mouse.y = e.pageY;
    if (e.type === "mousedown") { mouse.b = true }
    else if (e.type === "mouseup" || e.type === "mouseout") { mouse.b = false }     
    mouse.b && drawWithMouse(ctx, mouse);
}
function drawWithMouse(ctx, mouse) {
    ctx.beginPath();
    ctx.lineTo(mouse.ox, mouse.oy);
    ctx.lineTo(mouse.x, mouse.y);
    ctx.stroke();
}
function rotImage(ctx, deg, img) { 
    const cw = ctx.canvas.width, ch = ctx.canvas.height;       
    const w = img.naturalWidth, h = img.naturalHeight;
    const rad = deg * Math.PI / 180;
    const ax = Math.cos(rad), aax =  Math.abs(ax);
    const ay = Math.sin(rad), aay =  Math.abs(ay);
    const tw = aax * w + aay * h;
    const th = aay * w + aax * h;
    const sc = Math.min(1, cw / tw, ch / th);
    ctx.clearRect(0, 0, cw, ch);
    ctx.setTransform(ax * sc, ay * sc, -ay * sc, ax * sc, cw / 2, ch / 2);
    ctx.drawImage(img, -w / 2, -h / 2);
    ctx.setTransform(1, 0, 0, 1, 0, 0); 
}
* {font-family: arial;}
button { position: absolute; top: 10px; left: 10px }
canvas { position: absolute; top: 0px; left: 0px }
<canvas id="canvas"></canvas>   
<button id="rotBtn">Rotate</button>