将 deferred.resolve 从函数内部的操作移动到整个函数完成

Move deferred.resolve from an operation inside a function to the entire function completion

当我在将所有 svg 移动到 canvas 之后将 deferred.resolve 移动到外部时,我没有让整个功能在 deferred.resolve 中工作。我希望 deferred.promise() 在所有 svg 都转换为 canvas 而不仅仅是第一个 svg.

时执行
$(document).ready(function() {

    $( '#save_dashboard' ).click(function() {

        // Create a deferred object
        var dfd = new $.Deferred();

        // https://github.com/niklasvh/html2canvas/issues/95#issuecomment-45114424
        // First render all SVGs to canvases
        targetElem = $('#dashboard');

        var elements = targetElem.find('svg').map(function() {
            var svg = $(this);
            var canvas = $('<canvas></canvas>');
            svg.replaceWith(canvas);

            // Get the raw SVG string and curate it
            var content = svg.wrap('<p></p>').parent().html();
            content = content.replace(/xlink:title='hide\/show'/g, '');
            content = encodeURIComponent(content);
            svg.unwrap();

            // Create an image from the svg
            var image = new Image();
            image.src = 'data:image/svg+xml,' + content;
            image.onload = function() {
                canvas[0].width = image.width;
                canvas[0].height = image.height;

                // Render the image to the canvas
                var context = canvas[0].getContext('2d');
                dfd.resolve(context.drawImage(image, 0, 0));
            };

            return dfd.promise();
            /* return {
                svg: svg,
                canvas: canvas
            }; */
        }); // end of targetElem.find('svg').map(function() {...});

        // $.when(dfd).done(function(){
        //    console.log('dfd done');
        dfd.then(function(_canvas){
            console.log('dfd done', _canvas);

            // http://www.kubilayerdogan.net/html2canvas-take-screenshot-of-web-page-and-save-it-to-server-javascript-and-php/
            $('#dashboard').html2canvas({
                onrendered: function (canvas) {
                    //Set hidden field's value to image data (base-64 string)
                    var dashboardPng = canvas.toDataURL('image/png');
                    console.log('dashboardPng: ' + dashboardPng);

                    $.ajax({
                        url:'save_dashboard_image.php',
                        data:{dashboardPngData: dashboardPng},
                        type:'POST',
                        dataType:'json',
                        success: function(){
                            console.log('success');
                        }
                        ,
                        error: function(xhr, status, error){
                            console.log('The requested page was: ' + document.URL +
                                '. The error number returned was: ' + xhr.status +
                                '. The error message was: ' + error);
                        }
                    });
                }
            });
        // }); // end of when(dfd).done()
        }); // end of dfd.then(function(_canvas){...})

    }); // end of save_dashboard click function
}); // end of document ready

解决方案 修改自以下特里的回答:

$(document).ready(function() {

    $( '#save_dashboard' ).click(function() {

        // Declare an array to store all deferred objects from each svg element
        var svgDfds = [],
            targetElem = $('#dashboard');

        targetElem.find('svg').each(function() {
            var dfd = new $.Deferred(),
                svg = $(this),
                canvas = $('<canvas></canvas>');

            svg.replaceWith(canvas);

            // Get the raw SVG string and curate it
            var content = svg.wrap('<p></p>').parent().html();
            content = content.replace(/xlink:title='hide\/show'/g, '');
            content = encodeURIComponent(content);
            svg.unwrap();

            // Create an image from the svg
            var image = new Image();
            image.src = 'data:image/svg+xml,' + content;
            image.onload = function() {
                canvas[0].width = image.width;
                canvas[0].height = image.height;

                // Render the image to the canvas
                var context = canvas[0].getContext('2d');

                // Resolve or reject the deferred
                dfd.resolve(context.drawImage(image, 0, 0));
            };

            // Push deferred object into array
            svgDfds.push(dfd);

        }); // end of targetElem.find('svg').map(function() {...});

        // Check for all deferreds
        $.when.apply($, svgDfds).then(function(_canvas) {
            console.log('dfd done', _canvas);

            // http://www.kubilayerdogan.net/html2canvas-take-screenshot-of-web-page-and-save-it-to-server-javascript-and-php/
            $('#dashboard').html2canvas({
                onrendered: function (canvas) {
                    //Set hidden field's value to image data (base-64 string)
                    var dashboardPng = canvas.toDataURL('image/png');
                    console.log('dashboardPng: ' + dashboardPng);

                    $.ajax({
                        url:'save_dashboard_image.php',
                        data:{dashboardPngData: dashboardPng},
                        type:'POST',
                        dataType:'json',
                        success: function(){
                            console.log('success');
                        }
                        ,
                        error: function(xhr, status, error){
                            console.log('The requested page was: ' + document.URL +
                                '. The error number returned was: ' + xhr.status +
                                '. The error message was: ' + error);
                        }
                    });
                }
            });
        });

    }); // end of save_dashboard click function
}); // end of document ready

