【问题标题】:How to get coordinates of html canvas tag如何获取html画布标签的坐标
【发布时间】:2021-04-24 20:57:26
【问题描述】:

我想获取放置在 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>

【问题讨论】:

  • 你需要使用toDataURL()
  • @BurhamB.Soliman 您能否在答案或 JSfiddle 中给出一个工作示例,因为我在该站点中没有找到任何坐标示例。

标签: javascript html canvas coordinates


【解决方案1】:

要实现您要查找的内容,您需要存储(上、左)位置的 min(x, y) 坐标和(下、右)位置的 max(x, y) 坐标,如果您要删除该区域上的绘图,则无法在同一画布上获取图像。使用相对于区域框架的画布具有绝对位置的 HTML 元素,附加一个事件以进行裁剪,并显示另一个目标区域以将其移除。

这是一个包含很多 cmets 的工作示例,应该很清楚。点击“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>

此示例存在一个问题,如果两个区域重叠,则绘图将在两个区域的交叉处被删除,要解决此问题,您需要将每个绘图保留在自己的画布上(...很多工作)。

最后但并非最不重要的一点是,如果您使用像 fabric js 这样的库,您可以轻松实现相同的结果并更好地控制画布及其元素,看看 freedrawing 的这个示例,您会得到免费的绘图坐标,您仍然可以在同一个画布上拥有所有内容(图像或您需要添加到画布的任何内容以及所有没有重叠的绘图......),初始学习曲线可能需要一些时间,但最终,您将对 HTML 画布的整体有更好的理解。

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

【讨论】:

  • 抱歉回复晚了。 fabric js 是满足我所有要求的好选择。我尝试var imgSave = JSON.stringify(canvas); 保存图像和绘图。但是通过使用 canvas.loadFromJSON(); 在另一个页面上的画布上填充它。它给了这个error Uncaught SyntaxError: Unexpected token u in JSON at position 0 at JSON.parse (&lt;anonymous&gt;) at klass.loadFromJSON (fabric.js:13647)你能解决这个问题吗?
  • 抱歉回复晚了...试试var canvasJSON = canvas.toJSON()然后canvas.loadFromJSON(canvasJSON) ...fabricjs.com/docs/fabric.Canvas.html#toJSON)
  • 它返回 [object Object]alert(canvasJSON ) 你能在 jsfiddle 中给出一个工作示例吗?
  • 您在区分 JSON 作为对象和 JSON 作为字符串时遇到问题,请看这里 medium.com/@punitkmr/… .... 这里的任何方式都是一个工作小提琴jsfiddle.net/v9euzpq4/1 ...如果您需要更多帮助,请考虑打开一个新问题,您将获得更多帮助
  • 所以我应该将它作为对象或字符串发送到数据库,例如...$('#sendToDatabaseWithFormSubmit').val(canvas.toJSON())$('#sendToDatabaseWithFormSubmit').val(JSON.stringify(canvas))
【解决方案2】:

编辑:使用基于您的小提琴的工作演示进行更新

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

您现在可以根据需要对其进行投资回报率。


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

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/

【讨论】:

  • 你能举个例子吗,因为它返回(4) [0, 0, 0, 0]console.log(drawedArea );
  • 您好,我已经用一个工作示例更新了我的答案。希望对你有用。
  • 我们如何获得绘制的多个区域的坐标,因为我们可以在这个demo 中获得它。还有Jcrop.js
【解决方案3】:

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

这是一个非常简单的例子。我保持代码最小化以传达我的观点,即您已经拥有所需的一切,所需要的只是将其存储在一个变量中,您可以稍后带回 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>

这里我们在按钮单击上有一个新事件,我正在清除初始画布并将所有坐标绘制到第二个画布上,我们还可以将该数据发送到服务器进行存储,这应该不是问题。

【讨论】:

    【解决方案4】:

    我不知道这是否正是您的想法,但如果您只是希望相同的图像出现在另一个网页上,您可以使用ctx.getImageData() 将画布中的绘图复制为对象,然后将其转换到json,然后将其发送到数据库。然后在另一端,将其变回一个对象并使用ctx.putImageData() 将其放回新画布上。

    【讨论】:

      猜你喜欢
      • 2017-05-28
      • 2022-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多