Vue3 在线 PDF 编辑 1.0 画线批注

家人们!上回带大家看了 PDF 预览怎么玩,这回直接放大招 ------ 拆解 基于 Vue3 的在线 PDF 编辑 1.0 项目诞生记 里超实用的 画线批注 功能!话不多说,快跟上,一起钻进代码世界 "挖宝"!

初始化 fabric-Canvas:从 "硬刚" 到 "躺平" 的技术自救

一开始咱也是 "技术猛男",想着手写 Canvas 挑战批注功能,结果写着写着发现 ------ 这简直是在给自己挖坑!各种功能需求 "狂轰滥炸",写到怀疑人生。果断 "认怂",搬出老伙计 fabric 救场!虽然它平时都在搞图片编辑,但这次跨界到 PDF 批注,居然完美适配,只能说 "专业的事还得专业的工具来干"!

要实现画线批注,先得在页面上 "召唤" 两个 canvas 兄弟:

ini 复制代码
<canvas
    :data-index="index"
    class="pdf-box"
    :ref="(el:any) => (canvasRefs['canvas' + index] = el)"
></canvas>
<canvas
    class="annotation-canvas"
    :id="`annotation-canvas_${index}`"
></canvas>

第二个 canvas 记得给它加上 z-index: 2 的 "buff",不然在同一画布上操作,容易把 PDF 内容 "误伤"。当然,你也可以把 PDF 转成图片当背景,这样画布刷新时内容就不会 "人间蒸发",怎么玩就看各位的脑洞啦!

在渲染 PDF 时,咱得让第二个 canvas 和第一个 canvas "保持同款身材",再用 new fabric.Canvas 初始化,最后把鼠标事件绑定好,"万事俱备,只欠东风":

ini 复制代码
if (!pdfUrl.value) return;
const loadingTask = pdfjsLib.getDocument(pdfUrl.value);
const pdf = await loadingTask.promise;
// 省略部分代码...
for (let i = 1; i <= pagesCount.value; i++) {
    const page = await pdf.getPage(i);
    const viewport = page.getViewport({ scale });
    const canvas = canvasRefs["canvas" + (i - 1)];
    if (!canvas) break;
    const context = canvas.getContext("2d");
    canvas.height = viewport.height;
    canvas.width = viewport.width;
    const fabricCanvas = new fabric.Canvas(`annotation-canvas_${i - 1}`, {
        width: viewport.width,
        height: viewport.height,
        isDrawingMode: false,
    });
    fabricCanvas.selectionColor = 'transparent';
    fabricCanvas.selectionBorderColor = 'transparent';
    fabricCanvas.on('mouse:down', startLine.bind(fabricCanvas, {
        page: i - 1,
        canvas: fabricCanvas,
    }));
    fabricCanvas.on('mouse:move', drawLine.bind(fabricCanvas, {
        page: i - 1,
        canvas: fabricCanvas,
    }));
    fabricCanvas.on('mouse:up', stopDrwa.bind(fabricCanvas, {
        page: i - 1,
        canvas: fabricCanvas,
    }));
    fabricCanvasObj.value[`annotation-canvas_${i - 1}`] = fabricCanvas;
    const wrapper = canvas.parentElement;
    wrapper.style.width = `${viewport.width}px`;
    wrapper.style.height = `${viewport.height}px`;
    const renderContext = {
        canvasContext: context,
        viewport: viewport,
    };
    await page.render(renderContext).promise;
}

我这里用瀑布流展示 PDF,看着超带感!不过要是追求性能,换成翻页模式也是个 "真香" 选择!

fabric 画线批注:代码里的 "魔法画笔"

绑定好事件后,fabric 就要开始 "表演" 了!三个核心函数,让你的鼠标化身 "神笔马良":

  1. 鼠标按下:记录起点,创建线条
ini 复制代码
const startLine = async (event: { page: string, canvas: any }, e: any) => {
    if (!e || !event.canvas) return;
    const fabricCanvas = event.canvas;
    const pointer = fabricCanvas.getPointer(e.e);
    if (e.target) {
        fabricCanvas.setActiveObject(e.target);
        fabricCanvas.renderAll();
    } else if (drawConfig.value.type === "draw") {
        const currentObjet = new fabric.Line([pointer.x, pointer.y, pointer.x, pointer.y], {
            stroke: drawConfig.value.lineColor,
            strokeWidth: drawConfig.value.lineWidth,
            selectable: true
        });
        fabricCanvas.add(currentObjet);
    }
};
  1. 鼠标移动:实时更新线条,"指哪画哪"
ini 复制代码
const drawLine = (event: { page: string, canvas: any }, e: any) => {
    if (!e) return;
    const fabricCanvas = event.canvas;
    const pointer = fabricCanvas.getPointer(e.e);
    if (drawConfig.value.type === 'draw') {
        const currentObjet = fabricCanvas.getActiveObject();
        if (currentObjet) {
            currentObjet.set({ x2: pointer.x, y2: pointer.y });
            fabricCanvas.requestRenderAll();
        }
    }
};
  1. 鼠标松开:"收笔" 完成,完美 ending
ini 复制代码
const stopDrwa = (event: { page: string, canvas: any }, e: any) => {
    const fabricCanvas = event.canvas;
    fabricCanvas.discardActiveObject();
    fabricCanvas.renderAll();
};

有了这一套 "组合拳",画线批注直接拿捏!

以上就是画线批注功能的核心代码解析!后续还有更多宝藏功能等着解锁,想亲自上手的小伙伴,速去 项目仓库 拉取源码!也欢迎各位大佬、小伙伴来唠嗑,咱们一起把这个项目卷成 "六边形战士"!

相关推荐
LuciferHuang10 分钟前
震惊!三万star开源项目竟有致命Bug?
前端·javascript·debug
GISer_Jing11 分钟前
前端实习总结——案例与大纲
前端·javascript
天天进步201515 分钟前
前端工程化:Webpack从入门到精通
前端·webpack·node.js
姑苏洛言1 小时前
编写产品需求文档:黄历日历小程序
前端·javascript·后端
知识分享小能手2 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
姑苏洛言2 小时前
搭建一款结合传统黄历功能的日历小程序
前端·javascript·后端
你的人类朋友3 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手3 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
一只小灿灿3 小时前
前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理
前端·opencv·计算机视觉