Skip to content

Commit e115180

Browse files
committed
Add async parsing option.
In my testing, the major time sink is parsing. This commit adds a setTimeout() around parsing of each item so control can return to the browser. This increases the total time it takes to finish a screenshot but will not freeze the browser when it does. This is a good option when e.g. doing error reporting, where you might not want to freeze the browser while sending debugging information back to your server.
1 parent 806cd60 commit e115180

4 files changed

Lines changed: 274 additions & 221 deletions

File tree

build/html2canvas.js

Lines changed: 135 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ function h2cRenderContext(width, height) {
10291029
}
10301030
};
10311031
}
1032-
_html2canvas.Parse = function (images, options) {
1032+
_html2canvas.Parse = function (images, options, cb) {
10331033
window.scroll(0,0);
10341034

10351035
var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default
@@ -1050,6 +1050,28 @@ _html2canvas.Parse = function (images, options) {
10501050

10511051
images = images || {};
10521052

1053+
function init() {
1054+
var background = getCSS(document.documentElement, "backgroundColor"),
1055+
transparentBackground = (Util.isTransparent(background) && element === document.body),
1056+
stack = renderElement(element, null, false, transparentBackground);
1057+
1058+
parseChildren(element, stack, null, function() {
1059+
if (transparentBackground) {
1060+
background = stack.backgroundColor;
1061+
}
1062+
1063+
Util.log('Done parsing, moving to Render.');
1064+
1065+
body.removeChild(hidePseudoElements);
1066+
cb({
1067+
backgroundColor: background,
1068+
stack: stack
1069+
});
1070+
});
1071+
}
1072+
1073+
init();
1074+
10531075
function documentWidth () {
10541076
return Math.max(
10551077
Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
@@ -1367,6 +1389,13 @@ _html2canvas.Parse = function (images, options) {
13671389
}
13681390
}
13691391

1392+
function h2czContext(zindex) {
1393+
return {
1394+
zindex: zindex,
1395+
children: []
1396+
};
1397+
}
1398+
13701399
function renderImage(ctx, element, image, bounds, borders) {
13711400

13721401
var paddingLeft = getCSSInt(element, 'paddingLeft'),
@@ -1403,69 +1432,67 @@ _html2canvas.Parse = function (images, options) {
14031432
});
14041433
}
14051434

1406-
var getCurvePoints = (function(kappa) {
1407-
1408-
return function(x, y, r1, r2) {
1409-
var ox = (r1) * kappa, // control point offset horizontal
1410-
oy = (r2) * kappa, // control point offset vertical
1411-
xm = x + r1, // x-middle
1412-
ym = y + r2; // y-middle
1413-
return {
1414-
topLeft: bezierCurve({
1415-
x:x,
1416-
y:ym
1417-
}, {
1418-
x:x,
1419-
y:ym - oy
1420-
}, {
1421-
x:xm - ox,
1422-
y:y
1423-
}, {
1424-
x:xm,
1425-
y:y
1426-
}),
1427-
topRight: bezierCurve({
1428-
x:x,
1429-
y:y
1430-
}, {
1431-
x:x + ox,
1432-
y:y
1433-
}, {
1434-
x:xm,
1435-
y:ym - oy
1436-
}, {
1437-
x:xm,
1438-
y:ym
1439-
}),
1440-
bottomRight: bezierCurve({
1441-
x:xm,
1442-
y:y
1443-
}, {
1444-
x:xm,
1445-
y:y + oy
1446-
}, {
1447-
x:x + ox,
1448-
y:ym
1449-
}, {
1450-
x:x,
1451-
y:ym
1452-
}),
1453-
bottomLeft: bezierCurve({
1454-
x:xm,
1455-
y:ym
1456-
}, {
1457-
x:xm - ox,
1458-
y:ym
1459-
}, {
1460-
x:x,
1461-
y:y + oy
1462-
}, {
1463-
x:x,
1464-
y:y
1465-
})
1466-
};
1435+
function getCurvePoints(x, y, r1, r2) {
1436+
var kappa = 4 * ((Math.sqrt(2) - 1) / 3);
1437+
var ox = (r1) * kappa, // control point offset horizontal
1438+
oy = (r2) * kappa, // control point offset vertical
1439+
xm = x + r1, // x-middle
1440+
ym = y + r2; // y-middle
1441+
return {
1442+
topLeft: bezierCurve({
1443+
x:x,
1444+
y:ym
1445+
}, {
1446+
x:x,
1447+
y:ym - oy
1448+
}, {
1449+
x:xm - ox,
1450+
y:y
1451+
}, {
1452+
x:xm,
1453+
y:y
1454+
}),
1455+
topRight: bezierCurve({
1456+
x:x,
1457+
y:y
1458+
}, {
1459+
x:x + ox,
1460+
y:y
1461+
}, {
1462+
x:xm,
1463+
y:ym - oy
1464+
}, {
1465+
x:xm,
1466+
y:ym
1467+
}),
1468+
bottomRight: bezierCurve({
1469+
x:xm,
1470+
y:y
1471+
}, {
1472+
x:xm,
1473+
y:y + oy
1474+
}, {
1475+
x:x + ox,
1476+
y:ym
1477+
}, {
1478+
x:x,
1479+
y:ym
1480+
}),
1481+
bottomLeft: bezierCurve({
1482+
x:xm,
1483+
y:ym
1484+
}, {
1485+
x:xm - ox,
1486+
y:ym
1487+
}, {
1488+
x:x,
1489+
y:y + oy
1490+
}, {
1491+
x:x,
1492+
y:y
1493+
})
14671494
};
1468-
})(4 * ((Math.sqrt(2) - 1) / 3));
1495+
}
14691496

14701497
function bezierCurve(start, startControl, endControl, end) {
14711498

@@ -1984,9 +2011,8 @@ _html2canvas.Parse = function (images, options) {
19842011
return str.replace("px", "");
19852012
}
19862013

1987-
var transformRegExp = /(matrix)\((.+)\)/;
1988-
19892014
function getTransform(element, parentStack) {
2015+
var transformRegExp = /(matrix)\((.+)\)/;
19902016
var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform");
19912017
var transformOrigin = getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin") || "0px 0px";
19922018

@@ -2127,52 +2153,54 @@ _html2canvas.Parse = function (images, options) {
21272153
return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore"));
21282154
}
21292155

2130-
function parseElement (element, stack, pseudoElement) {
2156+
function parseElement (element, stack, pseudoElement, cb) {
2157+
if (!cb) {
2158+
cb = function(){};
2159+
}
21312160
if (isElementVisible(element)) {
21322161
stack = renderElement(element, stack, pseudoElement, false) || stack;
21332162
if (!ignoreElementsRegExp.test(element.nodeName)) {
2134-
parseChildren(element, stack, pseudoElement);
2163+
return parseChildren(element, stack, pseudoElement, cb);
21352164
}
21362165
}
2166+
cb();
21372167
}
21382168

2139-
function parseChildren(element, stack, pseudoElement) {
2140-
Util.Children(element).forEach(function(node) {
2169+
function parseChildren(element, stack, pseudoElement, cb) {
2170+
var children = Util.Children(element);
2171+
// After all nodes have processed, finished() will call the cb.
2172+
// We add one and kick it off so this will still work when children.length === 0.
2173+
// Note that unless async is true, this will happen synchronously, just will callbacks.
2174+
var jobs = children.length + 1;
2175+
finished();
2176+
2177+
if (options.async) {
2178+
children.forEach(function(node) {
2179+
// Don't block the page from rendering
2180+
setTimeout(function(){ parseNode(node); }, 0);
2181+
});
2182+
} else {
2183+
children.forEach(parseNode);
2184+
}
2185+
2186+
function parseNode(node) {
21412187
if (node.nodeType === node.ELEMENT_NODE) {
2142-
parseElement(node, stack, pseudoElement);
2188+
parseElement(node, stack, pseudoElement, finished);
21432189
} else if (node.nodeType === node.TEXT_NODE) {
21442190
renderText(element, node, stack);
2191+
finished();
2192+
} else {
2193+
finished();
2194+
}
2195+
}
2196+
function finished(el) {
2197+
if (--jobs <= 0){
2198+
Util.log("finished rendering " + children.length + " children.");
2199+
cb();
21452200
}
2146-
});
2147-
}
2148-
2149-
function init() {
2150-
var background = getCSS(document.documentElement, "backgroundColor"),
2151-
transparentBackground = (Util.isTransparent(background) && element === document.body),
2152-
stack = renderElement(element, null, false, transparentBackground);
2153-
parseChildren(element, stack);
2154-
2155-
if (transparentBackground) {
2156-
background = stack.backgroundColor;
21572201
}
2158-
2159-
body.removeChild(hidePseudoElements);
2160-
return {
2161-
backgroundColor: background,
2162-
stack: stack
2163-
};
21642202
}
2165-
2166-
return init();
21672203
};
2168-
2169-
function h2czContext(zindex) {
2170-
return {
2171-
zindex: zindex,
2172-
children: []
2173-
};
2174-
}
2175-
21762204
_html2canvas.Preload = function( options ) {
21772205

21782206
var images = {
@@ -2697,21 +2725,19 @@ window.html2canvas = function(elements, opts) {
26972725
return;
26982726
}
26992727
}
2700-
queue = _html2canvas.Parse( images, options );
2701-
2702-
if (typeof options.onparsed === "function") {
2703-
if ( options.onparsed( queue ) === false ) {
2704-
return;
2728+
_html2canvas.Parse( images, options, function(queue) {
2729+
if (typeof options.onparsed === "function") {
2730+
if ( options.onparsed( queue ) === false ) {
2731+
return;
2732+
}
27052733
}
2706-
}
2707-
2708-
canvas = _html2canvas.Renderer( queue, options );
2709-
2710-
if (typeof options.onrendered === "function") {
2711-
options.onrendered( canvas );
2712-
}
27132734

2735+
canvas = _html2canvas.Renderer( queue, options );
27142736

2737+
if (typeof options.onrendered === "function") {
2738+
options.onrendered( canvas );
2739+
}
2740+
});
27152741
};
27162742

27172743
// for pages without images, we still want this to be async, i.e. return methods before executing

build/html2canvas.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)