使用画布数据的PDFMake循环不能保持正确的顺序

时间:2016-07-16 16:04:57

标签: javascript html5 canvas pdf-generation

我使用以下代码将DOM元素渲染为画布数据,然后使用它们制作PDF。

我必须为此创建一个循环,因为如果它只是一个图像,则无法正确地将数据放在多个页面上。

$scope.pdfMaker = function() {

    var content = [];

    var pdfElement = document.getElementsByClassName("pdfElement");
    for (var i = pdfElement.length - 1; i >= 0; i--) {
        html2canvas(pdfElement[i], {
            onrendered: function(canvas) {

                var data = canvas.toDataURL();

                content.push({
                    image: data,
                    width: 500,
                    margin: [0, 5]
                });
                console.log(canvas);
            }

        });
    }
    var docDefinition = {
        content: content
    };

    setTimeout(function() {
        pdfMake.createPdf(docDefinition).download("Score_Details.pdf");
    }, 10000);

}

的问题:

  1. 我有令人讨厌的10秒超时以便有时间进行处理,我如何重构我的代码以允许在执行所有画布数据后制作PDF。

  2. 我的画布元素在转换时会变得混乱,正确的顺序是必不可少的。如何维护DOM元素的顺序?

2 个答案:

答案 0 :(得分:3)

似乎html2canvas返回一个promise:

$scope.pdfMaker = function() {

    var promises = [];
    var pdfElements = document.getElementsByClassName("pdfElement");

    for (var i = 0; i < pdfElements.length; i++) {
        promises.push(
            html2canvas(pdfElements[i]).then(function(canvas) {
                console.log('finished one!');
                return {
                    image: canvas.toDataURL(),
                    width: 500,
                    margin: [0, 5]
                };
            })
        );
    }

    console.log('done calling');
    $q.all(promises).then(function(content) {
        console.log('all done');
        pdfMake.createPdf({ content: content }).download("Score_Details.pdf");
        console.log('download one');
    }, function(err) {
        console.error(err);
    })

}

答案 1 :(得分:1)

要使代码正常工作,只需要几个mod。

您的描述中的问题是html2canvas的渲染时间会有所不同,因此它们会以不确定的顺序出现。如果你在for循环中使代码成为一个函数并将索引传递给它,函数将关闭参数变量,你可以在onrendered回调中使用该索引,因为(也将关闭索引)放置渲染html在内容数组的正确位置。

要知道何时完成所有渲染并且可以转换为pdf,请保留已启动的html2canvas渲染器数量的计数。当调用onrendered回调时,将计数减1。当计数为零时,您知道所有文档都已呈现,因此您可以调用createPdf。

以下是更改

$scope.pdfMaker = function() {
    var i,pdfElement,content,count,docDefinition;
    function makeCanvas(index){ // closure ensures that index is unique inside this function.
        count += 1; // count the new render request
        html2canvas(pdfElement[index], {
            onrendered: function(canvas) { // this function closes over index
                content[index] = {  // add the canvas content to the correct index
                    image: canvas.toDataURL(),
                    width: 500,
                    margin: [0, 5]
                };
                count -= 1; // reduce count
                if(count === 0){ // is count 0, if so then all done
                     // render the pdf and download
                    pdfMake.createPdf(docDefinition).download("Score_Details.pdf");
                }
            }
        });        
    }
    // get elements
    pdfElement = document.getElementsByClassName("pdfElement");
    content = []; // create content array
    count = 0; // set render request counter
    docDefinition = {content: content}; //
    for (i = pdfElement.length - 1; i >= 0; i--) { //do the loop thing
        makeCanvas(i);  // call the makeCanvas function with the index
    }

}

我唯一不确定的是您希望pdfElements出现的顺序。这些更改会将呈现的内容按照document.getElementsByClassName("pdfElement");获取的顺序放入内容数组中。

如果您希望逆转顺序中的文档在计数回零时调用content.reverse(),并且您已准备好创建pdf。

Closure是Javascript的一个非常强大的功能。如果您不确定封闭是如何工作的,那么审查是值得的。

MDN closures和任何一个开始都是一个好地方,网上有大量资源需要学习。