如何使用 PDF.js 和 jQuery 在 PDF 顶部创建可拖动元素

How to create a draggable element on top of a PDF using PDF.js and jQuery

我正在创建一项服务,使用 PDF.js 和 jQuery 用图像标记 pdf。我设法使用 PDF.js 创建了一个可拖动对象,但该对象在被拖动时留下了过去对象的痕迹。

我使用 context.clearRect(0, 0, canvas.width, canvas.height); 清除了过去的对象,但它也清除了 canvas 中的基础 PDF。

如何在不影响底层 PDF 的情况下拖动此对象?

这是我到目前为止所做的。

我正在使用以下代码将 PDF 加载到 canvas。

function loadPdfPreview(base64pdf){

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js';

        var loadingTask = pdfjsLib.getDocument({data: base64pdf});

        loadingTask.promise.then(function (pdf) {

            // Fetch the first page
            pdf.getPage(1).then(function (page) {

                var scale = 1.0;

                var viewport = page.getViewport(scale);

                // Prepare canvas using PDF page dimensions
                canvas = document.getElementById('pdf-canvas');
                context = canvas.getContext('2d');

                canvas.height = viewport.height;
                canvas.width = viewport.width;

                canvasOffset = $("#pdf-canvas").offset();
                offsetX = canvasOffset.left;
                offsetY = canvasOffset.top;


                // Render PDF page into canvas context
                var renderContext = {
                    canvasContext: context,
                    viewport: viewport
                };

                page.render(renderContext).then(function () {

                    // creating the dummy object on success
                    drawObjectFromBlueprint(blueprint);

                }, function (e) {
                    console.log(e);
                });

            });
        });
    }

pdf 加载到 canvas 后,我使用以下函数在 canvas 顶部绘制对象。

function drawObjectFromBlueprint(blueprint) {
        // drawing a draggable element inside the canvas
        context.strokeStyle = "lightgray";

        // Clearing the previous dummy objects
        context.clearRect(0, 0, canvas.width, canvas.height);

        // drawing the dummy object
        context.beginPath();
        context.moveTo(blueprint.x, blueprint.y);
        context.lineTo(blueprint.right, blueprint.y);
        context.lineTo(blueprint.right, blueprint.bottom);
        context.lineTo(blueprint.x, blueprint.bottom);
        context.closePath();

        context.fillStyle = blueprint.fill;
        context.fill();
        context.stroke();
    }

我使用以下代码处理鼠标移动事件。

function handleMouseMove(e) {

        var mouseX = parseInt(e.clientX - offsetX1);
        var mouseY = parseInt(e.clientY - offsetY1);

        // always update the global blueprint
        blueprint.x += (mouseX - lastX);
        blueprint.y += (mouseY - lastY);
        blueprint.right = blueprint.x + blueprint.width;
        blueprint.bottom = blueprint.y + blueprint.height;

        lastX = mouseX;
        lastY = mouseY;

        drawObjectFromBlueprint(blueprint);
    }

然后我使用以下代码监听鼠标移动事件。

$("#drawable-canvas").mousemove(function (e) {
        handleMouseMove(e);
    });

我想在不影响底层 PDF 的情况下重新绘制对象。尝试通过清除整个 canvas 来加载 PDF 并重新绘制对象,但它没有按预期工作。

page.render方法的回调中,pdf页面会绘制在canvas上。画出来的图要单独保存,这样原图才不会拖动消失

// maybe globalScope...?
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');

// your code

