js-demo源码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>mark-board</title>
<script src="https://www.unpkg.com/canvas-mark-board@0.0.1-beta.6/dist/index.umd.js"></script>
</head>
<style>
* {
box-sizing: border-box;
}
.remark {
position: relative;
}
.remark:hover div {
display: block;
}
.remark div {
background: #fff;
position: absolute;
display: none;
z-index: 999;
}
#mark-list {
height: 100%;
width: 200px;
overflow: auto;
}
.mark-board-box {
display: flex;
height: calc(100vh - 200px);
}
.mark-operate svg {
width: 24px;
height: 24px
}
</style>
<body style="width:80%;margin: 0 auto;max-width: 1440px;">
<h3 style="margin: 0;">JS canvas-mark-board Demo:</h3>
<div>
<div class="mark-operate">
<span class="mark-shape-list"></span>
<button id="upload">上传图片</button>
<button id="clear">清空画布</button>
<button id="importJson">导入JSON</button>
<button id="creact">创建</button>
<button id="destroy"> 销毁</button>
标签名:<input id="labelInput" type="text" value="person" />
颜色:<input id="colorInput" type="color" value="#ff0000" />
<a class="remark">
操作说明?
<div>
<b>画布操作</b>:1.双击鼠标恢复大小; 2.ctrl或cmd或alt+滚轮缩放;
3.按住空格拖动画布
<br />
<b>多边形绘制</b>:1.右键删除最后一个点;
2.点击第一个点或者按回车完成绘制
<br />
<b>其他操作</b>:1.可以点击图形然后拖动,调整点拖动改变大小;
2.按Devare键删除对象
<br />
</div>
</a>
</div>
</div>
<div class="mark-board-main">
<div class="mark-board-box">
<div style="flex:1">
<div id="mark-box" style="width:100%;height:100%;border: 1px solid black;"></div>
</div>
<div id="mark-list">
</div>
</div>
</div>
<textarea style="width:100%;height:100%;" id="textarea" cols="30" rows="10"></textarea>
<!-- 链接自己的js(也就是右边标签的js) -->
<script src="./test.js"></script>
</body>
</html>
js
var json = [
{
id: "f9e1cf23-8903-4f40-8ff9-724363190272",
label: "person",
type: "polygon",
pointList: [
{
x: 257.7016248153619,
y: 189.06942392909895,
},
{
x: 633.0044313146234,
y: 225.93796159527326,
},
{
x: 257.7016248153619,
y: 260.9158050221566,
},
],
},
];
var shapeMap = [
{
viewBox: "0 0 1024 1024",
type: "rect",
icon: "M904.50393 795.651268V225.084385c32.463366-10.869558 55.862285-41.503253 55.862286-77.623915 0-45.212738-36.647665-81.863473-81.864496-81.863473-35.721573 0-66.084091 22.890336-77.262688 54.798047H226.150158c-11.177574-31.907711-41.538045-54.79907-77.262688-54.79907-45.213761 0-81.864496 36.651758-81.864496 81.864496 0 35.530215 22.63758 65.76175 54.271045 77.086679v571.642379c-31.633465 11.32493-54.271044 41.555441-54.271045 77.084632 0 45.212738 36.652781 81.864496 81.864496 81.864496 34.495652 0 63.98222-21.342074 76.036767-51.532677h577.543786c12.05557 30.190603 41.546232 51.532677 76.035743 51.532677 45.217854 0 81.864496-36.651758 81.864496-81.864496-0.002047-36.120662-23.400966-66.753333-55.864332-77.622892z m-105.182574 56.789401H228.067834c-7.227612-27.548426-28.447912-49.415456-55.609529-57.570183V225.865167c26.077935-7.829316 46.683229-28.300556 54.682414-54.305836H800.249495c7.835456 25.473161 27.768437 45.63434 53.090149 53.80851V795.367813c-26.39516 8.519024-46.930869 30.065759-54.018288 57.072856z",
},
{
viewBox: "0 0 1024 1024",
type: "polygon",
icon: "M680.71 915.22H343.29c-39.53 0-76.37-21.27-96.12-55.52L78.46 567.49c-19.75-34.22-19.75-76.77 0-110.99l168.71-292.2c19.75-34.24 56.6-55.52 96.12-55.52h337.42c39.53 0 76.37 21.27 96.12 55.52l168.71 292.2c19.75 34.22 19.75 76.77 0 110.99L776.83 859.7c-19.75 34.24-56.59 55.52-96.12 55.52zM343.29 175.69c-15.7 0-30.35 8.43-38.21 22.04l-168.71 292.2c-7.84 13.61-7.84 30.52 0 44.13l168.71 292.2c7.86 13.61 22.51 22.04 38.21 22.04h337.42c15.7 0 30.35-8.43 38.21-22.04l168.71-292.2c7.84-13.61 7.84-30.52 0-44.13l-168.71-292.2c-7.86-13.61-22.51-22.04-38.21-22.04H343.29z",
},
{
viewBox: "0 0 1024 1024",
type: "circle",
icon: "M512 928C282.624 928 96 741.376 96 512S282.624 96 512 96s416 186.624 416 416-186.624 416-416 416z m0-768C317.92 160 160 317.92 160 512s157.92 352 352 352 352-157.92 352-352S706.08 160 512 160z",
},
{
viewBox: "0 0 1024 1024",
type: "ellipse",
icon: "M512 838.656c-228.864 0-415.232-146.432-415.232-326.656 0-180.224 186.368-326.656 415.232-326.656 228.864 0 415.232 146.432 415.232 326.656 0 180.224-186.368 326.656-415.232 326.656z m0-589.312c-194.048 0-351.232 117.76-351.232 262.656 0 144.896 157.696 262.656 351.232 262.656s351.232-117.76 351.232-262.656c0-144.896-157.184-262.656-351.232-262.656z",
},
{
viewBox: "0 0 1024 1024",
type: "line",
icon: "M960 470.857143H64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v64c0 5.028571 4.114286 9.142857 9.142857 9.142857h896c5.028571 0 9.142857-4.114286 9.142857-9.142857v-64c0-5.028571-4.114286-9.142857-9.142857-9.142857z",
},
{
viewBox: "0 0 1024 1024",
type: "polyline",
icon: "M221.342 686.08l-48.837-33.083 215.04-317.44 184.32 239.065L764.849 335.95l45.686 37.021-239.458 296.96-180.382-233.55L221.342 686.08z",
},
{
viewBox: "0 0 1024 1024",
type: "line_arrow",
icon: "M593.066667 793.642667a32.170667 32.170667 0 0 1 0-45.226667l236.373333-236.373333L593.066667 275.626667a32 32 0 0 1 45.226666-45.184l258.986667 258.986666a32.170667 32.170667 0 0 1 0 45.226667l-258.986667 258.986667a31.914667 31.914667 0 0 1-45.226666 0z M149.333333 544a32.213333 32.213333 0 0 1-32-32 32.213333 32.213333 0 0 1 32-32h718.08a32.213333 32.213333 0 0 1 32 32 32.213333 32.213333 0 0 1-32 32z",
},
{
viewBox: "0 0 480 480",
type: "sides_arrow",
icon: " M20 260 H460 V230 H20Z M225 85 V400 H255 V115Z M170 170 L240 100 L220 80 L150 150Z M310 170 L220 80 L240 60 L330 150Z",
},
{
viewBox: "0 0 1024 1024",
type: "polyline_arrow",
icon: "M593.066667 793.642667a32.170667 32.170667 0 0 1 0-45.226667l236.373333-236.373333L593.066667 275.626667a32 32 0 0 1 45.226666-45.184l258.986667 258.986666a32.170667 32.170667 0 0 1 0 45.226667l-258.986667 258.986667a31.914667 31.914667 0 0 1-45.226666 0z M149.333333 544a32.213333 32.213333 0 0 1-32-32 32.213333 32.213333 0 0 1 32-32h718.08a32.213333 32.213333 0 0 1 32 32 32.213333 32.213333 0 0 1-32 32z",
},
{
viewBox: "0 0 24 24",
type: "triangle",
icon: "M12 7.77L18.39 18H5.61L12 7.77M12 4L2 20h20L12 4z",
},
];
const { ClickMarkObject, MoveMarkObject, MarkBoardUtils } = CanvasMarkBoard;
/** 自定义带方向的线 */
class MarkSidesArrowObject extends MoveMarkObject {
constructor(box) {
super(box);
this.type = "sides_arrow";
}
setMoveEdit() {
this.pointList[this.acctivePointIndex] = {
x: this.lastMousePoint.x,
y: this.lastMousePoint.y,
};
}
isPointInside(point) {
let expand = this.expent / this.box.t.a;
let offset = MarkBoardUtils.isPointInPolygon(point, this.pointList);
return offset < expand;
}
get pathData() {
let path = ``;
if (
this.pointList.length === 2 &&
this.pointList[0].x != this.pointList[1].x &&
this.pointList[0].y != this.pointList[1].y
) {
path += `M${this.pointList[0].x},${this.pointList[1].y}`;
path += `L${this.pointList[0].x},${this.pointList[1].y}`;
const [side1, side2] = MarkBoardUtils.getSides(
this.pointList[0],
this.pointList[1]
);
const [arrow1, arrow2] = MarkBoardUtils.getArrow(
side2,
side1,
20 / this.box.t.a
);
path += `Z`;
path += `M${side2.x},${side2.y}`;
path += `L${side1.x},${side1.y}`;
// 绘制三角形
path += `Z`;
path += `M${arrow1.x},${arrow1.y}`;
path += `L${side1.x},${side1.y}`;
path += `M${side1.x},${side1.y}`;
path += `L${arrow2.x},${arrow2.y}`;
path += `Z`;
// 连接到原来位置
path += `M${this.pointList[0].x},${this.pointList[0].y}`;
path += `L${this.pointList[1].x},${this.pointList[1].y}`;
path += `L${this.pointList[0].x},${this.pointList[0].y} `;
path += `L${this.pointList[this.pointList.length - 1].x},${
this.pointList[this.pointList.length - 1].y
} `;
path += `Z `;
}
return path;
}
}
/** 自定义多线段箭头 */
class MarkPolylineArrowObject extends ClickMarkObject {
constructor(box) {
super(box);
this.type = "polyline_arrow";
}
/** 获取path */
get pathData() {
let path = ``;
if (this.pointList.length) {
this.pointList.forEach((point, index) => {
if (index === 0) {
path += `M${point.x},${point.y}`;
} else {
path += `L${point.x},${point.y}`;
}
});
if (this.pointList.length >= 2) {
for (let i = 0; i < this.pointList.length - 1; i++) {
const [arrow1, arrow2] = MarkBoardUtils.getArrow(
this.pointList[i],
this.pointList[i + 1],
20 / this.box.t.a
);
path += `M${arrow1.x},${arrow1.y}`;
path += `L${this.pointList[i + 1].x},${this.pointList[i + 1].y}`;
path += `M${this.pointList[i + 1].x},${this.pointList[i + 1].y}`;
path += `L${arrow2.x},${arrow2.y}`;
path += `M${this.pointList[i + 1].x},${this.pointList[i + 1].y}`;
}
}
}
return path;
}
}
/** 自定义三角形 */
class MarkTriangleObject extends MoveMarkObject {
constructor(box) {
super(box);
this.type = "triangle";
}
setMoveEdit(offset) {
if (this.acctivePointIndex == 0) {
this.pointList[0] = {
x: this.pointList[0].x + offset.x,
y: this.pointList[0].y + offset.y,
};
} else if (this.acctivePointIndex === 1) {
this.pointList[1] = {
x: this.pointList[1].x + offset.x,
y: this.pointList[1].y + offset.y,
};
} else if (this.acctivePointIndex === 2) {
this.pointList[0] = {
x: this.pointList[0].x + offset.x,
y: this.pointList[0].y,
};
this.pointList[1] = {
x: this.pointList[1].x,
y: this.pointList[1].y + offset.y,
};
}
}
/** 获取path */
get pathData() {
let path = ``;
if (this.vertexList.length) {
this.vertexList.forEach((point, index) => {
// 绘制线段
if (index === 0) {
path += `M${point.x},${point.y}`;
} else {
path += `L${point.x},${point.y}`;
}
});
path += `Z `;
}
return path;
}
/** 获取三角形顶点 */
get vertexList() {
if (this.pointList.length === 2) {
return [
{
x: (this.pointList[0].x + this.pointList[1].x) / 2,
y: this.pointList[0].y,
},
this.pointList[1],
{ x: this.pointList[0].x, y: this.pointList[1].y },
];
} else {
return [];
}
}
get indexPoint() {
return this.vertexList[0];
}
/** 获取三角形结果点 */
get resultPoints() {
return this.vertexList;
}
}
function onload() {
document.getElementById("upload").onclick = upload;
document.getElementById("clear").onclick = clear;
document.getElementById("importJson").onclick = importJson;
document.getElementById("creact").onclick = creact;
document.getElementById("destroy").onclick = destroy;
var mark;
var markObjectList = [];
var labelInputElm = document.querySelector("#labelInput");
let colorInputElm = document.querySelector("#colorInput");
var textareaElm = document.querySelector("#textarea");
var labelInput = labelInputElm.value;
let colorInput = colorInputElm.value;
labelInputElm.onchange = (e) => {
labelInput = e.target.value;
};
colorInputElm.onchange = (e) => {
colorInput = e.target.value;
};
creactShapeIcon();
// 创建
creact();
document.querySelector(".mark-operate").onclick = setDrawType;
// 创建mark icon
function creactShapeIcon() {
const shapeList = document.querySelector(".mark-shape-list");
let arr = [];
for (let index = 0; index < shapeMap.length; index++) {
const item = shapeMap[index];
arr.push(`
<button data-type="${item.type}">
<svg data-type="${item.type}" viewBox="${item.viewBox}" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path data-type="${item.type}"
d="${item.icon}"
fill="#333333"></path>
</svg>
</button>`);
}
shapeList.innerHTML = arr.join("");
}
// 设置画线类型
function setDrawType(e) {
if (e.target.dataset.type) {
mark.setDrawType(e.target.dataset.type);
}
}
// 上传图片
function upload() {
var input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.onchange = function (e) {
var file = e.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function (e) {
mark?.setBackground(e.target.result).then(() => {
mark.setDrawType(mark.currentDrawingType || "rect");
});
};
reader.readAsDataURL(file);
}
};
input.click();
}
// 清空
function clear() {
mark.clearMarkShapes();
}
// 创建
function creact() {
if (mark) return;
mark = new CanvasMarkBoard({
view: "#mark-box", // ID名或者DOM对象
});
mark.register("sides_arrow", MarkSidesArrowObject);
mark.register("polyline_arrow", MarkPolylineArrowObject);
mark.register("triangle", MarkTriangleObject);
mark.setDrawType(mark.currentDrawingType || "rect");
mark.on("ondraw", (e) => {
mark.currentDrawingType = e.type;
});
mark.on("oncomplete", (e) => {
e.ok({ label: labelInput, color: colorInput });
});
mark.on("onchange", () => {
markObjectList = mark.objects;
creactList();
textareaElm.value = JSON.stringify(markObjectList);
});
}
// 销毁
function destroy() {
mark.destroy();
mark = undefined;
}
// 导入
function importJson() {
mark.setObjectData(json);
}
// 创建右侧列表
function creactList() {
var box = document.getElementById("mark-list");
var arr = [];
arr.push("<ul id='mark-list-ul'>");
for (var i = 0; i < markObjectList.length; i++) {
var label = markObjectList[i].label;
arr.push(`<li>
${label}
<button data-type='del' data-id=${markObjectList[i].id}>删除</button>
</li>`);
}
arr.push("</ul>");
box.innerHTML = arr.join("");
document.getElementById("mark-list-ul").onclick = (e) => {
if (e.target.dataset.type) {
switch (e.target.dataset.type) {
case "del":
mark.deleteObject(e.target.dataset.id);
break;
default:
break;
}
}
};
}
}
window.onload = onload;
onload();