在线CAD图纸批注功能方案比较与实现

​ AutoCAD的出现与应用使得在其应用领域内的工作效率得到了质的飞跃。同时随着技术的不断进步,更多的人希望能摆脱AutoCAD环境,能方便的实现Web在线看图。图纸批注此功能作为图纸审核工作的一个重要工作,本文将介绍如何实现在线Web端图纸批注功能。

实现效果图

annotate.gif

Web在线打开AutoCAD图形

如果在Web网页端展示CAD图形(唯杰地图云端图纸管理平台 https://vjmap.com/app/cloud),这个在前面的博文中已讲过,这里不再重复,有需要的朋友可下载工程源代码研究下。

图纸批注方案比较

图纸批注功能做为web端图纸显示的一个图层,一般的技术思路可分为通过矢量图形绘制svg图形这两种方式把图纸要批注的内容叠加到地图图层上。两个方案的优缺点如下:

矢量图形绘制:

优点:性能高,叠加效果好,支持无级放大不失真;

缺点:编辑功能较弱,表达内容有限

SVG图形

优点:编辑功能强,表达内容丰富

缺点:大量svg图形会导致性能低下,放大到一定级别后会失真

总结: 鉴于图纸批注的需求,是一个要求表达内容丰富, 同时不会存在很大数据量的需求,所以用 svg图形方案叠加是不错的选择。

图纸批注实现

SVG图形编辑库选型

svg图形编辑库采用的是github上star多达22k的开源库Fabric.js

Fabric.js 是一个功能强大且简洁的 canvas 库。它提供了一系列可操作 canvas 的 API。让你轻松的操作 canvas。并且还提供了 SVG 转 canvas 和 canvas 转 SVG 的解析器供使用。

Fabric.js官网地址为http://fabricjs.com/ ,github仓库地址为https://github.com/fabricjs/fabric.js

图纸批注绘图相关代码

//绘画方法
    function drawing() {
        if (drawingObject) {
            canvas.remove(drawingObject);
        }
        var canvasObject = null;
        switch (drawType) {
            case "arrow": //箭头
                canvasObject = new fabric.Path(drawArrow(mouseFrom.x, mouseFrom.y, mouseTo.x, mouseTo.y, 30, 30), {
                    stroke: strokeColor.value,
                    fill: "rgba(255,255,255,0)",
                    strokeWidth: strokeWidth.value
                });
                break;
            case "line": //直线
                canvasObject = new fabric.Line([mouseFrom.x, mouseFrom.y, mouseTo.x, mouseTo.y], {
                    stroke: strokeColor.value,
                    strokeWidth: strokeWidth.value
                });
                break;
            case "dottedline": //虚线
                canvasObject = new fabric.Line([mouseFrom.x, mouseFrom.y, mouseTo.x, mouseTo.y], {
                    strokeDashArray: [3, 1],
                    stroke: strokeColor.value,
                    strokeWidth: strokeWidth.value
                });
                break;
            case "circle": //正圆
                var left = mouseFrom.x,
                    top = mouseFrom.y;
                var radius = Math.sqrt((mouseTo.x - left) * (mouseTo.x - left) + (mouseTo.y - top) * (mouseTo.y - top)) / 2;
                canvasObject = new fabric.Circle({
                    left: left,
                    top: top,
                    stroke: strokeColor.value,
                    fill: "rgba(255, 255, 255, 0)",
                    radius: radius,
                    strokeWidth: strokeWidth.value
                });
                break;
            case "ellipse": //椭圆
                var left = mouseFrom.x,
                    top = mouseFrom.y;
                var radius = Math.sqrt((mouseTo.x - left) * (mouseTo.x - left) + (mouseTo.y - top) * (mouseTo.y - top)) / 2;
                canvasObject = new fabric.Ellipse({
                    left: left,
                    top: top,
                    stroke: strokeColor.value,
                    fill: "rgba(255, 255, 255, 0)",
                    originX: "center",
                    originY: "center",
                    rx: Math.abs(left - mouseTo.x),
                    ry: Math.abs(top - mouseTo.y),
                    strokeWidth: strokeWidth.value
                });
                break;
            case "rectangle": //长方形
                var path =
                    "M " +
                    mouseFrom.x +
                    " " +
                    mouseFrom.y +
                    " L " +
                    mouseTo.x +
                    " " +
                    mouseFrom.y +
                    " L " +
                    mouseTo.x +
                    " " +
                    mouseTo.y +
                    " L " +
                    mouseFrom.x +
                    " " +
                    mouseTo.y +
                    " L " +
                    mouseFrom.x +
                    " " +
                    mouseFrom.y +
                    " z";
                canvasObject = new fabric.Path(path, {
                    left: left,
                    top: top,
                    stroke: strokeColor.value,
                    strokeWidth: strokeWidth.value,
                    fill: "rgba(255, 255, 255, 0)"
                });
                //也可以使用fabric.Rect
                break;
            case "rightangle": //直角三角形
                var path = "M " + mouseFrom.x + " " + mouseFrom.y + " L " + mouseFrom.x + " " + mouseTo.y + " L " + mouseTo.x + " " + mouseTo.y + " z";
                canvasObject = new fabric.Path(path, {
                    left: left,
                    top: top,
                    stroke: strokeColor.value,
                    strokeWidth: strokeWidth.value,
                    fill: "rgba(255, 255, 255, 0)"
                });
                break;
            case "equilateral": //等边三角形
                var height = mouseTo.y - mouseFrom.y;
                canvasObject = new fabric.Triangle({
                    top: mouseFrom.y,
                    left: mouseFrom.x,
                    width: Math.sqrt(Math.pow(height, 2) + Math.pow(height / 2.0, 2)),
                    height: height,
                    stroke: strokeColor.value,
                    strokeWidth: strokeWidth.value,
                    fill: "rgba(255,255,255,0)"
                });
                break;
            case "text":
                textbox = new fabric.Textbox("", {
                    left: mouseFrom.x - 60,
                    top: mouseFrom.y - 20,
                    width: 150,
                    fontSize: 30,
                    borderColor: "yellow",
                    fill: strokeColor.value,
                    hasControls: true
                });
                canvas.add(textbox);
                textbox.enterEditing();
                textbox.hiddenTextarea.focus();
                break;
            case "remove":
                break;
            default:
                break;
        }
        if (canvasObject) {
            canvas.add(canvasObject);
            drawingObject = canvasObject;
        }
    }

SVG图形与地图叠加显示

唯杰地图 vjmap提供的sdk中有在一个地理范围内创建一个随缩放而缩放的div的覆盖物类DivOverlay,其类型定义如下

/**
 * 在一个地理范围内创建一个随缩放而缩放的div的覆盖物
 * 注:如果是svg,则需设置为 viewBox="0 0 width height" preserveAspectRatio="xMinYMin meet", updateDivSize选项设置为true
 **/
export  class DivOverlay {
    options: DivOverlayOptions;
    _map?: Map;
    isShow: boolean;
    minZoom: number;
    maxZoom: number;
    isRemoved: boolean;
    parentContainer?: HTMLElement;
    constructor(options: DivOverlayOptions);
    addTo(map: Map, insertId?: string | HTMLElement): void;
    private _isShow;
    private _add;
    private _remove;
    /**
     * 设置是否显示隐藏
     * @param visible 是否显示
     * @param isDisplay true的话,表示用style的display去控制隐藏显示,dom还在文档中。false的话,会从文档动态清空增加
     */
    setVisible(visible?: boolean, isDisplay?: boolean): void;
    remove(): void;
    updateBounds(bounds: [GeoPointLike, GeoPointLike, GeoPointLike, GeoPointLike] | GeoBounds): void;
    updateSize(width: number, height: number): void;
    private _updateZoom;
    private _updateDivSize;
    private _adjustCoord;
    private _update;
}

export  interface DivOverlayOptions {
    /** 范围,四个点坐标 */
    bounds: [GeoPointLike, GeoPointLike, GeoPointLike, GeoPointLike] | GeoBounds;
    /** html元素 */
    element: HTMLElement;
    /** 元素宽 */
    width: number;
    /** 元素高 */
    height: number;
    /** 显示最大级别 */
    minZoom?: number;
    /** 显示最小级别 */
    maxZoom?: number;
    /** 自动更新div大小,(如果需要svg放大,需要设置为true) */
    updateDivSize?: boolean;
    /** 放大div时,最大的div大小,超过了就像素放大了 */
    maxDivSize?: number;
}

我们把fabricjs中绘制的图形导出为svg,然后通过DivOverlay叠加至地图上,就可以了。相关代码如下


 export const createDivSvg = (map: Map, eleId: string, pt1: GeoPoint, pt2: GeoPoint, width: number, height: number, svg: string) => {
    svg = svg.substring(svg.indexOf("<desc>"));
    const div = document.createElement( "div" );
    div.id = eleId;
    div.innerHTML = `
        <svg viewBox="0 0 ${width} ${height}" preserveAspectRatio="xMinYMin meet" version="1.1" xmlns="http://www.w3.org/2000/svg">
            ${svg}
        </svg>
    `;
    div.style.position = 'absolute';
    div.style.pointerEvents = "none"
    div.style.width = width + "px";
    div.style.height = height + "px";
    //div.style.opacity = '0.8';

    const divOverlay = new DivOverlay({
        bounds: [[pt1.x, pt2.y], [pt1.x, pt1.y], [pt2.x, pt1.y], [pt2.x, pt2.y]],
        element: div,
        width: width,
        height: height,
        updateDivSize: true // 如果svg需要放大,需要加此参数
    })
    divOverlay.addTo(map);
    return divOverlay;
}
let center = map.getCenter();
let anno = {
    mapId: app.curMapId,
    version: app.curVersion,
    name: name,
    darkTheme: svc.currentMapParam()?.darkMode === true ? true : false,
    imgSrc: img,
    zoom: map.getZoom(),
    centerX: center.lng,
    centerY: center.lat,
    bearing: map.getBearing(),
    pitch: map.getPitch(),
    id: id,
    pt1: canvasCoord1,
    pt2: canvasCoord2,
    width: canvasWidth,
    height: canvasHeight,
    svg: fabricCanvas.toSVG(),
    json: fabricCanvas.toJSON(),
}
// 增加divSvg
let divOverlay = createDivSvg(map, id, canvasCoord1, canvasCoord2, canvasWidth, canvasHeight, anno.svg);

image-20220724211826407

有需要的朋友可以在线上传图形创建属于自己的图纸批注。上面的案例代码已开源。访问 (唯杰地图云端图纸管理平台 https://vjmap.com/app/cloud) ,点击下载此案例源码即可。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现类似 Word 批注功能,可以使用 JavaScript 和 HTML/CSS 来创建一个类似的界面。以下是实现步骤: 1. 首先,需要在页面上创建一个可编辑的文本框,用于输入文本内容。 2. 在文本框下方创建一个评论区域,用于显示批注内容。 3. 当用户选中文本框中的一段文本时,弹出一个窗口,让用户输入批注内容并保存。 4. 将保存的批注内容展示在评论区域中,并且将其与原始文本关联起来。 5. 当用户点击评论区域中的某个批注时,高亮显示对应的文本,并且弹出批注内容。 实现这个功能需要用到一些 JavaScript 的 DOM 操作和事件监听。具体实现步骤可以参考下面的代码示例。 HTML 代码: ``` <textarea id="content" rows="10" cols="50"></textarea> <div id="comments"></div> ``` JavaScript 代码: ```javascript // 获取文本框和评论区域的 DOM 元素 var content = document.getElementById('content'); var comments = document.getElementById('comments'); // 监听文本框的选择事件 content.addEventListener('mouseup', function(e) { // 获取选择的文本 var selection = window.getSelection().toString(); // 如果有选择的文本,则弹出批注窗口 if (selection) { var comment = prompt('请添加批注:'); if (comment) { // 创建一个新的批注元素 var span = document.createElement('span'); span.innerHTML = comment; span.classList.add('comment'); // 将批注元素添加到评论区域 comments.appendChild(span); // 获取选择的文本的范围,并将其与批注元素关联起来 var range = window.getSelection().getRangeAt(0); range.surroundContents(span); } } }); // 监听评论区域的点击事件 comments.addEventListener('click', function(e) { // 如果点击的是批注元素,则高亮显示对应的文本,并弹出批注内容 if (e.target.classList.contains('comment')) { var range = document.createRange(); range.selectNodeContents(e.target.previousSibling); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); alert(e.target.innerHTML); } }); ``` CSS 代码: ```css .comment { background-color: yellow; cursor: pointer; } ``` 这段代码实现了一个简单的类似 Word 批注功能,当用户选择文本时,会弹出一个窗口让用户输入批注内容,然后将批注内容展示在评论区域中,同时将其与原始文本关联起来。当用户点击评论区域中的某个批注时,会高亮显示对应的文本,并且弹出批注内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值