page.render(renderContext).then(function () {
  // Save the original page image.
  tempCanvas.width = canvas.width;
  tempCanvas.height = canvas.height;
  tempCtx.drawImage(canvas, 0, 0);

  // creating the dummy object on success
  drawObjectFromBlueprint(blueprint);
}, ...

接下来请修改为使用drawObjectFromBlueprint中的原页面图片绘制。

function drawObjectFromBlueprint(blueprint) {
  // draw original page image
  context.drawImage(tempCanvas, 0, 0);

  // drawing a draggable element inside the canvas
  context.strokeStyle = "lightgray";
  ...
}

经过两个小时的尝试,找出了执行此操作的方法。正如@Nikolaus 建议的那样,我使用了两个 canvases,并使用底部 canvas 加载 PDF,顶部 canvas 用于绘图。

以下是我的HTML:

<div class="col-md-12" id="pdfDisplay" style="display: none;">
     <div id="pageContainer" class="pdfViewer nopadding" style="background-color:transparent">
          <canvas id="pdf-canvas" style="border:1px  solid black"></canvas>
          <canvas id="drawable-canvas" style="border:1px  solid black"></canvas>
      </div>
 </div>

以下是我 CSS 将两个 canvas 放在彼此之上的方法。

#pageContainer { position: relative; }
#drawable-canvas { position: absolute; top: 0; left: 0; }

以下是我的全局 Javascript 变量:

var isUploading = false;
var base64pdf = "";

var initX = 50;
var initY = 50;
var initWidth = 200;
var initHeight = 150;

// blueprint options are in pixels
// this blueprint holds the latest values for the draggable object
// always update this global blueprint when making a change so it holds the latest values
var blueprint = {
    x: initX,
    y: initY,
    width: initWidth,
    height: initHeight,
    right: (initX+initWidth), // x + width
    bottom: (initY+initHeight), // y + height
    fill: "skyblue"
 };
 var context = null;
 var canvas = null;
 var drawableContext = null;
 var drawableCanvas = null;

 var canvasOffset = null;
 var offsetX = 0;
 var offsetY = 0;

 var canvasOffset1 = null;
 var offsetX1 = 0;
 var offsetY1 = 0;
 
 var lastX = 0;
 var lastY = 0;
 
 var mouseIsDown = false;

Javascript 监听鼠标向上、向下和移动的函数,因为我只需要在使用鼠标拖动对象时跟踪鼠标移动。

$("#drawable-canvas").mousedown(function (e) {
     var mouseX = parseInt(e.clientX - offsetX);
     var mouseY = parseInt(e.clientY - offsetY);

     lastX = mouseX;
     lastY = mouseY;
     mouseIsDown = true;
 });

 $("#drawable-canvas").mousemove(function (e) {
     handleMouseMove(e);
 });

 $("#drawable-canvas").mouseup(function (e) {
      mouseIsDown = false;
 });

Javascript函数加载PDF到底部canvas.

    function loadPdfPreview(base64pdf){

        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js';

        var loadingTask = pdfjsLib.getDocument({data: base64pdf});

        loadingTask.promise.then(function (pdf) {

            // Fetch the first page
            pdf.getPage(1).then(function (page) {

                var scale = 1.0;

                var viewport = page.getViewport(scale);

                // Prepare canvas using PDF page dimensions
                canvas = document.getElementById('pdf-canvas');
                context = canvas.getContext('2d');

                canvas.height = viewport.height;
                canvas.width = viewport.width;

                canvasOffset = $("#pdf-canvas").offset();
                offsetX = canvasOffset.left;
                offsetY = canvasOffset.top;

                canvasOffset1 = $("#drawable-canvas").offset();
                offsetX1 = canvasOffset1.left;
                offsetY1 = canvasOffset1.top;

                // creating the drawable-canvas canvas for drawing purposes without affecting the pdf-canvas
                // it has identical width and height and X and Y values
                drawableCanvas = document.getElementById('drawable-canvas');;
                drawableCanvas.height = viewport.height;
                //bringing the drawable canvas up using z-index
                drawableCanvas.style.zIndex = 1;
                drawableCanvas.width = viewport.width;
                drawableContext = drawableCanvas.getContext('2d');

                // Render PDF page into canvas context
                var renderContext = {
                    canvasContext: context,
                    viewport: viewport
                };

                page.render(renderContext).then(function () {

                    // creating the dummy object on success
                    drawObjectFromBlueprint(blueprint);

                }, function (e) {
                    console.log(e);
                });

            });
        });
    }

处理鼠标移动(拖动对象):

    function handleMouseMove(e) {
        if (!mouseIsDown) {
            return;
        }

        var mouseX = parseInt(e.clientX - offsetX1);
        var mouseY = parseInt(e.clientY - offsetY1);

        // always update the global blueprint
        blueprint.x += (mouseX - lastX);
        blueprint.y += (mouseY - lastY);
        blueprint.right = blueprint.x + blueprint.width;
        blueprint.bottom = blueprint.y + blueprint.height;

        lastX = mouseX;
        lastY = mouseY;

        drawObjectFromBlueprint(blueprint);

        console.log(blueprint);
    }

Javascript函数在drawablecanvas上绘制对象,完全独立于pdf-canvas.

    function drawObjectFromBlueprint(blueprint) {
        // drawing a draggable element inside the canvas
        drawableContext.strokeStyle = "lightgray";

        // Clearing the previous dummy objects
        drawableContext.clearRect(0, 0, drawableCanvas.width, drawableCanvas.height);

        // drawing the dummy object
        drawableContext.beginPath();
        drawableContext.moveTo(blueprint.x, blueprint.y);
        drawableContext.lineTo(blueprint.right, blueprint.y);
        drawableContext.lineTo(blueprint.right, blueprint.bottom);
        drawableContext.lineTo(blueprint.x, blueprint.bottom);
        drawableContext.closePath();

        drawableContext.fillStyle = blueprint.fill;
        drawableContext.fill();
        drawableContext.stroke();
    }