From 9beae48cf0e6da0cd3a9a120d3bd0d27006abbfb Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Sat, 1 Feb 2014 21:48:30 +0200 Subject: [PATCH] Start implementing background gradients --- .jshintrc | 2 +- src/gradientcontainer.js | 15 +++++++++++++ src/imageloader.js | 41 ++++++++++++++++++---------------- src/lineargradientcontainer.js | 33 +++++++++++++++++++++++++++ src/nodeparser.js | 4 +++- src/renderer.js | 27 ++++++++++++++++------ src/renderers/canvas.js | 7 ++++++ src/webkitgradientcontainer.js | 6 +++++ tests/test.js | 2 +- 9 files changed, 108 insertions(+), 29 deletions(-) create mode 100644 src/gradientcontainer.js create mode 100644 src/lineargradientcontainer.js create mode 100644 src/webkitgradientcontainer.js diff --git a/.jshintrc b/.jshintrc index 18ad9d5..4c8b1ec 100644 --- a/.jshintrc +++ b/.jshintrc @@ -14,5 +14,5 @@ "jQuery": true }, "predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise", - "ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "log", "smallImage"] + "ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "GradientContainer", "LinearGradientContainer", "WebkitGradientContainer", "log", "smallImage"] } diff --git a/src/gradientcontainer.js b/src/gradientcontainer.js new file mode 100644 index 0000000..907b629 --- /dev/null +++ b/src/gradientcontainer.js @@ -0,0 +1,15 @@ +function GradientContainer(imageData) { + this.src = imageData.value; + this.colorStops = []; + this.type = null; + this.x0 = 0.5; + this.y0 = 0.5; + this.x1 = 0.5; + this.y1 = 0.5; + this.promise = Promise.resolve(true); +} + +GradientContainer.prototype.TYPES = { + LINEAR: 1, + RADIAL: 2 +}; diff --git a/src/imageloader.js b/src/imageloader.js index 6fa6ef7..2860ed5 100644 --- a/src/imageloader.js +++ b/src/imageloader.js @@ -12,7 +12,7 @@ ImageLoader.prototype.findImages = function(nodes) { }; ImageLoader.prototype.findBackgroundImage = function(images, container) { - container.parseBackgroundImages().filter(this.isImageBackground).map(this.getBackgroundUrl).forEach(this.addImage(images, this.loadImage), this); + container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this); return images; }; @@ -20,30 +20,33 @@ ImageLoader.prototype.addImage = function(images, callback) { return function(newImage) { if (!this.imageExists(images, newImage)) { images.splice(0, 0, callback.apply(this, arguments)); - log('Added image #' + (images.length), newImage.substring(0, 100)); + log('Added image #' + (images.length), newImage); } }; }; -ImageLoader.prototype.getBackgroundUrl = function(imageData) { - return imageData.args[0]; +ImageLoader.prototype.hasImageBackground = function(imageData) { + return imageData.method !== "none"; }; -ImageLoader.prototype.isImageBackground = function(imageData) { - return imageData.method === "url"; -}; - -ImageLoader.prototype.loadImage = function(src) { - if (src.match(/data:image\/.*;base64,/i)) { - return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false); - } else if (this.isSameOrigin(src) || this.options.allowTaint === true) { - return new ImageContainer(src, false); - } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) { - return new ImageContainer(src, true); - } else if (this.options.proxy) { - return new ProxyImageContainer(src); - } else { - return new DummyImageContainer(src); +ImageLoader.prototype.loadImage = function(imageData) { + if (imageData.method === "url") { + var src = imageData.args[0]; + if (src.match(/data:image\/.*;base64,/i)) { + return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false); + } else if (this.isSameOrigin(src) || this.options.allowTaint === true) { + return new ImageContainer(src, false); + } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) { + return new ImageContainer(src, true); + } else if (this.options.proxy) { + return new ProxyImageContainer(src); + } else { + return new DummyImageContainer(src); + } + } else if (imageData.method === "linear-gradient") { + return new LinearGradientContainer(imageData); + } else if (imageData.method === "gradient") { + return new WebkitGradientContainer(imageData); } }; diff --git a/src/lineargradientcontainer.js b/src/lineargradientcontainer.js new file mode 100644 index 0000000..5147038 --- /dev/null +++ b/src/lineargradientcontainer.js @@ -0,0 +1,33 @@ +function LinearGradientContainer(imageData) { + GradientContainer.apply(this, arguments); + this.type = this.TYPES.LINEAR; + + imageData.args[0].split(" ").forEach(function(position) { + switch(position) { + case "left": + this.x0 = 0; + this.x1 = 1; + break; + case "top": + this.y0 = 0; + this.y1 = 1; + break; + case "right": + this.x0 = 1; + this.x1 = 0; + break; + case "bottom": + this.y0 = 1; + this.y1 = 0; + break; + } + }, this); + + imageData.args.slice(1).forEach(function(colorStop) { + // console.log(colorStop, colorStop.match(this.stepRegExp)); + }, this); +} + +LinearGradientContainer.prototype = Object.create(GradientContainer.prototype); + +LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/; diff --git a/src/nodeparser.js b/src/nodeparser.js index 8446e74..64c8fb9 100644 --- a/src/nodeparser.js +++ b/src/nodeparser.js @@ -73,11 +73,13 @@ NodeParser.prototype.getBounds = function(node) { if (node.getBoundingClientRect) { var clientRect = node.getBoundingClientRect(); var isBody = node.nodeName === "BODY"; + var width = isBody ? node.scrollWidth : node.offsetWidth; return { top: clientRect.top, bottom: clientRect.bottom || (clientRect.top + clientRect.height), + right: clientRect.left + width, left: clientRect.left, - width: isBody ? node.scrollWidth : node.offsetWidth, + width: width, height: isBody ? node.scrollHeight : node.offsetHeight }; } diff --git a/src/renderer.js b/src/renderer.js index 65c7864..b48caa8 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -51,13 +51,26 @@ Renderer.prototype.renderBorder = function(data) { Renderer.prototype.renderBackgroundImage = function(container, bounds) { var backgroundImages = container.parseBackgroundImages(); backgroundImages.reverse().forEach(function(backgroundImage, index, arr) { - if (backgroundImage.method === "url") { - var image = this.images.get(backgroundImage.args[0]); - if (image) { - this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1)); - } else { - log("Error loading background-image", backgroundImage.args[0]); - } + switch(backgroundImage.method) { + case "url": + var image = this.images.get(backgroundImage.args[0]); + if (image) { + this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1)); + } else { + log("Error loading background-image", backgroundImage.args[0]); + } + break; + case "linear-gradient": + case "gradient": + var gradientImage = this.images.get(backgroundImage.value); + if (gradientImage) { + this.renderBackgroundGradient(gradientImage, bounds); + } else { + log("Error loading background-image", backgroundImage.args[0]); + } + break; + default: + log("Unknown background-image type", backgroundImage.args[0]); } }, this); }; diff --git a/src/renderers/canvas.js b/src/renderers/canvas.js index dd13c23..c9e5480 100644 --- a/src/renderers/canvas.js +++ b/src/renderers/canvas.js @@ -96,6 +96,13 @@ CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backg this.ctx.translate(-offsetX, -offsetY); }; +CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) { + if (gradientImage instanceof LinearGradientContainer) { + var gradient = this.ctx.createLinearGradient(bounds.left, bounds.top, bounds.right, bounds.bottom); + //console.log(gradientImage, bounds, gradient); + } +}; + CanvasRenderer.prototype.resizeImage = function(imageContainer, size) { var image = imageContainer.image; if(image.width === size.width && image.height === size.height) { diff --git a/src/webkitgradientcontainer.js b/src/webkitgradientcontainer.js new file mode 100644 index 0000000..7b88015 --- /dev/null +++ b/src/webkitgradientcontainer.js @@ -0,0 +1,6 @@ +function WebkitGradientContainer(imageData) { + GradientContainer.apply(this, arguments); + this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL; +} + +WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype); diff --git a/tests/test.js b/tests/test.js index 8d52de6..5a53625 100644 --- a/tests/test.js +++ b/tests/test.js @@ -11,7 +11,7 @@ var h2cSelector, h2cOptions; document.write(srcStart + '/tests/assets/jquery-1.6.2.js' + scrEnd); document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd); - var html2canvas = ['log', 'nodecontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', + var html2canvas = ['log', 'nodecontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', 'gradientcontainer', 'lineargradientcontainer', 'webkitgradientcontainer', 'imageloader', 'nodeparser', 'font', 'fontmetrics', 'core', 'renderer', 'promise', 'renderers/canvas'], i; for (i = 0; i < html2canvas.length; ++i) { document.write(srcStart + '/src/' + html2canvas[i] + '.js?' + Math.random() + scrEnd);