Vue + AILabel.js 实现图片标注
2025-08-20 11:43 Spiderman25 阅读(297) 评论(0) 收藏 举报https://blog.csdn.net/Zhang_aichi/article/details/123551676
一、网址以及demo
官方文档:AILabel与你一路同行
demo地址1: AILabel-标注篇
demo地址2: AILabel示例一览
二、我的实例代码(vue环境下)

三、完整代码
<template> <div class="main"> <div class="operation"> <div class="button-wrap"> <el-button type="text" class="el-icon-thumb" @click="setMode('PAN')" >平移</el-button > <el-button type="text" class="el-icon-location-outline" @click="setMode('MARKER')" >注记</el-button > <el-button type="text" class="el-icon-more-outline" @click="setMode('POINT')" >点</el-button > <el-button type="text" class="el-icon-minus" @click="setMode('LINE')" >线段</el-button > <el-button type="text" class="el-icon-share" @click="setMode('POLYLINE')" >多段线</el-button > <el-button type="text" class="el-icon-orange" @click="setMode('CIRCLE')" >圆</el-button > <el-button type="text" class="el-icon-full-screen" @click="setMode('RECT')" >矩形</el-button > <el-button type="text" class="el-icon-house" @click="setMode('POLYGON')" >多边形</el-button > <el-button type="text" class="el-icon-magic-stick" @click="Fill()" >填充</el-button > <el-button type="text" class="el-icon-refresh-left" @click="Revoke()" >撤销</el-button > <el-button type="text" @click="getFeatures()">获取标注数据</el-button> <!-- <button class="btn btn-default" @click="setMode('DRAWMASK')"> 涂抹 </button> --> <!-- <button class="btn btn-default" @click="setMode('CLEARMASK')"> 擦除 </button> --> <!-- <button class="btn btn-default" @click="getRle()">获取rle数据</button> --> </div> <div class="zoom-icon-wrapper"> <div class="zoom-icon-plus" @click="zoomIn">+</div> <div class="zoom-icon-minus" @click="zoomOut">-</div> </div> </div> <div id="map"></div> </div> </template> <script> import AILabel from "ailabel"; export default { name: "HelloWorld", data() { return { imgUrl: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F0186f0570f33d132f875a83991e34b.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1650076295&t=c0b8a135c2f9298d1d714703f5d30423", drawingStyle: {}, mode: "", itemName: "", editId: "", //待填充图形id deleteIconId: "delete01", gMap: null, //AILabel实例 gFirstFeatureLayer: null, //矢量图层实例(矩形,多边形等矢量) allFeatures: null, //所有features }; }, watch: { mode(mode) { this.gMap.setMode(mode); this.setDrawingStyle(mode); }, }, methods: { zoomIn() { this.gMap.zoomIn(); }, zoomOut() { this.gMap.zoomOut(); }, setMode(mode) { this.mode = mode; }, // 获取所有features getFeatures() { this.allFeatures = this.gFirstFeatureLayer.getAllFeatures(); console.log("--allFeatures--", this.allFeatures); }, // 初始样式 setDrawingStyle(mode) { let drawingStyle = {}; switch (mode) { //平移 case "PAN": { break; } //注记 case "MARKER": { // 忽略 break; } //点 case "POINT": { this.drawingStyle = { fillStyle: "#FF8C00" }; this.gMap.setDrawingStyle(drawingStyle); break; } //圆 case "CIRCLE": { this.drawingStyle = { fillStyle: "#87CEFF", strokeStyle: "#87CEFF", lineWidth: 5, }; this.gMap.setDrawingStyle(drawingStyle); break; } //线段 case "LINE": { this.drawingStyle = { strokeStyle: "#BA55D3", lineJoin: "round", lineCap: "round", lineWidth: 10, arrow: false, }; this.gMap.setDrawingStyle(drawingStyle); break; } //多线段 case "POLYLINE": { this.drawingStyle = { strokeStyle: "#FF1493", lineJoin: "round", lineCap: "round", lineWidth: 10, }; this.gMap.setDrawingStyle(drawingStyle); break; } //矩形 case "RECT": { this.drawingStyle = { strokeStyle: "#0f0", lineWidth: 1 }; this.gMap.setDrawingStyle(drawingStyle); break; } //多边形 case "POLYGON": { this.drawingStyle = { strokeStyle: "#0099CC", //边框颜色 fill: true, //是否填充 fillStyle: "#FF6666", //填充色 globalAlpha: 0.3, lineWidth: 3, fill: true, stroke: true, }; this.gMap.setDrawingStyle(drawingStyle); break; } //涂抹 case "DRAWMASK": { this.drawingStyle = { strokeStyle: "rgba(255, 0, 0, .5)", fillStyle: "#00f", lineWidth: 50, }; this.gMap.setDrawingStyle(drawingStyle); break; } //擦除 case "CLEARMASK": { this.drawingStyle = { fillStyle: "#00f", lineWidth: 30 }; this.gMap.setDrawingStyle(drawingStyle); break; } default: break; } }, // 添加图形 addFeature(data, type, id) { let that = this; let drawingStyle = this.drawingStyle; //线 if (type === "LINE") { const scale = that.gMap.getScale(); const width = drawingStyle.lineWidth / scale; const lineFeature = new AILabel.Feature.Line( id, // id { ...data, width }, // shape { name }, // props drawingStyle // style ); that.gFirstFeatureLayer.addFeature(lineFeature); } //线段 else if (type === "POLYLINE") { const scale = that.gMap.getScale(); const width = drawingStyle.lineWidth / scale; const polylineFeature = new AILabel.Feature.Polyline( id, // id { points: data, width }, // shape { name }, // props drawingStyle // style ); that.gFirstFeatureLayer.addFeature(polylineFeature); } //矩形 else if (type === "RECT") { const rectFeature = new AILabel.Feature.Rect( id, // id data, // shape { name }, // props drawingStyle // style ); that.gFirstFeatureLayer.addFeature(rectFeature); } //多边形 else if (type === "POLYGON") { const polygonFeature = new AILabel.Feature.Polygon( id, // id { points: data }, // shape { name }, // props drawingStyle // style ); that.gFirstFeatureLayer.addFeature(polygonFeature); } //点 else if (type == "POINT") { const gFirstFeaturePoint = new AILabel.Feature.Point( id, // id { x: data.x, y: data.y, r: 5 }, // shape { name }, // props { fillStyle: "#FF8C00", zIndex: 5, lineWidth: 2 } // style ); that.gFirstFeatureLayer.addFeature(gFirstFeaturePoint); } //注记 else if (type == "MARKER") { const gFirstMarker = new AILabel.Marker( id, // id { src: "http://ailabel.com.cn/public/ailabel/demo/marker.png", position: { // marker坐标位置 x: data.x, y: data.y, }, offset: { x: -16, y: 32, }, }, // markerInfo { name: "第一个marker注记" } // props ); that.gFirstFeatureLayer.addFeature(gFirstMarker); } //圆 else if (type == "CIRCLE") { const gFirstFeatureCircle = new AILabel.Feature.Circle( id, // id { cx: data.cx, cy: data.cy, r: data.r }, // shape { name: "第一个矢量图层" }, // props { fillStyle: "#87CEFF", strokeStyle: "#3CB371", globalAlpha: 1, lineWidth: 5, } // style ); that.gFirstFeatureLayer.addFeature(gFirstFeatureCircle); } //涂抹 else if (type == "DRAWMASK") { const drawMaskAction = new AILabel.Mask.Draw( `${+new Date()}`, // id "铅笔", { points: data, width: 5 }, // shape { name: "港币", price: "1元" }, // props { strokeStyle: "#FF0000" } // style ); that.gFirstFeatureLayer.addAction(drawMaskAction); } //擦除 else if (type == "CLEARMASK") { const clearMaskAction = new AILabel.Mask.Clear( "first-action-clear", // id { points: data, width: 5 } // shape ); that.gFirstMaskLayer.addAction(clearMaskAction); } this.getFeatures(); }, // 画完取名 getName(mode) { return this.$prompt("请输入填写名字", { confirmButtonText: "确定", showCancelButton: false, }) .then(({ value }) => { this.itemName = value; return value; }) .catch(() => { return null; }); }, // 增加删除图标 addDeleteIcon(feature, shape) { let gMap = this.gMap; let that = this; // 添加delete-icon // let points = that.getPoints(feature); console.log(shape, "shape"); const gFirstMarker = new AILabel.Marker( that.deleteIconId, // id { src: "https://s1.aigei.com/src/img/png/45/45aabfc232a34e5b9bfaf75412973c08.png?|watermark/3/image/aHR0cHM6Ly9zMS5haWdlaS5jb20vd2F0ZXJtYXJrLzUwMC0xLnBuZz9lPTE3MzU0ODgwMDAmdG9rZW49UDdTMlhwemZ6MTF2QWtBU0xUa2ZITjdGdy1vT1pCZWNxZUpheHlwTDpjYWQ1NHVoRlhGUUViSGR3Vm02aXctVTJoWVE9/dissolve/40/gravity/NorthWest/dx/18/dy/21/ws/0.0/wst/0&e=1735488000&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:C11LKqsRLbAqQo2uVPETYDya0QU=", position: { x: shape.x + shape.width, y: shape.y - 15 }, // 矩形右上角 根据图形动态调整 offset: { x: -20, y: -4, }, }, // markerInfo { name: "delete" } // props ); gFirstMarker.events.on("click", (marker) => { // 首先删除当前marker gMap.markerLayer.removeMarkerById(marker.id); // 删除对应text // gFirstTextLayer.removeTextById(textId); // 删除对应feature that.gFirstFeatureLayer.removeFeatureById(feature.id); }); gMap.markerLayer.addMarker(gFirstMarker); // that.gFirstFeatureLayer }, // 删除 删除按钮 deIcon() { this.gMap.markerLayer.removeAllMarkers(); }, // 增加事件 addEvent() { let that = this; let gMap = this.gMap; gMap.events.on("drawDone", (type, data) => { console.log("--type, data--", type, data); // that.addFeature(data, type); if (type == "CLEARMASK" || type == "DRAWMASK") { that.addFeature(data, type); } else { that.getName(type).then((id) => { if (id) { that.addFeature(data, type, id); } else { this.$message({ type: "info", message: "请填写名字", }); } }); } }); gMap.events.on("boundsChanged", (data) => { console.log("--map boundsChanged--", data); return ""; }); // 双击编辑 在绘制模式下双击feature触发选中 gMap.events.on("featureSelected", (feature) => { this.editId = feature.id; console.log("--map featureSelected--", feature, "双击编辑"); //设置编辑feature gMap.setActiveFeature(feature); if (feature.type != "POINT") { // 增加删除按钮 that.addDeleteIcon(feature, feature.shape); } }); //右键 目前只针对点双击选中右键触发 gMap.events.on("featureDeleted", (feature) => { if (feature.type == "POINT") { // 根据id删除对应feature that.gFirstFeatureLayer.removeFeatureById(feature.id); } }); // 单机空白取消编辑 gMap.events.on("featureUnselected", () => { // 取消featureSelected that.editId = ""; that.deIcon(); gMap.setActiveFeature(null); }); // 更新完 gMap.events.on("featureUpdated", (feature, shape) => { console.log(feature); // 更新或者移动需要重新设置删除图标 that.deIcon(); feature.updateShape(shape); if (feature.type != "POINT") { that.addDeleteIcon(feature, shape); } }); // 删除 gMap.events.on("FeatureDeleted", () => { console.log(2222222); // that.gFirstFeatureLayer.removeFeatureById(that.editId); }); }, // 获取坐标 需要自行添加 getPoints(feature) { switch (feature.type) { case "RECT": return feature.getPoints(); case "LINE": return [feature.shape.start, feature.shape.end]; case "POLYLINE": return feature.shape.points; case "POLYGON": return feature.shape.points; default: return []; } }, //填充事件 Fill() { console.log("填充事件"); let fill = this.gFirstFeatureLayer.getFeatureById(this.editId); console.log("--填充对象--", fill); fill.style.fillStyle = "#FFDAB9"; fill.style.fill = true; //刷新map this.gMap.refresh(); }, //撤销 Revoke() { console.log("撤销"); this.getFeatures(); this.allFeatures.pop(); //刷新map this.gMap.refresh(); console.log(this.allFeatures, "--所有操作--"); }, }, mounted() { let that = this; const gMap = new AILabel.Map("map", { center: { x: 250, y: 150 }, // 为了让图片居中 zoom: 500, mode: "PAN", // 绘制线段 refreshDelayWhenZooming: true, // 缩放时是否允许刷新延时,性能更优 zoomWhenDrawing: true, panWhenDrawing: true, zoomWheelRatio: 5, // 控制滑轮缩放缩率[0, 10), 值越小,则缩放越快,反之越慢 withHotKeys: true, // 关闭快捷键 }); that.gMap = gMap; this.addEvent(); // 图片层添加 const gFirstImageLayer = new AILabel.Layer.Image( "first-layer-image", // id { src: that.imgUrl, width: 500, height: 300, crossOrigin: false, // 如果跨域图片,需要设置为true position: { // 左上角相对中心点偏移量 x: 0, y: 0, }, // 网格 grid: { // 3 * 3 columns: [{ color: "#9370DB" }, { color: "#FF6347" }], rows: [{ color: "#9370DB" }, { color: "#FF6347" }], }, }, // imageInfo { name: "第一个图片图层" }, // props { zIndex: 5 } // style ); // 添加到gMap对象 gMap.addLayer(gFirstImageLayer); // 添加矢量图层 const gFirstFeatureLayer = new AILabel.Layer.Feature( "first-layer-feature", // id { name: "第一个矢量图层" }, // props { zIndex: 10 } // style ); this.gFirstFeatureLayer = gFirstFeatureLayer; gMap.addLayer(gFirstFeatureLayer); window.onresize = function () { this.gMap && this.gMap.resize(); }; }, beforeDestroy() { this.gMap.destroy(); }, }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style> .main { display: flex; flex-direction: row; margin: 50px; justify-content: center; } .button-wrap { display: flex; flex-direction: column; padding-bottom: 10px; position: relative; z-index: 99; } #map { /* margin: 0 auto; */ overflow: hidden; position: relative; height: 600px; width: 800px; border: 1px dashed #ccc; } .zoom-icon-wrapper { position: absolute; /* left: 20px; */ /* top: 20px; */ z-index: 1000; } .zoom-icon-plus { width: 30px; height: 30px; line-height: 20px; text-align: center; border: 3px solid #6495ed; font-size: 20px; border-top-left-radius: 6px; border-top-right-radius: 6px; color: #ff8c00; cursor: pointer; } .zoom-icon-plus:hover { border-color: #4169e1; } .zoom-icon-minus { margin-top: 6px; width: 30px; height: 30px; line-height: 20px; text-align: center; border: 3px solid #6495ed; font-size: 25px; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; color: #ff8c00; cursor: pointer; } .zoom-icon-minus:hover { border-color: #4169e1; } /* 删除图标 */ #delete01 { width: 20px; height: 20px; } .el-button + .el-button { margin-left: 0px; } </style>
浙公网安备 33010602011771号