html2canvas
Version:
Screenshots with JavaScript
328 lines (286 loc) • 16.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _Bounds = require('./Bounds');
var _Font = require('./Font');
var _Gradient = require('./Gradient');
var _TextContainer = require('./TextContainer');
var _TextContainer2 = _interopRequireDefault(_TextContainer);
var _background = require('./parsing/background');
var _border = require('./parsing/border');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Renderer = function () {
function Renderer(target, options) {
_classCallCheck(this, Renderer);
this.target = target;
this.options = options;
target.render(options);
}
_createClass(Renderer, [{
key: 'renderNode',
value: function renderNode(container) {
if (container.isVisible()) {
this.renderNodeBackgroundAndBorders(container);
this.renderNodeContent(container);
}
}
}, {
key: 'renderNodeContent',
value: function renderNodeContent(container) {
var _this = this;
var callback = function callback() {
if (container.childNodes.length) {
container.childNodes.forEach(function (child) {
if (child instanceof _TextContainer2.default) {
var style = child.parent.style;
_this.target.renderTextNode(child.bounds, style.color, style.font, style.textDecoration, style.textShadow);
} else {
_this.target.drawShape(child, container.style.color);
}
});
}
if (container.image) {
var _image = _this.options.imageStore.get(container.image);
if (_image) {
var contentBox = (0, _Bounds.calculateContentBox)(container.bounds, container.style.padding, container.style.border);
var _width = typeof _image.width === 'number' && _image.width > 0 ? _image.width : contentBox.width;
var _height = typeof _image.height === 'number' && _image.height > 0 ? _image.height : contentBox.height;
if (_width > 0 && _height > 0) {
_this.target.clip([(0, _Bounds.calculatePaddingBoxPath)(container.curvedBounds)], function () {
_this.target.drawImage(_image, new _Bounds.Bounds(0, 0, _width, _height), contentBox);
});
}
}
}
};
var paths = container.getClipPaths();
if (paths.length) {
this.target.clip(paths, callback);
} else {
callback();
}
}
}, {
key: 'renderNodeBackgroundAndBorders',
value: function renderNodeBackgroundAndBorders(container) {
var _this2 = this;
var HAS_BACKGROUND = !container.style.background.backgroundColor.isTransparent() || container.style.background.backgroundImage.length;
var hasRenderableBorders = container.style.border.some(function (border) {
return border.borderStyle !== _border.BORDER_STYLE.NONE && !border.borderColor.isTransparent();
});
var callback = function callback() {
var backgroundPaintingArea = (0, _background.calculateBackgroungPaintingArea)(container.curvedBounds, container.style.background.backgroundClip);
if (HAS_BACKGROUND) {
_this2.target.clip([backgroundPaintingArea], function () {
if (!container.style.background.backgroundColor.isTransparent()) {
_this2.target.fill(container.style.background.backgroundColor);
}
_this2.renderBackgroundImage(container);
});
}
container.style.border.forEach(function (border, side) {
if (border.borderStyle !== _border.BORDER_STYLE.NONE && !border.borderColor.isTransparent()) {
_this2.renderBorder(border, side, container.curvedBounds);
}
});
};
if (HAS_BACKGROUND || hasRenderableBorders) {
var paths = container.parent ? container.parent.getClipPaths() : [];
if (paths.length) {
this.target.clip(paths, callback);
} else {
callback();
}
}
}
}, {
key: 'renderBackgroundImage',
value: function renderBackgroundImage(container) {
var _this3 = this;
container.style.background.backgroundImage.slice(0).reverse().forEach(function (backgroundImage) {
if (backgroundImage.source.method === 'url' && backgroundImage.source.args.length) {
_this3.renderBackgroundRepeat(container, backgroundImage);
} else if (/gradient/i.test(backgroundImage.source.method)) {
_this3.renderBackgroundGradient(container, backgroundImage);
}
});
}
}, {
key: 'renderBackgroundRepeat',
value: function renderBackgroundRepeat(container, background) {
var image = this.options.imageStore.get(background.source.args[0]);
if (image) {
var backgroundPositioningArea = (0, _background.calculateBackgroungPositioningArea)(container.style.background.backgroundOrigin, container.bounds, container.style.padding, container.style.border);
var backgroundImageSize = (0, _background.calculateBackgroundSize)(background, image, backgroundPositioningArea);
var position = (0, _background.calculateBackgroundPosition)(background.position, backgroundImageSize, backgroundPositioningArea);
var _path = (0, _background.calculateBackgroundRepeatPath)(background, position, backgroundImageSize, backgroundPositioningArea, container.bounds);
var _offsetX = Math.round(backgroundPositioningArea.left + position.x);
var _offsetY = Math.round(backgroundPositioningArea.top + position.y);
this.target.renderRepeat(_path, image, backgroundImageSize, _offsetX, _offsetY);
}
}
}, {
key: 'renderBackgroundGradient',
value: function renderBackgroundGradient(container, background) {
var backgroundPositioningArea = (0, _background.calculateBackgroungPositioningArea)(container.style.background.backgroundOrigin, container.bounds, container.style.padding, container.style.border);
var backgroundImageSize = (0, _background.calculateGradientBackgroundSize)(background, backgroundPositioningArea);
var position = (0, _background.calculateBackgroundPosition)(background.position, backgroundImageSize, backgroundPositioningArea);
var gradientBounds = new _Bounds.Bounds(Math.round(backgroundPositioningArea.left + position.x), Math.round(backgroundPositioningArea.top + position.y), backgroundImageSize.width, backgroundImageSize.height);
var gradient = (0, _Gradient.parseGradient)(container, background.source, gradientBounds);
if (gradient) {
switch (gradient.type) {
case _Gradient.GRADIENT_TYPE.LINEAR_GRADIENT:
// $FlowFixMe
this.target.renderLinearGradient(gradientBounds, gradient);
break;
case _Gradient.GRADIENT_TYPE.RADIAL_GRADIENT:
// $FlowFixMe
this.target.renderRadialGradient(gradientBounds, gradient);
break;
}
}
}
}, {
key: 'renderBorder',
value: function renderBorder(border, side, curvePoints) {
this.target.drawShape((0, _Bounds.parsePathForBorder)(curvePoints, side), border.borderColor);
}
}, {
key: 'renderStack',
value: function renderStack(stack) {
var _this4 = this;
if (stack.container.isVisible()) {
var _opacity = stack.getOpacity();
if (_opacity !== this._opacity) {
this.target.setOpacity(stack.getOpacity());
this._opacity = _opacity;
}
var _transform = stack.container.style.transform;
if (_transform !== null) {
this.target.transform(stack.container.bounds.left + _transform.transformOrigin[0].value, stack.container.bounds.top + _transform.transformOrigin[1].value, _transform.transform, function () {
return _this4.renderStackContent(stack);
});
} else {
this.renderStackContent(stack);
}
}
}
}, {
key: 'renderStackContent',
value: function renderStackContent(stack) {
var _splitStackingContext = splitStackingContexts(stack),
_splitStackingContext2 = _slicedToArray(_splitStackingContext, 5),
negativeZIndex = _splitStackingContext2[0],
zeroOrAutoZIndexOrTransformedOrOpacity = _splitStackingContext2[1],
positiveZIndex = _splitStackingContext2[2],
nonPositionedFloats = _splitStackingContext2[3],
nonPositionedInlineLevel = _splitStackingContext2[4];
var _splitDescendants = splitDescendants(stack),
_splitDescendants2 = _slicedToArray(_splitDescendants, 2),
inlineLevel = _splitDescendants2[0],
nonInlineLevel = _splitDescendants2[1];
// https://www.w3.org/TR/css-position-3/#painting-order
// 1. the background and borders of the element forming the stacking context.
this.renderNodeBackgroundAndBorders(stack.container);
// 2. the child stacking contexts with negative stack levels (most negative first).
negativeZIndex.sort(sortByZIndex).forEach(this.renderStack, this);
// 3. For all its in-flow, non-positioned, block-level descendants in tree order:
this.renderNodeContent(stack.container);
nonInlineLevel.forEach(this.renderNode, this);
// 4. All non-positioned floating descendants, in tree order. For each one of these,
// treat the element as if it created a new stacking context, but any positioned descendants and descendants
// which actually create a new stacking context should be considered part of the parent stacking context,
// not this new one.
nonPositionedFloats.forEach(this.renderStack, this);
// 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
nonPositionedInlineLevel.forEach(this.renderStack, this);
inlineLevel.forEach(this.renderNode, this);
// 6. All positioned, opacity or transform descendants, in tree order that fall into the following categories:
// All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order.
// For those with 'z-index: auto', treat the element as if it created a new stacking context,
// but any positioned descendants and descendants which actually create a new stacking context should be
// considered part of the parent stacking context, not this new one. For those with 'z-index: 0',
// treat the stacking context generated atomically.
//
// All opacity descendants with opacity less than 1
//
// All transform descendants with transform other than none
zeroOrAutoZIndexOrTransformedOrOpacity.forEach(this.renderStack, this);
// 7. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index
// order (smallest first) then tree order.
positiveZIndex.sort(sortByZIndex).forEach(this.renderStack, this);
}
}, {
key: 'render',
value: function render(stack) {
var _this5 = this;
if (this.options.backgroundColor) {
this.target.rectangle(this.options.x, this.options.y, this.options.width, this.options.height, this.options.backgroundColor);
}
this.renderStack(stack);
var target = this.target.getTarget();
if (process.env.NODE_ENV !== 'production') {
return target.then(function (output) {
_this5.options.logger.log('Render completed');
return output;
});
}
return target;
}
}]);
return Renderer;
}();
exports.default = Renderer;
var splitDescendants = function splitDescendants(stack) {
var inlineLevel = [];
var nonInlineLevel = [];
var length = stack.children.length;
for (var i = 0; i < length; i++) {
var child = stack.children[i];
if (child.isInlineLevel()) {
inlineLevel.push(child);
} else {
nonInlineLevel.push(child);
}
}
return [inlineLevel, nonInlineLevel];
};
var splitStackingContexts = function splitStackingContexts(stack) {
var negativeZIndex = [];
var zeroOrAutoZIndexOrTransformedOrOpacity = [];
var positiveZIndex = [];
var nonPositionedFloats = [];
var nonPositionedInlineLevel = [];
var length = stack.contexts.length;
for (var i = 0; i < length; i++) {
var child = stack.contexts[i];
if (child.container.isPositioned() || child.container.style.opacity < 1 || child.container.isTransformed()) {
if (child.container.style.zIndex.order < 0) {
negativeZIndex.push(child);
} else if (child.container.style.zIndex.order > 0) {
positiveZIndex.push(child);
} else {
zeroOrAutoZIndexOrTransformedOrOpacity.push(child);
}
} else {
if (child.container.isFloating()) {
nonPositionedFloats.push(child);
} else {
nonPositionedInlineLevel.push(child);
}
}
}
return [negativeZIndex, zeroOrAutoZIndexOrTransformedOrOpacity, positiveZIndex, nonPositionedFloats, nonPositionedInlineLevel];
};
var sortByZIndex = function sortByZIndex(a, b) {
if (a.container.style.zIndex.order > b.container.style.zIndex.order) {
return 1;
} else if (a.container.style.zIndex.order < b.container.style.zIndex.order) {
return -1;
}
return a.container.index > b.container.index ? 1 : -1;
};