如何获取 html canvas 标签的坐标

How to get coordinates of html canvas tag

我想获取放置在HTML canvas 标签中的图像的绘制区域的坐标并将其发送到数据库。并将其填充到另一页中该图像的同一区域。以及如何通过单击重置按钮重置或清除绘制区域。

-------------------------------------------------------JSfiddle example----------------------------------------------------

var canvas = document.getElementById("canvas");
var img = document.getElementById("imagearea"),
    ctx = canvas.getContext("2d"),
    painting = false,
    lastX = 0,
    lastY = 0,
    lineThickness = 1;

canvas.width = canvas.height = 600;
ctx.fillRect(0, 0, 600, 600);
ctx.drawImage(img, 10, 10);

canvas.onmousedown = function(e) {
    painting = true;
    ctx.fillStyle = "#ff0000";
    lastX = e.pageX - this.offsetLeft;
    lastY = e.pageY - this.offsetTop;
};

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

canvas.onmousemove = function(e) {
    if (painting) {
        mouseX = e.pageX - this.offsetLeft;
        mouseY = e.pageY - this.offsetTop;

        // 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 id="canvas">
</canvas>
<img id="imagearea" src="https://media.istockphoto.com/photos/green-apple-with-leaf-and-cut-isolated-on-white-picture-id1141004606?k=6&m=1141004606&s=170667a&w=0&h=zwbN4lLc7MFb6f_aZ4npNL3i4Tgde-yINlYTztlI1QQ=" style="display: none;" />

<button> Reset </button>

编辑:使用基于您的 fiddle

的工作演示进行更新

您可能需要调整此功能以包括绘制线条的粗细(它们可能出现在注册区域之外)。 但是像这样,你有你的位置和绘制区域的大小。

如果需要,您现在可以对其进行投资回报。


您可以跟踪使用如下函数绘制的区域:

var drawedArea = [0,0,0,0];
function drawedAreaTrack(x, y) {
  // top left x
  if (drawedArea[0] === 0) {
  drawedArea[0] = x;
  } else {
  drawedArea[0] = Math.min(drawedArea[0], x);
  }
  

  // top left y
   if (drawedArea[1] === 0) {
  drawedArea[1] = y;
  } else {
  drawedArea[1] = Math.min(drawedArea[1], y);
  }

  // bottom right x
  drawedArea[2] = Math.max(drawedArea[2], x);

  // bottom right y
  drawedArea[3] = Math.max(drawedArea[3], y);
  
  console.log(drawedArea);
}

您可以使用这两个点来获得绘制的总面积。

这是一个工作示例-> Fiddle :

http://jsfiddle.net/b90h6gaq/8/

要实现您要查找的内容,您需要存储(顶部,左侧)位置的最小(x,y)坐标和(底部,右侧)位置的最大(x,y)坐标位置,如果您要删除该区域的绘图,则无法在同一 canvas 上获取图像。使用相对于 canvas 绝对位置的 HTML 元素作为区域框架,将事件附加到裁剪,并显示另一个目标区域以将其删除。

这是一个工作示例,有很多注释,应该很清楚。点击区域显示预览在“x”上去掉​​随绘图,可以处理多个区域。

const source = "https://media.istockphoto.com/photos/green-apple-with-leaf-and-cut-isolated-on-white-picture-id1141004606?k=6&m=1141004606&s=170667a&w=0&h=zwbN4lLc7MFb6f_aZ4npNL3i4Tgde-yINlYTztlI1QQ=";
const container = document.querySelector("#container");
const canvas = container.querySelector("canvas");
const ctx = canvas.getContext("2d");
const resetButton = document.querySelector("button");

let lastDrawnArea = [[Infinity, Infinity], [0, 0]];
let image;
let painting = false;
let lastX = 0;
let lastY = 0;
let lineThickness = 1;

init();

async function init() {

  await loadDrawImage();
  
  // Start Event Listening
  canvas.onmousedown = function(e) {
    painting = true;
    ctx.fillStyle = "#ff0000";
    lastX = e.pageX - this.offsetLeft;
    lastY = e.pageY - this.offsetTop;
  };

  canvas.onmouseup = function(e){
      painting = false;
      // Set the drawing area  
      setDrawingArea();
  }

  canvas.onmousemove = function(e) {
      if (painting) {
          mouseX = e.pageX - this.offsetLeft;
          mouseY = e.pageY - this.offsetTop;

          // 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;
          
          // Set The min, max coordinate of the current drawing 
          // to define the current drawing area
          lastDrawnArea = [
            [// Top left min([x, y]) coords
              Math.min(lastDrawnArea[0][0], mouseX),
              Math.min(lastDrawnArea[0][1], mouseY)
            ],
            [// Bottom right max([x, y]) coords
              Math.max(lastDrawnArea[1][0], mouseX),
              Math.max(lastDrawnArea[1][1], mouseY)
            ]
          ]
      }
  }
}

async function loadDrawImage() {
  image = new Image();
  
  // Load the image
  await new Promise(resolve => {
    image.onload = resolve;
    image.src = source;
  });

  const [width, height] = [image.naturalWidth, image.naturalHeight];

  // Set the container and canvas size
  container.style.width = `${width}px`;
  container.style.height = `${height}px`;
  canvas.width = width;
  canvas.height = height;
  
  // Set the container in the background
  container.style.background = `url(${image.src})`;
}

function setDrawingArea(){
  const [TOP_LEFT, BOTTOM_RIGHT, X, Y] = [0, 1, 0, 1];
  const container = document.querySelector("#container");
  const template = document.querySelector("#areaTemplate");
  const area = template.content.firstElementChild.cloneNode(true);
  
  // You should replace this with the lineThickness 
  const offset = 10;
  
  // Get the area size
  const width = lastDrawnArea[BOTTOM_RIGHT][X] - lastDrawnArea[TOP_LEFT][X];
  const height = lastDrawnArea[BOTTOM_RIGHT][Y] - lastDrawnArea[TOP_LEFT][Y];
  
  area.style.left = `${lastDrawnArea[TOP_LEFT][X] - offset}px`;
  area.style.top = `${lastDrawnArea[TOP_LEFT][Y] - offset}px`;
  area.style.width = `${width + (offset * 2)}px`;
  area.style.height = `${height + (offset * 2)}px`;

  // Draw the template
  container.append(area);
  
  // Add the events
  area.onclick = previewArea; // Preveiw event
  area.querySelector("b").onclick = removeArea; // Remove event
  
  // Reset "lastDrawnArea" value
  lastDrawnArea = [[Infinity, Infinity], [0, 0]];  
  
}

function previewArea(e) {
  const preview = document.querySelector("#preview");
  const previewCanvas = preview.querySelector("canvas");
  const previewCtx = previewCanvas.getContext("2d");
  
  // Get the drawing area coords
  const area = e.target;
  const [x, y, width, height] = [
    parseFloat(area.style.left),
    parseFloat(area.style.top),
    parseFloat(area.style.width),
    parseFloat(area.style.height)
  ];
  
  // Draw the preview area
  previewCanvas.width = width;
  previewCanvas.height = height;
  previewCtx.drawImage(image,
    x, y, width, height,
    0, 0, width, height);
}

function removeArea(e) {
  const area = e.target.parentElement;
  const [x, y, width, height] = [
    parseFloat(area.style.left),
    parseFloat(area.style.top),
    parseFloat(area.style.width),
    parseFloat(area.style.height)
  ];
  
  ctx.clearRect(x, y, width, height);
  
  area.remove();
}

resetButton.onclick = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  document.querySelectorAll('.area').forEach(el => el.remove());
}
body {
  display: flex;
}

#container {
  position: relative;
}

