first implementation for matrix transforms

This commit is contained in:
Niklas von Hertzen 2013-08-06 21:11:08 +03:00
parent 518dd702a2
commit 10b40821e5
3 changed files with 57 additions and 22 deletions

View File

@ -166,6 +166,20 @@ _html2canvas.Util.Bounds = function (element) {
return bounds; return bounds;
}; };
// TODO ideally, we'd want everything to go through this function instead of Util.Bounds,
// but would require further work to calculate the correct positions for elements with offsetParents
_html2canvas.Util.OffsetBounds = function (element) {
var parent = element.offsetParent ? _html2canvas.Util.OffsetBounds(element.offsetParent) : {top: 0, left: 0};
return {
top: element.offsetTop + parent.top,
bottom: element.offsetTop + element.offsetHeight + parent.top,
left: element.offsetLeft + parent.left,
width: element.offsetWidth,
height: element.offsetHeight
};
};
function toPX(element, attribute, value ) { function toPX(element, attribute, value ) {
var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute], var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute],
left, left,
@ -295,14 +309,13 @@ function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroun
if(prop !== 'backgroundSize') { if(prop !== 'backgroundSize') {
left -= (backgroundSize || image).width*percentage; left -= (backgroundSize || image).width*percentage;
} }
} else { } else {
if(prop === 'backgroundSize') { if(prop === 'backgroundSize') {
if(bgposition[0] === 'auto') { if(bgposition[0] === 'auto') {
left = image.width; left = image.width;
} else { } else {
if (/contain|cover/.test(bgposition[0])) { if (/contain|cover/.test(bgposition[0])) {
var resized = _html2canvas.Util.resizeBounds( image.width, image.height, bounds.width, bounds.height, bgposition[0] ); var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, bgposition[0]);
left = resized.width; left = resized.width;
topPos = resized.height; topPos = resized.height;
} else { } else {
@ -335,6 +348,7 @@ _html2canvas.Util.BackgroundPosition = function( el, bounds, image, imageIndex,
var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize ); var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize );
return { left: result[0], top: result[1] }; return { left: result[0], top: result[1] };
}; };
_html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) { _html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) {
var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex ); var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex );
return { width: result[0], height: result[1] }; return { width: result[0], height: result[1] };

View File

@ -48,16 +48,18 @@ _html2canvas.Parse = function (images, options) {
} }
} }
function capitalize(m, p1, p2) {
if (m.length > 0) {
return p1 + p2.toUpperCase();
}
}
function textTransform (text, transform) { function textTransform (text, transform) {
switch(transform){ switch(transform){
case "lowercase": case "lowercase":
return text.toLowerCase(); return text.toLowerCase();
case "capitalize": case "capitalize":
return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { return text.replace( /(^|\s|:|-|\(|\))([a-z])/g, capitalize);
if (m.length > 0) {
return p1 + p2.toUpperCase();
}
} );
case "uppercase": case "uppercase":
return text.toUpperCase(); return text.toUpperCase();
default: default:
@ -127,16 +129,16 @@ _html2canvas.Parse = function (images, options) {
} }
} }
function getTextBounds(state, text, textDecoration, isLast) { function getTextBounds(state, text, textDecoration, isLast, transform) {
var bounds; var bounds;
if (support.rangeBounds) { if (support.rangeBounds && !transform) {
if (textDecoration !== "none" || Util.trimText(text).length !== 0) { if (textDecoration !== "none" || Util.trimText(text).length !== 0) {
bounds = textRangeBounds(text, state.node, state.textOffset); bounds = textRangeBounds(text, state.node, state.textOffset);
} }
state.textOffset += text.length; state.textOffset += text.length;
} else if (state.node && typeof state.node.nodeValue === "string" ){ } else if (state.node && typeof state.node.nodeValue === "string" ){
var newTextNode = (isLast) ? state.node.splitText(text.length) : null; var newTextNode = (isLast) ? state.node.splitText(text.length) : null;
bounds = textWrapperBounds(state.node); bounds = textWrapperBounds(state.node, transform);
state.node = newTextNode; state.node = newTextNode;
} }
return bounds; return bounds;
@ -149,7 +151,7 @@ _html2canvas.Parse = function (images, options) {
return range.getBoundingClientRect(); return range.getBoundingClientRect();
} }
function textWrapperBounds(oldTextNode) { function textWrapperBounds(oldTextNode, transform) {
var parent = oldTextNode.parentNode, var parent = oldTextNode.parentNode,
wrapElement = doc.createElement('wrapper'), wrapElement = doc.createElement('wrapper'),
backupText = oldTextNode.cloneNode(true); backupText = oldTextNode.cloneNode(true);
@ -157,7 +159,7 @@ _html2canvas.Parse = function (images, options) {
wrapElement.appendChild(oldTextNode.cloneNode(true)); wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement, oldTextNode); parent.replaceChild(wrapElement, oldTextNode);
var bounds = Util.Bounds(wrapElement); var bounds = transform ? Util.OffsetBounds(wrapElement) : Util.Bounds(wrapElement);
parent.replaceChild(backupText, wrapElement); parent.replaceChild(backupText, wrapElement);
return bounds; return bounds;
} }
@ -195,7 +197,7 @@ _html2canvas.Parse = function (images, options) {
} }
textList.forEach(function(text, index) { textList.forEach(function(text, index) {
var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1)); var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix);
if (bounds) { if (bounds) {
drawText(text, bounds.left, bounds.bottom, ctx); drawText(text, bounds.left, bounds.bottom, ctx);
renderTextDecoration(ctx, textDecoration, bounds, metrics, color); renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
@ -947,14 +949,21 @@ _html2canvas.Parse = function (images, options) {
return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1)); return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1));
} }
function setTransform(ctx, element, parentStack) { function removePx(str) {
return str.replace("px", "");
}
var transformRegExp = /(matrix)\((.+)\)/;
function getTransform(element, parentStack) {
var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform"); var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform");
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"); 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";
transformOrigin = transformOrigin.split(" ").map(removePx).map(Util.asFloat);
var matrix; var matrix;
var TRANSFORM_REGEXP = /(matrix)\((.+)\)/;
if (transform && transform !== "none") { if (transform && transform !== "none") {
var match = transform.match(TRANSFORM_REGEXP); var match = transform.match(transformRegExp);
if (match) { if (match) {
switch(match[1]) { switch(match[1]) {
case "matrix": case "matrix":
@ -970,14 +979,14 @@ _html2canvas.Parse = function (images, options) {
}; };
} }
function createStack(element, parentStack, bounds) { function createStack(element, parentStack, bounds, transform) {
var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height),
stack = { stack = {
ctx: ctx, ctx: ctx,
opacity: setOpacity(ctx, element, parentStack), opacity: setOpacity(ctx, element, parentStack),
cssPosition: getCSS(element, "position"), cssPosition: getCSS(element, "position"),
borders: getBorderData(element), borders: getBorderData(element),
transform: setTransform(ctx, element, parentStack), transform: transform,
clip: (parentStack && parentStack.clip) ? Util.Extend( {}, parentStack.clip ) : null clip: (parentStack && parentStack.clip) ? Util.Extend( {}, parentStack.clip ) : null
}; };
@ -1006,16 +1015,26 @@ _html2canvas.Parse = function (images, options) {
return backgroundBounds; return backgroundBounds;
} }
function renderElement(element, parentStack, pseudoElement){ function getBounds(element, transform) {
var bounds = Util.Bounds(element), var bounds = (transform.matrix) ? Util.OffsetBounds(element) : Util.Bounds(element);
transform.origin[0] += bounds.left;
transform.origin[1] += bounds.top;
return bounds;
}
function renderElement(element, parentStack, pseudoElement) {
var transform = getTransform(element, parentStack),
bounds = getBounds(element, transform),
image, image,
bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"), bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"),
stack = createStack(element, parentStack, bounds), stack = createStack(element, parentStack, bounds, transform),
borders = stack.borders, borders = stack.borders,
ctx = stack.ctx, ctx = stack.ctx,
backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip),
borderData = parseBorders(element, bounds, borders); borderData = parseBorders(element, bounds, borders);
createShape(ctx, borderData.clip); createShape(ctx, borderData.clip);
ctx.save(); ctx.save();

View File

@ -85,7 +85,9 @@ _html2canvas.Renderer.Canvas = function(options) {
ctx.save(); ctx.save();
if (storageContext.transform.matrix) { if (storageContext.transform.matrix) {
ctx.translate(storageContext.transform.origin[0], storageContext.transform.origin[1]);
ctx.transform.apply(ctx, storageContext.transform.matrix); ctx.transform.apply(ctx, storageContext.transform.matrix);
ctx.translate(-storageContext.transform.origin[0], -storageContext.transform.origin[1]);
} }
if (storageContext.clip){ if (storageContext.clip){