mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Begin implementing background-image rendering
This commit is contained in:
18
src/core.js
18
src/core.js
@@ -3,9 +3,16 @@ window.html2canvas = function(nodeList, options) {
|
||||
var clonedWindow = container.contentWindow;
|
||||
var element = (nodeList === undefined) ? document.body : nodeList[0];
|
||||
var node = clonedWindow.document.documentElement;
|
||||
var support = new Support();
|
||||
var imageLoader = new ImageLoader(options, support);
|
||||
options = options || {};
|
||||
if (options.logging) {
|
||||
window.html2canvas.logging = true;
|
||||
window.html2canvas.start = Date.now();
|
||||
}
|
||||
|
||||
var renderer = new CanvasRenderer(documentWidth(), documentHeight());
|
||||
var parser = new NodeParser(node, renderer, options || {});
|
||||
var renderer = new CanvasRenderer(documentWidth(), documentHeight(), imageLoader);
|
||||
var parser = new NodeParser(node, renderer, support, imageLoader, options);
|
||||
|
||||
window.console.log(parser);
|
||||
};
|
||||
@@ -43,21 +50,22 @@ function createWindowClone(ownerDocument, width, height) {
|
||||
return container;
|
||||
}
|
||||
|
||||
function NodeParser(element, renderer, options) {
|
||||
function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
this.renderer = renderer;
|
||||
this.options = options;
|
||||
this.support = new Support();
|
||||
this.range = null;
|
||||
this.support = support;
|
||||
this.stack = new StackingContext(true, 1, element.ownerDocument, null);
|
||||
var parent = new NodeContainer(element, null);
|
||||
parent.visibile = parent.isElementVisible();
|
||||
this.nodes = [parent].concat(this.getChildren(parent)).filter(function(container) {
|
||||
return container.visible = container.isElementVisible();
|
||||
});
|
||||
this.images = new ImageLoader(this.nodes.filter(isElement), options, this.support);
|
||||
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
||||
this.createStackingContexts();
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.images.ready.then(bind(function() {
|
||||
log("Images loaded, starting parsing");
|
||||
this.parse(this.stack);
|
||||
options.onrendered(renderer.canvas);
|
||||
}, this));
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
function ImageLoader(nodes, options, support) {
|
||||
function ImageLoader(options, support) {
|
||||
this.link = null;
|
||||
this.options = options;
|
||||
this.support = support;
|
||||
this.origin = window.location.protocol + window.location.host;
|
||||
this.images = nodes.reduce(bind(this.findImages, this), []);
|
||||
this.ready = Promise.all(this.images.map(this.getPromise));
|
||||
}
|
||||
|
||||
ImageLoader.prototype.findImages = function(images, container) {
|
||||
@@ -54,3 +52,16 @@ ImageLoader.prototype.isSameOrigin = function(url) {
|
||||
ImageLoader.prototype.getPromise = function(container) {
|
||||
return container.promise;
|
||||
};
|
||||
|
||||
ImageLoader.prototype.get = function(src) {
|
||||
var found = null;
|
||||
return this.images.some(function(img) {
|
||||
return (found = img).src === src;
|
||||
}) ? found : null;
|
||||
};
|
||||
|
||||
ImageLoader.prototype.fetch = function(nodes) {
|
||||
this.images = nodes.reduce(bind(this.findImages, this), []);
|
||||
this.ready = Promise.all(this.images.map(this.getPromise));
|
||||
return this;
|
||||
};
|
||||
|
||||
5
src/log.js
Normal file
5
src/log.js
Normal file
@@ -0,0 +1,5 @@
|
||||
function log() {
|
||||
if (window.html2canvas.logging && window.console && window.console.log) {
|
||||
window.console.log.apply(window.console, [(Date.now() - window.html2canvas.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0)));
|
||||
}
|
||||
}
|
||||
@@ -146,3 +146,76 @@ NodeContainer.prototype.parseBackgroundImages = function() {
|
||||
|
||||
return this.backgroundImages || (this.backgroundImages = results);
|
||||
};
|
||||
|
||||
NodeContainer.prototype.cssList = function(property, index) {
|
||||
var value = (this.css(property) || '').split(',');
|
||||
value = value[index || 0] || value[0] || 'auto';
|
||||
value = value.trim().split(' ');
|
||||
if (value.length === 1) {
|
||||
value = [value[0], value[0]];
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) {
|
||||
var size = this.cssList("backgroundSize", index);
|
||||
var width, height;
|
||||
|
||||
if (isPercentage(size[0])) {
|
||||
width = bounds.width * parseFloat(size[0]) / 100;
|
||||
} else if (/contain|cover/.test(size[0])) {
|
||||
var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height;
|
||||
return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio};
|
||||
} else {
|
||||
width = parseInt(size[0], 10);
|
||||
}
|
||||
|
||||
if (size[0] === 'auto' && size[1] === 'auto') {
|
||||
height = image.height;
|
||||
} else if (size[1] === 'auto') {
|
||||
height = width / image.width * image.height;
|
||||
} else if (isPercentage(size[1])) {
|
||||
height = bounds.height * parseFloat(size[1]) / 100;
|
||||
} else {
|
||||
height = parseInt(size[1], 10);
|
||||
}
|
||||
|
||||
if (size[0] === 'auto') {
|
||||
width = height / image.height * image.width;
|
||||
}
|
||||
|
||||
return {width: width, height: height};
|
||||
};
|
||||
|
||||
NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) {
|
||||
var position = this.cssList('backgroundPosition', index);
|
||||
var left, top;
|
||||
|
||||
if (isPercentage(position[0])){
|
||||
left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100);
|
||||
} else {
|
||||
left = parseInt(position[0], 10);
|
||||
}
|
||||
|
||||
if (position[1] === 'auto') {
|
||||
top = left / image.width * image.height;
|
||||
} else if (isPercentage(position[1])){
|
||||
top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100;
|
||||
} else {
|
||||
top = parseInt(position[1], 10);
|
||||
}
|
||||
|
||||
if (position[0] === 'auto') {
|
||||
left = top / image.height * image.width;
|
||||
}
|
||||
|
||||
return {left: left, top: top};
|
||||
};
|
||||
|
||||
NodeContainer.prototype.parseBackgroundRepeat = function(index) {
|
||||
return this.cssList("backgroundRepeat", index)[0];
|
||||
};
|
||||
|
||||
function isPercentage(value) {
|
||||
return value.toString().indexOf("%") !== -1;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
function Renderer() {}
|
||||
function NYI() {
|
||||
return function() {
|
||||
throw new Error("Render function not implemented");
|
||||
};
|
||||
function Renderer(width, height, images) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
Renderer.prototype.renderBackground = function(container, bounds) {
|
||||
@@ -15,13 +14,7 @@ Renderer.prototype.renderBackground = function(container, bounds) {
|
||||
Renderer.prototype.renderBackgroundColor = function(container, bounds) {
|
||||
var color = container.css("backgroundColor");
|
||||
if (!this.isTransparent(color)) {
|
||||
this.rectangle(
|
||||
bounds.left,
|
||||
bounds.top,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
container.css("backgroundColor")
|
||||
);
|
||||
this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, container.css("backgroundColor"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,13 +29,42 @@ Renderer.prototype.renderBorder = function(data) {
|
||||
};
|
||||
|
||||
Renderer.prototype.renderBackgroundImage = function(container, bounds) {
|
||||
var backgroundImages = container.parseBackgroundImages();
|
||||
backgroundImages.reverse().forEach(function(backgroundImage, index) {
|
||||
if (backgroundImage.method === "url") {
|
||||
var image = this.images.get(backgroundImage.args[0]);
|
||||
if (image) {
|
||||
this.renderBackgroundRepeating(container, bounds, image, index);
|
||||
} else {
|
||||
log("Error loading background-image", backgroundImage.args[0]);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index) {
|
||||
var size = container.parseBackgroundSize(bounds, imageContainer.image, index);
|
||||
var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size);
|
||||
var repeat = container.parseBackgroundRepeat(index);
|
||||
// image = resizeImage(image, backgroundSize);
|
||||
switch (repeat) {
|
||||
case "repeat-x":
|
||||
case "repeat no-repeat":
|
||||
this.backgroundRepeatShape(imageContainer, position, bounds, bounds.left, bounds.top + position.top, 99999, imageContainer.image.height);
|
||||
break;
|
||||
case "repeat-y":
|
||||
case "no-repeat repeat":
|
||||
this.backgroundRepeatShape(imageContainer, position, bounds, bounds.left + position.left, bounds.top, imageContainer.image.width, 99999);
|
||||
break;
|
||||
case "no-repeat":
|
||||
this.backgroundRepeatShape(imageContainer, position, bounds, bounds.left + position.left, bounds.top + position.top, imageContainer.image.width, imageContainer.image.height);
|
||||
break;
|
||||
default:
|
||||
this.renderBackgroundRepeat(imageContainer, position, {top: bounds.top, left: bounds.left});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Renderer.prototype.isTransparent = function(color) {
|
||||
return (!color || color === "transparent" || color === "rgba(0, 0, 0, 0)");
|
||||
};
|
||||
|
||||
Renderer.prototype.clip = NYI();
|
||||
Renderer.prototype.rectangle = NYI();
|
||||
Renderer.prototype.shape = NYI();
|
||||
|
||||
@@ -50,3 +50,24 @@ CanvasRenderer.prototype.setOpacity = function(opacity) {
|
||||
CanvasRenderer.prototype.text = function(text, left, bottom) {
|
||||
this.ctx.fillText(text, left, bottom);
|
||||
};
|
||||
|
||||
CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, bounds, left, top, width, height) {
|
||||
var shape = [
|
||||
["line", Math.round(left), Math.round(top)],
|
||||
["line", Math.round(left + width), Math.round(top)],
|
||||
["line", Math.round(left + width), Math.round(height + top)],
|
||||
["line", Math.round(left), Math.round(height + top)]
|
||||
];
|
||||
console.log(shape);
|
||||
this.clip(shape, function() {
|
||||
this.renderBackgroundRepeat(imageContainer, backgroundPosition, bounds);
|
||||
}, this);
|
||||
};
|
||||
|
||||
CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, bounds) {
|
||||
var offsetX = Math.round(bounds.left + backgroundPosition.left), offsetY = Math.round(bounds.top + backgroundPosition.top);
|
||||
this.setFillStyle(this.ctx.createPattern(imageContainer.image, "repeat"));
|
||||
this.ctx.translate(offsetX, offsetY);
|
||||
this.ctx.fill();
|
||||
this.ctx.translate(-offsetX, -offsetY);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user