.area {
  position: absolute;
  border: 2px solid #333;
  color: #333;
  cursor: pointer;
}

.area b {
  position: absolute;
  right: 0;
  top: 0;
  transform: translate(100%, -100%);
  color: red;
  cursor: pointer;
}
<div id="container">
  <canvas></canvas>
</div>

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

<template id="areaTemplate">
  <div class="area">
    <b>x</b>
  </div>
</template>

<button> Reset All </button>

如果两个区域重叠,则此示例存在一个问题,绘图将在两个区域的交集处被删除,要解决此问题,您需要将每个绘图单独保存 canvas(...很多工作)。

最后但同样重要的是,如果使用像 fabric js, take a look at this example of freedrawing 这样的库,您可以轻松获得相同的结果并更好地控制 canvas 及其元素,您将获得绘图坐标免费,并且您仍然可以在相同的 canvas 上拥有所有内容(图像或您需要添加到 canvas 的任何内容以及所有没有重叠的绘图......),初始学习曲线可能需要一些时间,但到一天结束时,您将更好地了解 HTML canvas 整体。

旁注:您使用的图像有跨源限制,您应该使用来自同一域或允许跨源的域的图像。

我不知道这是否正是您所想的,但是如果您只是想让同一张图片出现在另一个网页上,您可以使用 ctx.getImageData() 从 [=14] 复制绘图=]作为一个对象,将其转换为json,然后将其发送到数据库。然后在另一端,将其变回对象并使用 ctx.putImageData() 将其放回新的 canvas.

'onmousemove' 正在完成所有绘图,我会将您需要的所有内容保存到一个数组中,然后在某些事件发送到数据库或绘制到另一个 canvas...

这是一个非常简单的例子。我将代码保持最少以传达我的观点,即您已经拥有了所需的一切,所需要的只是将其存储在变量中,您可以带回 lineThickness,稍后的其余计算和逻辑,不应该是个问题。

var button = document.getElementById("btn");
var canvas = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");

var ctx = canvas.getContext("2d");
var ctx2 = canvas2.getContext("2d");

var painting = false;
var coordinates = [];

canvas.onmousedown = function(e) {painting = true;}
canvas.onmouseup = function(e) {painting = false;}

canvas.onmousemove = function(e) {
  if (painting) {
    x = e.pageX - this.offsetLeft;
    y = e.pageY - this.offsetTop;
    coordinates.push({x, y})
    ctx.fillRect(x, y, 5, 5);
  }
}

button.onmousedown = function(e) {
  ctx.clearRect(0, 0, 300, 150);
  coordinates.forEach(coord => {
    ctx2.fillRect(coord.x, coord.y, 5, 5);
  });
};
<canvas id="canvas1" width=300 height=150></canvas>
<button id="btn"> Reset </button>
<canvas id="canvas2" width=300 height=150></canvas>

这里我们有一个关于按钮点击的新事件,我正在清除初始 canvas 并将所有坐标绘制到第二个 canvas,我们也可以将该数据发送到服务器存储应该不是问题。