这段代码实现了一个简单的图像标注工具,允许用户在加载的图像上进行点选标注,并且通过右键确认一个点序列来形成一个多边形。
标注效果如下
实现代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Image Annotation</title>
<style>
canvas {
border: 1px solid black; /* 给画布添加边框 */
}
</style>
</head>
<body>
<!-- 文件输入控件 -->
<input type="file" id="file-input">
<!-- 用于显示图像和标注的画布 -->
<canvas id="image-canvas"></canvas>
<script>
const fileInput = document.getElementById('file-input'); // 获取文件输入元素
const canvas = document.getElementById('image-canvas'); // 获取画布元素
const ctx = canvas.getContext('2d'); // 获取画布的2D渲染上下文
let drawing = false; // 标记是否正在绘图
let ix = -1, iy = -1; // 当前鼠标位置的坐标
let points = []; // 当前正在绘制的点列表
let pointList = []; // 已完成的点列表
// 当选择文件时触发
fileInput.addEventListener('change', function (event) {
const file = event.target.files[0]; // 获取选择的文件
const reader = new FileReader(); // 创建文件读取器
// 文件读取完成后执行
reader.onload = function (event) {
const img = new Image(); // 创建图像对象
img.src = event.target.result; // 将读取的数据赋值给图像源
// 图像加载完成后执行
img.onload = function () {
canvas.width = img.width; // 设置画布宽度为图像宽度
canvas.height = img.height; // 设置画布高度为图像高度
ctx.drawImage(img, 0, 0); // 在画布上绘制图像
};
};
reader.readAsDataURL(file); // 开始读取文件
});
// 右键菜单处理
canvas.addEventListener('contextmenu', (event) => {
event.preventDefault(); // 阻止默认的右键菜单
// 创建自定义菜单
const menu = document.createElement('div');
menu.style.position = 'absolute';
menu.style.left = event.clientX + 'px';
menu.style.top = event.clientY + 'px';
menu.style.backgroundColor = 'white';
menu.style.border = '1px solid black';
// 清空画布菜单项
const clearItem = document.createElement('div');
clearItem.textContent = '清空画布';
clearItem.style.padding = '5px';
clearItem.addEventListener('click', () => {
//ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布
// 恢复绘图状态
ctx.restore();
menu.remove(); // 移除菜单
});
//menu.appendChild(clearItem);
// 复制坐标菜单项
const copyItem = document.createElement('div');
copyItem.textContent = '复制坐标';
copyItem.style.padding = '5px';
copyItem.addEventListener('click', () => {
if (pointList.length > 0) {
navigator.clipboard.writeText("(" + pointList[pointList.length - 1].join('),(') + ")");
alert('坐标已复制到剪贴板');
}
menu.remove(); // 移除菜单
});
//menu.appendChild(copyItem);
document.body.appendChild(menu); // 将菜单添加到页面
return false;
});
// 鼠标按下事件
canvas.addEventListener('mousedown', function (event) {
if (event.button === 0) { // 左键点击
drawing = true;
[ix, iy] = [event.offsetX, event.offsetY]; // 获取鼠标位置
points.push([ix, iy]); // 添加到点列表
drawCircle(ix, iy); // 绘制点
drawText(ix, iy); // 绘制点坐标文本
} else if (event.button === 2) { // 右键点击
if (points.length > 1) {
ctx.beginPath();
ctx.moveTo(points[points.length - 1][0], points[points.length - 1][1]);
ctx.lineTo(points[0][0], points[0][1]);
ctx.strokeStyle = 'green';
ctx.lineWidth = 2;
ctx.stroke();
pointList.push([...points]);
console.log(points);
//navigator.clipboard.writeText("(" + points.join('),(') + ")");
points = [];
}
ctx.drawImage(canvas, 0, 0);
}
});
// 鼠标抬起事件
canvas.addEventListener('mouseup', function (event) {
if (event.button === 0) {
drawing = false;
if (points.length > 1) {
ctx.beginPath();
ctx.moveTo(points[points.length - 2][0], points[points.length - 2][1]);
ctx.lineTo(points[points.length - 1][0], points[points.length - 1][1]);
ctx.strokeStyle = 'green';
ctx.lineWidth = 2;
ctx.stroke();
}
ctx.drawImage(canvas, 0, 0);
}
});
// 鼠标移出画布事件
canvas.addEventListener('mouseout', function () {
drawing = false;
});
// 绘制点函数
function drawCircle(x, y) {
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI * 2);
ctx.fillStyle = 'blue';
ctx.fill();
}
// 绘制点坐标文本函数
function drawText(x, y) {
ctx.font = '16px Arial';
ctx.fillStyle = 'red';
let text = "(" + x + "," + y + ")";
ctx.fillText(text, x, y);
}
</script>
</body>
</html>
代码中还包含了创建右键菜单的功能,但相关的菜单项(清空画布和复制坐标)被注释掉了。如果想启用这些功能,只需取消注释相应的代码行即可。
自己动手实现业务代码,这只是代码片段,具体实现还需要根据业务需要做相应的更改;