From 9180de3e296841973d000b1af0df65ce79684b81 Mon Sep 17 00:00:00 2001 From: Michael Hemesath Date: Sat, 30 Jul 2011 20:48:05 -0500 Subject: [PATCH 1/4] Added SVG Gradient Generation Support --- lib/nib.js | 1 + lib/nib/gradients.styl | 10 +++ lib/nodes/gradient.js | 150 ++++++++++++++++++++++++++++------------- test/gradients.styl | 40 ++++++++--- test/index.jade | 6 ++ 5 files changed, 152 insertions(+), 55 deletions(-) diff --git a/lib/nib.js b/lib/nib.js index 0d3c14c4..e338f8cb 100644 --- a/lib/nib.js +++ b/lib/nib.js @@ -51,6 +51,7 @@ function plugin() { if (gradient) { style.define('create-gradient-image', gradient.create) style.define('gradient-data-uri', gradient.dataURL) + style.define('gradient-svg-data-uri', gradient.svgDataURL) style.define('add-color-stop', gradient.addColorStop) style.define('has-canvas', nodes.true); } else { diff --git a/lib/nib/gradients.styl b/lib/nib/gradients.styl index d0ab996c..88edf2e8 100644 --- a/lib/nib/gradients.styl +++ b/lib/nib/gradients.styl @@ -144,3 +144,13 @@ linear-gradient-image(start, stops...) stops = normalize-stops(stops) add-color-stop(grad, stop[0], stop[1]) for stop in stops 'url(%s)' % gradient-data-uri(grad) + +linear-gradient-svg(start, stops...) + stops = stops[0] if length(stops) == 1 + error('gradient image size required') unless start[0] is a 'unit' + size = start[0] + start = start[1] or 'top' + grad = create-gradient-image(size, start) + stops = normalize-stops(stops) + add-color-stop(grad, stop[0], stop[1]) for stop in stops + 'url(%s)' % gradient-svg-data-uri(grad) diff --git a/lib/nodes/gradient.js b/lib/nodes/gradient.js index d1aea9bf..c7767857 100644 --- a/lib/nodes/gradient.js +++ b/lib/nodes/gradient.js @@ -5,6 +5,7 @@ var stylus = require('stylus') , Canvas = require('canvas') + , jade = require('jade') , nodes = stylus.nodes , utils = stylus.utils; @@ -61,6 +62,11 @@ exports.dataURL = function(grad){ return new nodes.String(grad.toDataURL()); }; +exports.svgDataURL = function(grad){ + utils.assertType(grad, 'gradient'); + return new nodes.String(grad.toSVGDataURL()); +}; + /** * Initialize a new `Gradient` node with the given `size` * and `start` position. @@ -72,12 +78,8 @@ exports.dataURL = function(grad){ function Gradient(size, start) { this.size = size; - this.canvas = new Canvas(1, 1); - this.setStartPosition(start); - this.ctx = this.canvas.getContext('2d'); - this.grad = this.ctx.createLinearGradient( - this.from[0], this.from[1] - , this.to[0], this.to[1]); + this.start = start; + this.colorStops = []; }; /** @@ -95,68 +97,126 @@ Gradient.prototype.toString = function(){ }; /** - * Set `start` position. + * Add color stop `pos` / `color`. * - * @param {String} start + * @param {Number} pos + * @param {String} color * @api private */ -Gradient.prototype.setStartPosition = function(start){ - var size = this.size - , canvas = this.canvas; +Gradient.prototype.addColorStop = function(pos, color){ + this.colorStops.push({ pos: pos, color: color }) +}; - switch (start) { +/** + * Return a PNG data URI string. + * + * @return {String} + * @api private + */ + +Gradient.prototype.toDataURL = function(){ + return this.toPNGDataURL(); +}; + +/** + * Return a PNG data URI string. + * + * @return {String} + * @api private + */ + +Gradient.prototype.toPNGDataURL = function() { + var canvas = new Canvas(1, 1) + , from + , to + , ctx + , grad; + + switch (this.start) { case 'top': - canvas.height = size; - this.from = [canvas.width / 2, 0]; - this.to = [canvas.width / 2, canvas.height]; + canvas.height = this.size; + from = [canvas.width / 2, 0]; + to = [canvas.width / 2, canvas.height]; break; case 'bottom': - canvas.height = size; - this.from = [canvas.width / 2, canvas.height]; - this.to = [canvas.width / 2, 0]; + canvas.height = this.size; + from = [canvas.width / 2, canvas.height]; + to = [canvas.width / 2, 0]; break; case 'left': - canvas.width = size; - this.from = [0, 0]; - this.to = [canvas.width, canvas.height]; + canvas.width = this.size; + from = [0, 0]; + to = [canvas.width, canvas.height]; break; case 'right': - canvas.width = size; - this.from = [canvas.width, canvas.height]; - this.to = [0, 0]; + canvas.width = this.size; + from = [canvas.width, canvas.height]; + to = [0, 0]; break; default: throw new Error('invalid start position "' + start + '"'); } -}; - -/** - * Add color stop `pos` / `color`. - * - * @param {Number} pos - * @param {String} color - * @api private - */ - -Gradient.prototype.addColorStop = function(pos, color){ - this.grad.addColorStop(pos, color); -}; + + ctx = canvas.getContext('2d'); + grad = ctx.createLinearGradient( + from[0], from[1] + , to[0], to[1]); + + this.colorStops.forEach(function(colorStop) { + grad.addColorStop(colorStop.pos, colorStop.color); + }); + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return canvas.toDataURL(); +} /** - * Return data URI string. + * Return a SVG data URI string. * * @return {String} * @api private */ -Gradient.prototype.toDataURL = function(){ - var canvas = this.canvas - , ctx = this.ctx; - ctx.fillStyle = this.grad; - ctx.fillRect(0, 0, canvas.width, canvas.height); - return canvas.toDataURL(); -}; +Gradient.prototype.toSVGDataURL = function() { + var x1 + , y1 + , x2 + , y2; + + switch (this.start) { + case 'top': + x1 = x2 = y1 = '0%'; + y2 = '100%'; + break; + case 'bottom': + x1 = x2 = y2 = '0%'; + y1 = '100%'; + break; + case 'left': + x1 = y1 = y1 = '0%'; + x2 = '100%'; + break; + case 'right': + x2 = y1 = y1 = '0%'; + x1 = '100%'; + break; + default: + throw new Error('invalid start position "' + start + '"'); + } + + var svg = jade.render('svg(xmlns="http://www.w3.org/2000/svg", viewBox="0 0 1 1", preserveAspectRatio="none", version="1.1")\n' + + ' defs\n' + + ' linearGradient(id="g", x1=x1, y1=y1, x2=x2, y2=y2)\n' + + ' - colorStops.forEach(function(colorStop) {\n' + + ' stop(offset=colorStop.pos, stop-color=colorStop.color)\n' + + ' - })\n' + + ' rect(fill="url(#g)", width="1", height="1")\n', { locals: { colorStops: this.colorStops, x1: x1, x2: x2, y1: y1, y2: y2 }}); + + console.log(svg); + return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64'); +} /** * Inherit from `nodes.Node.prototype`. diff --git a/test/gradients.styl b/test/gradients.styl index b35418d1..00a62ffd 100644 --- a/test/gradients.styl +++ b/test/gradients.styl @@ -1,7 +1,7 @@ @import 'nib/gradients' -#gradients tr +#gradients tbody tr height: 50px color: white td @@ -14,43 +14,63 @@ tr:nth-child(1) td:first-child background: linear-gradient(top, yellow, blue) - td:last-child + td:nth-child(2) background: linear-gradient-image(50px, yellow, blue) + td:last-child + background: linear-gradient-svg(50px, yellow, blue) tr:nth-child(2) td:first-child background: linear-gradient(top, red, green, yellow, blue) - td:last-child + td:nth-child(2) background: linear-gradient-image(50px, red, green, yellow, blue) + td:last-child + background: linear-gradient-svg(50px, red, green, yellow, blue) tr:nth-child(3) td:first-child background: linear-gradient(top, red, green 10%, 90% yellow, blue) - td:last-child + td:nth-child(2) background: linear-gradient-image(50px, red, green 10%, 90% yellow, blue) + td:last-child + background: linear-gradient-svg(50px, red, green 10%, 90% yellow, blue) tr:nth-child(4) td:first-child background: linear-gradient(top, red 15, green 80%, white, 90% yellow, blue) - td:last-child + td:nth-child(2) background: linear-gradient-image(50px, red 15, green 80%, white, 90% yellow, blue) + td:last-child + background: linear-gradient-svg(50px, red 15, green 80%, white, 90% yellow, blue) tr:nth-child(5) td:first-child background: linear-gradient(bottom, #fff, #000) - td:last-child + td:nth-child(2) background: linear-gradient-image(50px bottom, #fff, #000) + td:last-child + background: linear-gradient-svg(50px bottom, #fff, #000) tr:nth-child(6) td:first-child background: linear-gradient(left, #fff, #000) - td:last-child + td:nth-child(2) background: linear-gradient-image(150px left, #fff, #000) + td:last-child + background: linear-gradient-svg(150px left, #fff, #000) tr:nth-child(7) td:first-child background: linear-gradient(right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) - td:last-child + td:nth-child(2) background: linear-gradient-image(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) + td:last-child + background: linear-gradient-svg(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) tr:nth-child(8) td:first-child background: linear-gradient(top, red, 50% green, blue) - td:last-child + td:nth-child(2) background: linear-gradient-image(50px top, red, 50% green, blue) + td:last-child + background: linear-gradient-svg(50px top, red, 50% green, blue) tr:nth-child(9) td:first-child - background: linear-gradient(50px top, red, green, yellow, blue) \ No newline at end of file + background: linear-gradient(50px top, red, green, yellow, blue) + td:nth-child(2) + background: linear-gradient-image(50px top, red, green, yellow, blue) + td:last-child + background: linear-gradient-svg(50px top, red, green, yellow, blue) \ No newline at end of file diff --git a/test/index.jade b/test/index.jade index 773d061f..7db762f8 100644 --- a/test/index.jade +++ b/test/index.jade @@ -15,12 +15,18 @@ html body h2 Gradients table#gradients + thead + tr + th CSS + th PNG Data URI + th SVG Data URI - var n = 9 tbody - while (n--) tr td td + td h2 Buttons table#buttons tbody From e4128695b8d3325b65d594e5be5573ebf23cab62 Mon Sep 17 00:00:00 2001 From: Michael Hemesath Date: Fri, 5 Aug 2011 09:03:30 -0500 Subject: [PATCH 2/4] Added gradient formats config options. --- lib/nib.js | 16 +++++----- lib/nib/config.styl | 6 ++++ lib/nib/gradients.styl | 70 ++++++++++++++++++++++++------------------ lib/nodes/gradient.js | 34 +++++++++----------- test/gradients.styl | 38 +++++++++++------------ 5 files changed, 86 insertions(+), 78 deletions(-) diff --git a/lib/nib.js b/lib/nib.js index e338f8cb..92b2c3ca 100644 --- a/lib/nib.js +++ b/lib/nib.js @@ -12,16 +12,13 @@ var stylus = require('stylus') , nodes = stylus.nodes , utils = stylus.utils - , gradient + , gradient = require('./nodes/gradient') , Canvas; exports = module.exports = plugin; -// conditionally expose gradient api - try { - require('canvas'); - gradient = require('./nodes/gradient'); + Canvas = require('canvas'); } catch (err) { // ignore } @@ -48,11 +45,12 @@ exports.path = __dirname; function plugin() { return function(style){ style.include(__dirname); - if (gradient) { - style.define('create-gradient-image', gradient.create) + style.define('create-gradient-image', gradient.create) + style.define('gradient-svg-data-uri', gradient.svgDataURL) + style.define('add-color-stop', gradient.addColorStop) + + if (Canvas) { style.define('gradient-data-uri', gradient.dataURL) - style.define('gradient-svg-data-uri', gradient.svgDataURL) - style.define('add-color-stop', gradient.addColorStop) style.define('has-canvas', nodes.true); } else { style.define('has-canvas', nodes.false); diff --git a/lib/nib/config.styl b/lib/nib/config.styl index a254587c..cdd8c55e 100644 --- a/lib/nib/config.styl +++ b/lib/nib/config.styl @@ -10,3 +10,9 @@ support-for-ie = true */ vendor-prefixes ?= webkit moz official + +/* + * Default vendor prefixes. + */ + +gradient-formats = png svg css \ No newline at end of file diff --git a/lib/nib/gradients.styl b/lib/nib/gradients.styl index 88edf2e8..60ee705a 100644 --- a/lib/nib/gradients.styl +++ b/lib/nib/gradients.styl @@ -74,6 +74,21 @@ join-stops(stops, translate) str += translate(color, pos) unquote(str) + +/* +* Build a linear gradient object with the given start position +* and variable number of color stops. +*/ + +build-gradient(start, stops) + stops = stops[0] if length(stops) == 1 + size = start[0] + start = start[1] or 'top' + grad = create-gradient-image(size, start) + stops = normalize-stops(stops) + add-color-stop(grad, stop[0], stop[1]) for stop in stops + grad + /* * Legacy Webkit color stop. */ @@ -101,33 +116,40 @@ std-stop(color, pos) * */ -linear-gradient(start, stops...) +linear-gradient(start, stops..., formats=gradient-formats) error('color stops required') unless length(stops) prop = current-property[0] val = current-property[1] stops = normalize-stops(stops) + // gradient image - if start[0] is a 'unit' - if has-canvas + if start[0] is a 'unit' + if png in formats img = linear-gradient-image(start, stops) add-property(prop, replace(val, '__CALL__', img)) - start = start[1] + + if svg in formats + img = linear-gradient-svg(start, stops) + add-property(prop, replace(val, '__CALL__', img)) + start = start[1] + - // legacy webkit - end = grad-point(opposite-position(start)) - webkit-legacy = '-webkit-gradient(linear, %s, %s, %s)' % (grad-point(start) end join-stops(stops, webkit-stop)) - add-property(prop, replace(val, '__CALL__', webkit-legacy)) + if css in formats + // legacy webkit + end = grad-point(opposite-position(start)) + webkit-legacy = '-webkit-gradient(linear, %s, %s, %s)' % (grad-point(start) end join-stops(stops, webkit-stop)) + add-property(prop, replace(val, '__CALL__', webkit-legacy)) - // vendor prefixed - stops = join-stops(stops, std-stop) - for prefix in vendor-prefixes - unless prefix == official - gradient = '-%s-linear-gradient(%s, %s)' % (prefix start stops) - add-property(prop, replace(val, '__CALL__', gradient)) + // vendor prefixed + stops = join-stops(stops, std-stop) + for prefix in vendor-prefixes + unless prefix == official + gradient = '-%s-linear-gradient(%s, %s)' % (prefix start stops) + add-property(prop, replace(val, '__CALL__', gradient)) - // standard - 'linear-gradient(%s, %s)' % (start stops) + // standard + 'linear-gradient(%s, %s)' % (start stops) /* * Create a linear gradient image with the given start position @@ -135,22 +157,10 @@ linear-gradient(start, stops...) */ linear-gradient-image(start, stops...) - error('node-canvas is required for linear-gradient-image()') unless has-canvas - stops = stops[0] if length(stops) == 1 error('gradient image size required') unless start[0] is a 'unit' - size = start[0] - start = start[1] or 'top' - grad = create-gradient-image(size, start) - stops = normalize-stops(stops) - add-color-stop(grad, stop[0], stop[1]) for stop in stops + grad = build-gradient(start, stops) 'url(%s)' % gradient-data-uri(grad) linear-gradient-svg(start, stops...) - stops = stops[0] if length(stops) == 1 - error('gradient image size required') unless start[0] is a 'unit' - size = start[0] - start = start[1] or 'top' - grad = create-gradient-image(size, start) - stops = normalize-stops(stops) - add-color-stop(grad, stop[0], stop[1]) for stop in stops + grad = build-gradient(start, stops) 'url(%s)' % gradient-svg-data-uri(grad) diff --git a/lib/nodes/gradient.js b/lib/nodes/gradient.js index c7767857..cd8fbe58 100644 --- a/lib/nodes/gradient.js +++ b/lib/nodes/gradient.js @@ -4,11 +4,19 @@ */ var stylus = require('stylus') - , Canvas = require('canvas') + , Canvas , jade = require('jade') + , readFileSync = require('fs').readFileSync , nodes = stylus.nodes - , utils = stylus.utils; - + , utils = stylus.utils + , renderSVGLinearGradient = jade.compile(readFileSync(__dirname + '/../templates/linearGradient.jade', 'utf8')); + +try { + Canvas = require('canvas'); +} catch (err) { + // ignore +} + /** * Expose `Gradient`. */ @@ -180,41 +188,27 @@ Gradient.prototype.toPNGDataURL = function() { */ Gradient.prototype.toSVGDataURL = function() { - var x1 - , y1 - , x2 - , y2; + var x1 = y1 = x2 = y2 = "0%" + , svg; switch (this.start) { case 'top': - x1 = x2 = y1 = '0%'; y2 = '100%'; break; case 'bottom': - x1 = x2 = y2 = '0%'; y1 = '100%'; break; case 'left': - x1 = y1 = y1 = '0%'; x2 = '100%'; break; case 'right': - x2 = y1 = y1 = '0%'; x1 = '100%'; break; default: throw new Error('invalid start position "' + start + '"'); } - var svg = jade.render('svg(xmlns="http://www.w3.org/2000/svg", viewBox="0 0 1 1", preserveAspectRatio="none", version="1.1")\n' - + ' defs\n' - + ' linearGradient(id="g", x1=x1, y1=y1, x2=x2, y2=y2)\n' - + ' - colorStops.forEach(function(colorStop) {\n' - + ' stop(offset=colorStop.pos, stop-color=colorStop.color)\n' - + ' - })\n' - + ' rect(fill="url(#g)", width="1", height="1")\n', { locals: { colorStops: this.colorStops, x1: x1, x2: x2, y1: y1, y2: y2 }}); - - console.log(svg); + svg = renderSVGLinearGradient.call(this, { colorStops: this.colorStops, x1: x1, x2: x2, y1: y1, y2: y2 }) return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64'); } diff --git a/test/gradients.styl b/test/gradients.styl index 00a62ffd..fce77f93 100644 --- a/test/gradients.styl +++ b/test/gradients.styl @@ -15,62 +15,62 @@ td:first-child background: linear-gradient(top, yellow, blue) td:nth-child(2) - background: linear-gradient-image(50px, yellow, blue) + background: linear-gradient(50px, yellow, blue, formats: png) td:last-child - background: linear-gradient-svg(50px, yellow, blue) + background: linear-gradient(50px, yellow, blue, formats: svg) tr:nth-child(2) td:first-child background: linear-gradient(top, red, green, yellow, blue) td:nth-child(2) - background: linear-gradient-image(50px, red, green, yellow, blue) + background: linear-gradient(50px, red, green, yellow, blue, formats: png) td:last-child - background: linear-gradient-svg(50px, red, green, yellow, blue) + background: linear-gradient(50px, red, green, yellow, blue, formats: svg) tr:nth-child(3) td:first-child background: linear-gradient(top, red, green 10%, 90% yellow, blue) td:nth-child(2) - background: linear-gradient-image(50px, red, green 10%, 90% yellow, blue) + background: linear-gradient(50px, red, green 10%, 90% yellow, blue, formats: png) td:last-child - background: linear-gradient-svg(50px, red, green 10%, 90% yellow, blue) + background: linear-gradient(50px, red, green 10%, 90% yellow, blue, formats: svg) tr:nth-child(4) td:first-child background: linear-gradient(top, red 15, green 80%, white, 90% yellow, blue) td:nth-child(2) - background: linear-gradient-image(50px, red 15, green 80%, white, 90% yellow, blue) + background: linear-gradient(50px, red 15, green 80%, white, 90% yellow, blue, formats: png) td:last-child - background: linear-gradient-svg(50px, red 15, green 80%, white, 90% yellow, blue) + background: linear-gradient(50px, red 15, green 80%, white, 90% yellow, blue, formats: svg) tr:nth-child(5) td:first-child background: linear-gradient(bottom, #fff, #000) td:nth-child(2) - background: linear-gradient-image(50px bottom, #fff, #000) + background: linear-gradient(50px bottom, #fff, #000, formats: png) td:last-child - background: linear-gradient-svg(50px bottom, #fff, #000) + background: linear-gradient(50px bottom, #fff, #000, formats: svg) tr:nth-child(6) td:first-child background: linear-gradient(left, #fff, #000) td:nth-child(2) - background: linear-gradient-image(150px left, #fff, #000) + background: linear-gradient(150px left, #fff, #000, formats: png) td:last-child - background: linear-gradient-svg(150px left, #fff, #000) + background: linear-gradient(150px left, #fff, #000, formats: svg) tr:nth-child(7) td:first-child background: linear-gradient(right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) td:nth-child(2) - background: linear-gradient-image(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) + background: linear-gradient(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4, formats: png) td:last-child - background: linear-gradient-svg(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) + background: linear-gradient(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4, formats: svg) tr:nth-child(8) td:first-child background: linear-gradient(top, red, 50% green, blue) td:nth-child(2) - background: linear-gradient-image(50px top, red, 50% green, blue) + background: linear-gradient(50px top, red, 50% green, blue, formats: png) td:last-child - background: linear-gradient-svg(50px top, red, 50% green, blue) + background: linear-gradient(50px top, red, 50% green, blue, formats: svg) tr:nth-child(9) td:first-child - background: linear-gradient(50px top, red, green, yellow, blue) + background: linear-gradient(top, red, green, yellow, blue) td:nth-child(2) - background: linear-gradient-image(50px top, red, green, yellow, blue) + background: linear-gradient(50px top, red, green, yellow, blue, formats: png) td:last-child - background: linear-gradient-svg(50px top, red, green, yellow, blue) \ No newline at end of file + background: linear-gradient(50px top, red, green, yellow, blue, formats: svg) \ No newline at end of file From 5b56afbce1e814ea723d497a2dead31a800a307f Mon Sep 17 00:00:00 2001 From: Michael Hemesath Date: Fri, 5 Aug 2011 09:06:41 -0500 Subject: [PATCH 3/4] added gradient template --- lib/templates/linearGradient.jade | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lib/templates/linearGradient.jade diff --git a/lib/templates/linearGradient.jade b/lib/templates/linearGradient.jade new file mode 100644 index 00000000..9abf964a --- /dev/null +++ b/lib/templates/linearGradient.jade @@ -0,0 +1,7 @@ +svg(xmlns="http://www.w3.org/2000/svg", viewBox="0 0 1 1", preserveAspectRatio="none", version="1.1") + defs + linearGradient(id="g", x1=x1, y1=y1, x2=x2, y2=y2) + - colorStops.forEach(function(colorStop) { + stop(offset=colorStop.pos, stop-color=colorStop.color) + - }) + rect(fill="url(#g)", width="1", height="1") \ No newline at end of file From 6b3f403b2ee41147352e2c3a38cb7e2a693e847b Mon Sep 17 00:00:00 2001 From: Michael Hemesath Date: Thu, 11 Aug 2011 20:42:58 -0500 Subject: [PATCH 4/4] SVG gradients support size. Updated gradient tests to only output CSS format --- lib/nib.js | 1 + lib/nib/gradients.styl | 30 +++++++++++---------- lib/nodes/gradient.js | 39 ++++++++++++++++++++------- lib/templates/linearGradient.jade | 2 +- test/cases/linear-gradient.styl | 10 +++---- test/gradients.styl | 45 ++++++++++++++++++++++++------- test/index.jade | 2 ++ 7 files changed, 90 insertions(+), 39 deletions(-) diff --git a/lib/nib.js b/lib/nib.js index 92b2c3ca..f61bb107 100644 --- a/lib/nib.js +++ b/lib/nib.js @@ -48,6 +48,7 @@ function plugin() { style.define('create-gradient-image', gradient.create) style.define('gradient-svg-data-uri', gradient.svgDataURL) style.define('add-color-stop', gradient.addColorStop) + style.define('set-gradient-size', gradient.setSize) if (Canvas) { style.define('gradient-data-uri', gradient.dataURL) diff --git a/lib/nib/gradients.styl b/lib/nib/gradients.styl index 60ee705a..0fa89001 100644 --- a/lib/nib/gradients.styl +++ b/lib/nib/gradients.styl @@ -82,9 +82,13 @@ join-stops(stops, translate) build-gradient(start, stops) stops = stops[0] if length(stops) == 1 - size = start[0] - start = start[1] or 'top' - grad = create-gradient-image(size, start) + if start[0] is a 'unit' + size = start[0] + start = start[1] or 'top' + else + start = start[0] + grad = create-gradient-image(start) + set-gradient-size(grad, size) if size is a 'unit' stops = normalize-stops(stops) add-color-stop(grad, stop[0], stop[1]) for stop in stops grad @@ -122,20 +126,18 @@ linear-gradient(start, stops..., formats=gradient-formats) val = current-property[1] stops = normalize-stops(stops) - - // gradient image - if start[0] is a 'unit' - if png in formats - img = linear-gradient-image(start, stops) - add-property(prop, replace(val, '__CALL__', img)) - - if svg in formats - img = linear-gradient-svg(start, stops) - add-property(prop, replace(val, '__CALL__', img)) - start = start[1] + // gradient png image + if png in formats and start[0] is a 'unit' + img = linear-gradient-image(start, stops) + add-property(prop, replace(val, '__CALL__', img)) + // gradient svg image + if svg in formats + img = linear-gradient-svg(start, stops) + add-property(prop, replace(val, '__CALL__', img)) if css in formats + start = start[1] if start[0] is a 'unit' // legacy webkit end = grad-point(opposite-position(start)) webkit-legacy = '-webkit-gradient(linear, %s, %s, %s)' % (grad-point(start) end join-stops(stops, webkit-stop)) diff --git a/lib/nodes/gradient.js b/lib/nodes/gradient.js index cd8fbe58..810a97eb 100644 --- a/lib/nodes/gradient.js +++ b/lib/nodes/gradient.js @@ -27,16 +27,28 @@ exports = module.exports = Gradient; * Create a new `Gradient` node with the given `size` * and `start` position. * - * @param {Number} size * @param {String|Ident|Literal} start * @return {Gradient} * @api public */ -exports.create = function(size, start){ - utils.assertType(size, 'unit', 'size'); +exports.create = function(start){ utils.assertString(start, 'start'); - return new Gradient(size.val, start.string); + return new Gradient(start.string); +}; + +/** + * Set gradient size + * + * @param {Gradient} grad + * @param {Number} size + * @return {Null} + * @api public + */ +exports.setSize = function(grad, size){ + utils.assertType(size, 'unit', 'size'); + grad.size = size.val; + return nodes.null; }; /** @@ -79,13 +91,11 @@ exports.svgDataURL = function(grad){ * Initialize a new `Gradient` node with the given `size` * and `start` position. * - * @param {Number} size * @param {String} start * @api private */ -function Gradient(size, start) { - this.size = size; +function Gradient(start) { this.start = start; this.colorStops = []; }; @@ -98,7 +108,8 @@ function Gradient(size, start) { */ Gradient.prototype.toString = function(){ - return 'Gradient(' + this.size + 'px ' + var size = this.size ? ' ' + this.size + 'px ' : ''; + return 'Gradient(' + size + this.stops.map(function(stop){ return stop[0] + ' ' + stop[1]; }).join(', ') + ')'; @@ -141,6 +152,8 @@ Gradient.prototype.toPNGDataURL = function() { , ctx , grad; + if (!this.size) throw new Error('Size required for PNG data URL'); + switch (this.start) { case 'top': canvas.height = this.size; @@ -189,26 +202,32 @@ Gradient.prototype.toPNGDataURL = function() { Gradient.prototype.toSVGDataURL = function() { var x1 = y1 = x2 = y2 = "0%" - , svg; + , svg + , height = '100%' + , width = '100%'; switch (this.start) { case 'top': y2 = '100%'; + height = this.size || height; break; case 'bottom': y1 = '100%'; + height = this.size || height; break; case 'left': x2 = '100%'; + width = this.size || width; break; case 'right': x1 = '100%'; + width = this.size || width; break; default: throw new Error('invalid start position "' + start + '"'); } - svg = renderSVGLinearGradient.call(this, { colorStops: this.colorStops, x1: x1, x2: x2, y1: y1, y2: y2 }) + svg = renderSVGLinearGradient.call(this, { height: height, width: width, colorStops: this.colorStops, x1: x1, x2: x2, y1: y1, y2: y2 }) return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64'); } diff --git a/lib/templates/linearGradient.jade b/lib/templates/linearGradient.jade index 9abf964a..03090d98 100644 --- a/lib/templates/linearGradient.jade +++ b/lib/templates/linearGradient.jade @@ -1,4 +1,4 @@ -svg(xmlns="http://www.w3.org/2000/svg", viewBox="0 0 1 1", preserveAspectRatio="none", version="1.1") +svg(xmlns="http://www.w3.org/2000/svg", height=height, width=width, viewBox="0 0 1 1", preserveAspectRatio="none", version="1.1") defs linearGradient(id="g", x1=x1, y1=y1, x2=x2, y2=y2) - colorStops.forEach(function(colorStop) { diff --git a/test/cases/linear-gradient.styl b/test/cases/linear-gradient.styl index 21d636a3..5f20dbf8 100644 --- a/test/cases/linear-gradient.styl +++ b/test/cases/linear-gradient.styl @@ -2,18 +2,18 @@ @import 'nib/gradients' body - background: linear-gradient(top, white, black) + background: linear-gradient(top, white, black, formats: css) body - background: linear-gradient(top left, white, red, blue, black) + background: linear-gradient(top left, white, red, blue, black, formats: css) body - background: linear-gradient(bottom right, white, black 80%) + background: linear-gradient(bottom right, white, black 80%, formats: css) body - background: linear-gradient(right bottom, white, 80% black) + background: linear-gradient(right bottom, white, 80% black, formats: css) vendor-prefixes = webkit moz ms o official body - background: linear-gradient(top, white, black) \ No newline at end of file + background: linear-gradient(top, white, black, formats: css) \ No newline at end of file diff --git a/test/gradients.styl b/test/gradients.styl index fce77f93..00dc47bb 100644 --- a/test/gradients.styl +++ b/test/gradients.styl @@ -16,61 +16,88 @@ background: linear-gradient(top, yellow, blue) td:nth-child(2) background: linear-gradient(50px, yellow, blue, formats: png) + td:nth-child(3) + background: linear-gradient(top, yellow, blue, formats: svg) td:last-child - background: linear-gradient(50px, yellow, blue, formats: svg) + background: linear-gradient(25px, yellow, blue, formats: svg) + background-repeat: no-repeat tr:nth-child(2) td:first-child background: linear-gradient(top, red, green, yellow, blue) td:nth-child(2) background: linear-gradient(50px, red, green, yellow, blue, formats: png) + td:nth-child(3) + background: linear-gradient(top, red, green, yellow, blue, formats: svg) td:last-child - background: linear-gradient(50px, red, green, yellow, blue, formats: svg) + background: linear-gradient(25px, red, green, yellow, blue, formats: svg) + background-repeat: no-repeat tr:nth-child(3) td:first-child background: linear-gradient(top, red, green 10%, 90% yellow, blue) td:nth-child(2) background: linear-gradient(50px, red, green 10%, 90% yellow, blue, formats: png) + td:nth-child(3) + background: linear-gradient(top, red, green 10%, 90% yellow, blue, formats: svg) td:last-child - background: linear-gradient(50px, red, green 10%, 90% yellow, blue, formats: svg) + background: linear-gradient(25px, red, green 10%, 90% yellow, blue, formats: svg) + background-repeat: no-repeat tr:nth-child(4) td:first-child background: linear-gradient(top, red 15, green 80%, white, 90% yellow, blue) td:nth-child(2) background: linear-gradient(50px, red 15, green 80%, white, 90% yellow, blue, formats: png) + td:nth-child(3) + background: linear-gradient(top, red 15, green 80%, white, 90% yellow, blue, formats: svg) td:last-child - background: linear-gradient(50px, red 15, green 80%, white, 90% yellow, blue, formats: svg) + background: linear-gradient(25px top, red 15, green 80%, white, 90% yellow, blue, formats: svg) + background-repeat: no-repeat tr:nth-child(5) td:first-child background: linear-gradient(bottom, #fff, #000) td:nth-child(2) background: linear-gradient(50px bottom, #fff, #000, formats: png) + td:nth-child(3) + background: linear-gradient(bottom, #fff, #000, formats: svg) td:last-child - background: linear-gradient(50px bottom, #fff, #000, formats: svg) + background: linear-gradient(25px bottom, #fff, #000, formats: svg) + background-repeat: no-repeat tr:nth-child(6) td:first-child background: linear-gradient(left, #fff, #000) td:nth-child(2) background: linear-gradient(150px left, #fff, #000, formats: png) + td:nth-child(3) + background: linear-gradient(left, #fff, #000, formats: svg) td:last-child - background: linear-gradient(150px left, #fff, #000, formats: svg) + background: linear-gradient(75px left, #fff, #000, formats: svg) + background-repeat: no-repeat tr:nth-child(7) td:first-child background: linear-gradient(right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4) td:nth-child(2) background: linear-gradient(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4, formats: png) + td:nth-child(3) + background: linear-gradient(right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4, formats: svg) td:last-child - background: linear-gradient(150px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4, formats: svg) + background: linear-gradient(75px right, #008AB4, #E9FAFF, 2% #90E4FD, #1FCBFF, 80% #008AB4, formats: svg) + background-repeat: no-repeat tr:nth-child(8) td:first-child background: linear-gradient(top, red, 50% green, blue) td:nth-child(2) background: linear-gradient(50px top, red, 50% green, blue, formats: png) + td:nth-child(3) + background: linear-gradient(top, red, 50% green, blue, formats: svg) td:last-child - background: linear-gradient(50px top, red, 50% green, blue, formats: svg) + background: linear-gradient(25px top, red, 50% green, blue, formats: svg) + background-repeat: no-repeat tr:nth-child(9) td:first-child background: linear-gradient(top, red, green, yellow, blue) td:nth-child(2) background: linear-gradient(50px top, red, green, yellow, blue, formats: png) + td:nth-child(3) + background: linear-gradient(top, red, green, yellow, blue, formats: svg) td:last-child - background: linear-gradient(50px top, red, green, yellow, blue, formats: svg) \ No newline at end of file + background: linear-gradient(25px top, red, green, yellow, blue, formats: svg) + background-repeat: no-repeat \ No newline at end of file diff --git a/test/index.jade b/test/index.jade index 7db762f8..a1a7a208 100644 --- a/test/index.jade +++ b/test/index.jade @@ -20,6 +20,7 @@ html th CSS th PNG Data URI th SVG Data URI + th Sized SVG Data URI - var n = 9 tbody - while (n--) @@ -27,6 +28,7 @@ html td td td + td h2 Buttons table#buttons tbody