html2canvas
Version:
Screenshots with JavaScript
255 lines (222 loc) • 11.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _Path = require('../drawing/Path');
var _textDecoration = require('../parsing/textDecoration');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var addColorStops = function addColorStops(gradient, canvasGradient) {
var maxStop = Math.max.apply(null, gradient.colorStops.map(function (colorStop) {
return colorStop.stop;
}));
var f = 1 / Math.max(1, maxStop);
gradient.colorStops.forEach(function (colorStop) {
canvasGradient.addColorStop(f * colorStop.stop, colorStop.color.toString());
});
};
var CanvasRenderer = function () {
function CanvasRenderer(canvas) {
_classCallCheck(this, CanvasRenderer);
this.canvas = canvas ? canvas : document.createElement('canvas');
}
_createClass(CanvasRenderer, [{
key: 'render',
value: function render(options) {
this.ctx = this.canvas.getContext('2d');
this.options = options;
this.canvas.width = Math.floor(options.width * options.scale);
this.canvas.height = Math.floor(options.height * options.scale);
this.canvas.style.width = options.width + 'px';
this.canvas.style.height = options.height + 'px';
this.ctx.scale(this.options.scale, this.options.scale);
this.ctx.translate(-options.x, -options.y);
this.ctx.textBaseline = 'bottom';
options.logger.log('Canvas renderer initialized (' + options.width + 'x' + options.height + ' at ' + options.x + ',' + options.y + ') with scale ' + this.options.scale);
}
}, {
key: 'clip',
value: function clip(clipPaths, callback) {
var _this = this;
if (clipPaths.length) {
this.ctx.save();
clipPaths.forEach(function (path) {
_this.path(path);
_this.ctx.clip();
});
}
callback();
if (clipPaths.length) {
this.ctx.restore();
}
}
}, {
key: 'drawImage',
value: function drawImage(image, source, destination) {
this.ctx.drawImage(image, source.left, source.top, source.width, source.height, destination.left, destination.top, destination.width, destination.height);
}
}, {
key: 'drawShape',
value: function drawShape(path, color) {
this.path(path);
this.ctx.fillStyle = color.toString();
this.ctx.fill();
}
}, {
key: 'fill',
value: function fill(color) {
this.ctx.fillStyle = color.toString();
this.ctx.fill();
}
}, {
key: 'getTarget',
value: function getTarget() {
this.canvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0);
return Promise.resolve(this.canvas);
}
}, {
key: 'path',
value: function path(_path) {
var _this2 = this;
this.ctx.beginPath();
if (Array.isArray(_path)) {
_path.forEach(function (point, index) {
var start = point.type === _Path.PATH.VECTOR ? point : point.start;
if (index === 0) {
_this2.ctx.moveTo(start.x, start.y);
} else {
_this2.ctx.lineTo(start.x, start.y);
}
if (point.type === _Path.PATH.BEZIER_CURVE) {
_this2.ctx.bezierCurveTo(point.startControl.x, point.startControl.y, point.endControl.x, point.endControl.y, point.end.x, point.end.y);
}
});
} else {
this.ctx.arc(_path.x + _path.radius, _path.y + _path.radius, _path.radius, 0, Math.PI * 2, true);
}
this.ctx.closePath();
}
}, {
key: 'rectangle',
value: function rectangle(x, y, width, height, color) {
this.ctx.fillStyle = color.toString();
this.ctx.fillRect(x, y, width, height);
}
}, {
key: 'renderLinearGradient',
value: function renderLinearGradient(bounds, gradient) {
var linearGradient = this.ctx.createLinearGradient(bounds.left + gradient.direction.x1, bounds.top + gradient.direction.y1, bounds.left + gradient.direction.x0, bounds.top + gradient.direction.y0);
addColorStops(gradient, linearGradient);
this.ctx.fillStyle = linearGradient;
this.ctx.fillRect(bounds.left, bounds.top, bounds.width, bounds.height);
}
}, {
key: 'renderRadialGradient',
value: function renderRadialGradient(bounds, gradient) {
var _this3 = this;
var x = bounds.left + gradient.center.x;
var y = bounds.top + gradient.center.y;
var radialGradient = this.ctx.createRadialGradient(x, y, 0, x, y, gradient.radius.x);
if (!radialGradient) {
return;
}
addColorStops(gradient, radialGradient);
this.ctx.fillStyle = radialGradient;
if (gradient.radius.x !== gradient.radius.y) {
// transforms for elliptical radial gradient
var midX = bounds.left + 0.5 * bounds.width;
var midY = bounds.top + 0.5 * bounds.height;
var f = gradient.radius.y / gradient.radius.x;
var invF = 1 / f;
this.transform(midX, midY, [1, 0, 0, f, 0, 0], function () {
return _this3.ctx.fillRect(bounds.left, invF * (bounds.top - midY) + midY, bounds.width, bounds.height * invF);
});
} else {
this.ctx.fillRect(bounds.left, bounds.top, bounds.width, bounds.height);
}
}
}, {
key: 'renderRepeat',
value: function renderRepeat(path, image, imageSize, offsetX, offsetY) {
this.path(path);
this.ctx.fillStyle = this.ctx.createPattern(this.resizeImage(image, imageSize), 'repeat');
this.ctx.translate(offsetX, offsetY);
this.ctx.fill();
this.ctx.translate(-offsetX, -offsetY);
}
}, {
key: 'renderTextNode',
value: function renderTextNode(textBounds, color, font, textDecoration, textShadows) {
var _this4 = this;
this.ctx.font = [font.fontStyle, font.fontVariant, font.fontWeight, font.fontSize, font.fontFamily].join(' ');
textBounds.forEach(function (text) {
_this4.ctx.fillStyle = color.toString();
if (textShadows && text.text.trim().length) {
textShadows.slice(0).reverse().forEach(function (textShadow) {
_this4.ctx.shadowColor = textShadow.color.toString();
_this4.ctx.shadowOffsetX = textShadow.offsetX * _this4.options.scale;
_this4.ctx.shadowOffsetY = textShadow.offsetY * _this4.options.scale;
_this4.ctx.shadowBlur = textShadow.blur;
_this4.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
});
} else {
_this4.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
}
if (textDecoration !== null) {
var textDecorationColor = textDecoration.textDecorationColor || color;
textDecoration.textDecorationLine.forEach(function (textDecorationLine) {
switch (textDecorationLine) {
case _textDecoration.TEXT_DECORATION_LINE.UNDERLINE:
// Draws a line at the baseline of the font
// TODO As some browsers display the line as more than 1px if the font-size is big,
// need to take that into account both in position and size
var _options$fontMetrics$ = _this4.options.fontMetrics.getMetrics(font),
baseline = _options$fontMetrics$.baseline;
_this4.rectangle(text.bounds.left, Math.round(text.bounds.top + baseline), text.bounds.width, 1, textDecorationColor);
break;
case _textDecoration.TEXT_DECORATION_LINE.OVERLINE:
_this4.rectangle(text.bounds.left, Math.round(text.bounds.top), text.bounds.width, 1, textDecorationColor);
break;
case _textDecoration.TEXT_DECORATION_LINE.LINE_THROUGH:
// TODO try and find exact position for line-through
var _options$fontMetrics$2 = _this4.options.fontMetrics.getMetrics(font),
middle = _options$fontMetrics$2.middle;
_this4.rectangle(text.bounds.left, Math.ceil(text.bounds.top + middle), text.bounds.width, 1, textDecorationColor);
break;
}
});
}
});
}
}, {
key: 'resizeImage',
value: function resizeImage(image, size) {
if (image.width === size.width && image.height === size.height) {
return image;
}
var canvas = this.canvas.ownerDocument.createElement('canvas');
canvas.width = size.width;
canvas.height = size.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height);
return canvas;
}
}, {
key: 'setOpacity',
value: function setOpacity(opacity) {
this.ctx.globalAlpha = opacity;
}
}, {
key: 'transform',
value: function transform(offsetX, offsetY, matrix, callback) {
this.ctx.save();
this.ctx.translate(offsetX, offsetY);
this.ctx.transform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
this.ctx.translate(-offsetX, -offsetY);
callback();
this.ctx.restore();
}
}]);
return CanvasRenderer;
}();
exports.default = CanvasRenderer;