From 017320d15f5a9de25d2723234ef124f3c8c95839 Mon Sep 17 00:00:00 2001 From: MoyuScript Date: Tue, 1 Aug 2017 23:27:12 +0800 Subject: [PATCH] Calculate correct bounds for text/elements under nested transforms --- src/Bounds.js | 16 ++-------------- src/NodeContainer.js | 7 ++++++- src/NodeParser.js | 5 +++-- src/TextBounds.js | 29 +++++++++++++++++++++-------- src/index.js | 11 ++++++----- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/Bounds.js b/src/Bounds.js index 25f81e2..aaba24c 100644 --- a/src/Bounds.js +++ b/src/Bounds.js @@ -44,20 +44,8 @@ export class Bounds { } } -export const parseBounds = (node: HTMLElement, isTransformed: boolean): Bounds => { - return isTransformed ? offsetBounds(node) : Bounds.fromClientRect(node.getBoundingClientRect()); -}; - -const offsetBounds = (node: HTMLElement): Bounds => { - // //$FlowFixMe - const parent = node.offsetParent ? offsetBounds(node.offsetParent) : {top: 0, left: 0}; - - return new Bounds( - node.offsetLeft + parent.left, - node.offsetTop + parent.top, - node.offsetWidth, - node.offsetHeight - ); +export const parseBounds = (node: HTMLElement): Bounds => { + return Bounds.fromClientRect(node.getBoundingClientRect()); }; export const calculatePaddingBox = (bounds: Bounds, borders: Array): Bounds => { diff --git a/src/NodeContainer.js b/src/NodeContainer.js index 83773f6..8e22718 100644 --- a/src/NodeContainer.js +++ b/src/NodeContainer.js @@ -88,10 +88,15 @@ export default class NodeContainer { zIndex: parseZIndex(style.zIndex) }; + if (this.isTransformed()) { + // getBoundingClientRect provides values post-transform, we want them without the transformation + node.style.transform = 'matrix(1,0,0,1,0,0)'; + } + this.image = // $FlowFixMe node.tagName === 'IMG' ? imageLoader.loadImage(node.currentSrc || node.src) : null; - this.bounds = parseBounds(node, this.isTransformed()); + this.bounds = parseBounds(node); if (__DEV__) { this.name = `${node.tagName.toLowerCase()}${node.id ? `#${node.id}` diff --git a/src/NodeParser.js b/src/NodeParser.js index 6079264..6e23be0 100644 --- a/src/NodeParser.js +++ b/src/NodeParser.js @@ -35,7 +35,8 @@ const parseNodeTree = ( stack: StackingContext, imageLoader: ImageLoader ): void => { - node.childNodes.forEach((childNode: Node) => { + for (let childNode = node.firstChild, nextNode; childNode; childNode = nextNode) { + nextNode = childNode.nextSibling; if (childNode.nodeType === Node.TEXT_NODE) { //$FlowFixMe if (childNode.data.trim().length > 0) { @@ -72,7 +73,7 @@ const parseNodeTree = ( } } } - }); + } }; const createsRealStackingContext = (container: NodeContainer, node: HTMLElement): boolean => { diff --git a/src/TextBounds.js b/src/TextBounds.js index 05e9acf..4073fb0 100644 --- a/src/TextBounds.js +++ b/src/TextBounds.js @@ -3,7 +3,7 @@ import {ucs2} from 'punycode'; import type TextContainer from './TextContainer'; -import {Bounds} from './Bounds'; +import {Bounds, parseBounds} from './Bounds'; import {TEXT_DECORATION} from './parsing/textDecoration'; import FEATURES from './Feature'; @@ -40,19 +40,32 @@ export const parseTextBounds = (textContainer: TextContainer, node: Text): Array ) { if (FEATURES.SUPPORT_RANGE_BOUNDS) { textBounds.push(new TextBounds(text, getRangeBounds(node, offset, text.length))); + } else { + const replacementNode = node.splitText(text.length); + textBounds.push(new TextBounds(text, getWrapperBounds(node))); + node = replacementNode; } + } else if (!FEATURES.SUPPORT_RANGE_BOUNDS) { + node = node.splitText(text.length); } offset += text.length; } return textBounds; +}; - /* - else if (container.node && typeof container.node.data === 'string') { - var replacementNode = container.node.splitText(text.length); - var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform()); - container.node = replacementNode; - return bounds; - }*/ +const getWrapperBounds = (node: Text): Bounds => { + const wrapper = node.ownerDocument.createElement('html2canvaswrapper'); + wrapper.appendChild(node.cloneNode(true)); + const parentNode = node.parentNode; + if (parentNode) { + parentNode.replaceChild(wrapper, node); + const bounds = parseBounds(wrapper); + if (wrapper.firstChild) { + parentNode.replaceChild(wrapper.firstChild, wrapper); + } + return bounds; + } + return new Bounds(0, 0, 0, 0); }; const getRangeBounds = (node: Text, offset: number, length: number): Bounds => { diff --git a/src/index.js b/src/index.js index e87b03b..1104a62 100644 --- a/src/index.js +++ b/src/index.js @@ -19,10 +19,7 @@ export type Options = { type: ?string }; -const html2canvas = ( - element: HTMLElement, - config: Options -): Promise => { +const html2canvas = (element: HTMLElement, config: Options): Promise => { const logger = new Logger(); const ownerDocument = element.ownerDocument; @@ -91,7 +88,11 @@ const html2canvas = ( } } - const renderer = new CanvasRenderer(canvas, {scale: options.scale, backgroundColor, imageStore}); + const renderer = new CanvasRenderer(canvas, { + scale: options.scale, + backgroundColor, + imageStore + }); return renderer.render(stack); }); });