如何获取 html canvas 标签的坐标
How to get coordinates of html canvas tag
我想获取放置在HTML canvas 标签中的图像的绘制区域的坐标并将其发送到数据库。并将其填充到另一页中该图像的同一区域。以及如何通过单击重置按钮重置或清除绘制区域。
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 :
要实现您要查找的内容,您需要存储(顶部,左侧)位置的最小(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,我们也可以将该数据发送到服务器存储应该不是问题。
我想获取放置在HTML canvas 标签中的图像的绘制区域的坐标并将其发送到数据库。并将其填充到另一页中该图像的同一区域。以及如何通过单击重置按钮重置或清除绘制区域。
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 :
要实现您要查找的内容,您需要存储(顶部,左侧)位置的最小(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,我们也可以将该数据发送到服务器存储应该不是问题。