您不能对多个图像加载使用单个延迟。每个图像都需要自己的 deferred,这样你就可以为它创建一个专用的 promise。只有这样你才能得到一系列不同的承诺(每个承诺在加载各自的图像时解决),然后你可以 pass all to $.when 等待它们。

您的代码应如下所示:

$( '#save_dashboard' ).click(function() {
    var targetElem = $('#dashboard');

    var elementPromises = targetElem.find('svg').map(function() {
        // Create the deferred objects here!
        var dfd = new $.Deferred();
        …
        var image = new Image();
        …
        image.onload = function() {
            …
            dfd.resolve(context.drawImage(image, 0, 0));
        };

        return dfd.promise();
    }).get(); // an array, not a jquery collection

    var allLoaded = $.when.apply($, elementPromises);
    allLoaded.then(…);
});

您应该按以下方式构建代码:

  1. 在点击时创建一个新数组,用它来存储所有来自 SVG 的延迟
  2. 遍历所有 SVG 元素
    • 在每个实例中,创建一个新的内部延迟对象
    • 解决或拒绝延期
    • 将其推入循环外的数组
  3. 使用$.when.apply($, arrayOfDeferredObjects)检查数组中所有延迟对象的状态

在代码中,应该如下所示:

$('#save_dashboard').click(function() {

    var svgDfd = [],  // Declare an array to store ALL deferreds from svgs
        $targetElem = $('#dashboard');

    // Use .each() to iterate through all SVGs
    $targetElem.find('svg').each(function() {

        // New deferred object per SVG instance
        var dfd = new $.Deferred();

        // At some point in your code, resolve or reject the deferred
        dfd.resolve();

        // Push deferred object into array
        svgDfds.push(dfd);
    }

    // Check for all deferreds
    $.when.apply($, svgDfds).then(function() {
        // Do stuff
    });
});

因此,对您的代码稍作修改和重构以适应上述范例:

$(document).ready(function() {

    $( '#save_dashboard' ).click(function() {

        // Declare an array to store all deferredo objects from each svg element
        var svgDfds = [],
            $targetElem = $('#dashboard');

        $targetElem.find('svg').each(function() {
            var dfd = new $.Deferred(),
                svg = $(this),
                canvas = $('<canvas></canvas>');

            svg.replaceWith(canvas);

            // Get the raw SVG string and curate it
            var content = svg.wrap('<p></p>').parent().html();
            content = content.replace(/xlink:title='hide\/show'/g, '');
            content = encodeURIComponent(content);
            svg.unwrap();

            // Create an image from the svg
            var image = new Image();
            image.src = 'data:image/svg+xml,' + content;
            image.onload = function() {
                canvas[0].width = image.width;
                canvas[0].height = image.height;

                // Render the image to the canvas
                var context = canvas[0].getContext('2d');
                dfd.resolve(context.drawImage(image, 0, 0));
            };

            svgDfds.push(dfd);

        });


        $.when.apply($, svgDfds).then(function(){
            $('#dashboard').html2canvas({
                onrendered: function (canvas) {
                    //Set hidden field's value to image data (base-64 string)
                    var dashboardPng = canvas.toDataURL('image/png');
                    console.log('dashboardPng: ' + dashboardPng);

                    $.ajax({
                        url:'save_dashboard_image.php',
                        data:{dashboardPngData: dashboardPng},
                        type:'POST',
                        dataType:'json',
                        success: function(){
                            console.log('success');
                        }
                        ,
                        error: function(xhr, status, error){
                            console.log('The requested page was: ' + document.URL +
                                '. The error number returned was: ' + xhr.status +
                                '. The error message was: ' + error);
                        }
                    });
                }
            });
        });

    });
});