summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorwout <wout@impinc.co.uk>2013-03-06 19:43:17 +0000
committerwout <wout@impinc.co.uk>2013-03-06 19:43:17 +0000
commit5e3ff072994d3d9b224c62d71ca1331951249bac (patch)
tree9bb8b5af1ca4372fcf0c03b0444530fefee20735 /src
parent6a8a3fee7dbfc9a9f4a36dc5877f955f5743a4ac (diff)
downloadsvg.js-5e3ff072994d3d9b224c62d71ca1331951249bac.tar.gz
svg.js-5e3ff072994d3d9b224c62d71ca1331951249bac.zip
Reworked color management
Diffstat (limited to 'src')
-rw-r--r--src/color.js165
-rw-r--r--src/element.js4
-rw-r--r--src/fx.js46
-rw-r--r--src/regex.js15
-rw-r--r--src/sugar.js2
5 files changed, 191 insertions, 41 deletions
diff --git a/src/color.js b/src/color.js
new file mode 100644
index 0000000..dde2c65
--- /dev/null
+++ b/src/color.js
@@ -0,0 +1,165 @@
+// Module for color convertions
+SVG.Color = function(color) {
+ var match
+
+ /* initialize defaults */
+ this.r = 0
+ this.g = 0
+ this.b = 0
+
+ /* parse color */
+ if (typeof color == 'string') {
+ if (SVG.regex.isRgb.test(color)) {
+ /* get rgb values */
+ match = SVG.regex.rgb.exec(color.replace(/\s/g,''))
+
+ /* parse numeric values */
+ this.r = parseInt(m[1])
+ this.g = parseInt(m[2])
+ this.b = parseInt(m[3])
+
+ } else if (SVG.regex.isHex.test(color)) {
+ /* get hex values */
+ match = SVG.regex.hex.exec(this._fullHex(color))
+
+ /* parse numeric values */
+ this.r = parseInt(match[1], 16)
+ this.g = parseInt(match[2], 16)
+ this.b = parseInt(match[3], 16)
+
+ } else if (SVG.regex.isHsb.test(color)) {
+ /* get hsb values */
+ match = SVG.regex.hsb.exec(color.replace(/\s/g,''))
+
+ /* convert hsb to rgb */
+ color = this._hsbToRgb(match[1], match[2], match[3])
+ }
+
+ } else if (typeof color == 'object') {
+ if (SVG.Color.isHsb(color))
+ color = this._hsbToRgb(color.h, color.s, color.b)
+
+ this.r = color.r
+ this.g = color.g
+ this.b = color.b
+
+ }
+
+}
+
+SVG.extend(SVG.Color, {
+ // Default to hex conversion
+ toString: function() {
+ return this.toHex()
+ }
+ // Build hex value
+, toHex: function() {
+ return '#'
+ + this._compToHex(this.r)
+ + this._compToHex(this.g)
+ + this._compToHex(this.b)
+ }
+ // Build rgb value
+, toRgb: function() {
+ return 'rgb(' + [this.r, this.g, this.b].join() + ')'
+ }
+ // Calculate true brightness
+, brightness: function() {
+ return (this.r / 255 * 0.30)
+ + (this.g / 255 * 0.59)
+ + (this.b / 255 * 0.11)
+ }
+ // Private: convert hsb to rgb
+, _hsbToRgb: function(h, s, v) {
+ var vs, vsf
+
+ /* process hue */
+ h = parseInt(h) % 360
+ if (h < 0) h += 360
+
+ /* process saturation */
+ s = parseInt(s)
+ s = s > 100 ? 100 : s
+
+ /* process brightness */
+ v = parseInt(v)
+ v = (v < 0 ? 0 : v > 100 ? 100 : v) * 255 / 100
+
+ /* compile rgb */
+ vs = v * s / 100
+ vsf = (vs * ((h * 256 / 60) % 256)) / 256
+
+ switch (Math.floor(h / 60)) {
+ case 0:
+ r = v
+ g = v - vs + vsf
+ b = v - vs
+ break
+ case 1:
+ r = v - vsf
+ g = v
+ b = v - vs
+ break
+ case 2:
+ r = v - vs
+ g = v
+ b = v - vs + vsf
+ break
+ case 3:
+ r = v - vs
+ g = v - vsf
+ b = v
+ break
+ case 4:
+ r = v - vs + vsf
+ g = v - vs
+ b = v
+ break
+ case 5:
+ r = v
+ g = v - vs
+ b = v - vsf
+ break
+ }
+
+ /* parse values */
+ return {
+ r: Math.floor(r + 0.5)
+ , g: Math.floor(g + 0.5)
+ , b: Math.floor(b + 0.5)
+ }
+ }
+ // Private: ensure to six-based hex
+, _fullHex: function(hex) {
+ return hex.length == 4 ?
+ [ '#',
+ hex.substring(1, 2), hex.substring(1, 2)
+ , hex.substring(2, 3), hex.substring(2, 3)
+ , hex.substring(3, 4), hex.substring(3, 4)
+ ].join('') : hex
+ }
+ // Private: component to hex value
+, _compToHex: function(comp) {
+ var hex = comp.toString(16)
+ return hex.length == 1 ? '0' + hex : hex
+ }
+
+})
+
+// Test if given value is a color string
+SVG.Color.test = function(color) {
+ color += ''
+ return SVG.regex.isHex.test(color)
+ || SVG.regex.isRgb.test(color)
+ || SVG.regex.isHsb.test(color)
+}
+
+// Test if given value is a rgb object
+SVG.Color.isRgb = function(color) {
+ return typeof color.r == 'number'
+}
+
+// Test if given value is a hsb object
+SVG.Color.isHsb = function(color) {
+ return typeof color.h == 'number'
+} \ No newline at end of file
diff --git a/src/element.js b/src/element.js
index 63f8c31..e11f650 100644
--- a/src/element.js
+++ b/src/element.js
@@ -160,6 +160,10 @@ SVG.extend(SVG.Element, {
if (a == 'stroke-width')
this.attr('stroke', parseFloat(v) > 0 ? this.attrs.stroke : null)
+ /* ensure hex color */
+ if (SVG.Color.test(v) || SVG.Color.isRgb(v) || SVG.Color.isHsb(v))
+ v = new SVG.Color(v).toHex()
+
/* set give attribute on node */
n != null ?
this.node.setAttributeNS(n, a, v) :
diff --git a/src/fx.js b/src/fx.js
index cf80f38..94a6873 100644
--- a/src/fx.js
+++ b/src/fx.js
@@ -14,14 +14,14 @@ SVG.extend(SVG.FX, {
var akeys, tkeys, tvalues
, element = this.target
, fx = this
- , start = (new Date).getTime()
+ , start = new Date().getTime()
, finish = start + duration
/* start animation */
this.interval = setInterval(function(){
// This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby.
var index
- , time = (new Date).getTime()
+ , time = new Date().getTime()
, pos = time > finish ? 1 : (time - start) / duration
/* collect attribute keys */
@@ -180,7 +180,7 @@ SVG.extend(SVG.FX, {
this._unit(o, pos) :
/* color recalculation */
- o.to && (o.to.r || /^#/.test(o.to)) ?
+ o.to && (o.to.r || SVG.Color.test(o.to)) ?
this._color(o, pos) :
/* for all other values wait until pos has reached 1 to return the final value */
@@ -206,48 +206,18 @@ SVG.extend(SVG.FX, {
/* normalise pos */
pos = pos < 0 ? 0 : pos > 1 ? 1 : pos
- /* convert FROM hex to rgb */
- from = this._h2r(o.from || '#000')
+ /* convert FROM */
+ from = new SVG.Color(o.from)
/* convert TO hex to rgb */
- to = this._h2r(o.to)
+ to = new SVG.Color(o.to)
/* tween color and return hex */
- return this._r2h({
+ return new SVG.Color({
r: ~~(from.r + (to.r - from.r) * pos)
, g: ~~(from.g + (to.g - from.g) * pos)
, b: ~~(from.b + (to.b - from.b) * pos)
- })
- }
- // Private: convert hex to rgb object
-, _h2r: function(hex) {
- /* parse full hex */
- var match = SVG.regex.hex.exec(this._fh(hex))
-
- /* if the hex is successfully parsed, return it in rgb, otherwise return black */
- return match ? {
- r: parseInt(match[1], 16)
- , g: parseInt(match[2], 16)
- , b: parseInt(match[3], 16)
- } : { r: 0, g: 0, b: 0 }
- }
- // Private: convert rgb object to hex string
-, _r2h: function(rgb) {
- return '#' + this._c2h(rgb.r) + this._c2h(rgb.g) + this._c2h(rgb.b)
- }
- // Private: convert component to hex
-, _c2h: function(c) {
- var hex = c.toString(16)
- return hex.length == 1 ? '0' + hex : hex
- }
- // Private: force potential 3-based hex to 6-based
-, _fh: function(hex) {
- return hex.length == 4 ?
- [ '#',
- hex.substring(1, 2), hex.substring(1, 2)
- , hex.substring(2, 3), hex.substring(2, 3)
- , hex.substring(3, 4), hex.substring(3, 4)
- ].join('') : hex
+ }).toHex()
}
})
diff --git a/src/regex.js b/src/regex.js
index 7ecb557..e89dc85 100644
--- a/src/regex.js
+++ b/src/regex.js
@@ -1,7 +1,18 @@
// Storage for regular expressions
SVG.regex = {
- unit: /^([\d\.]+)([a-z%]{0,2})$/
+ unit: /^([\d\.]+)([a-z%]{0,2})$/
+
+, hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
+
+, rgb: /rgb\((\d+),(\d+),(\d+),([\d\.]+)\)/
+
+, hsb: /hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/
+
+, isHex: /^#/i
+
+, isRgb: /^rgb\(/
+
+, isHsb: /^hsb\(/
-, hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
} \ No newline at end of file
diff --git a/src/sugar.js b/src/sugar.js
index 42883e5..f8a5660 100644
--- a/src/sugar.js
+++ b/src/sugar.js
@@ -15,7 +15,7 @@ var _colorPrefix = function(type, attr) {
SVG.Shape.prototype[method] = function(o) {
var indexOf
- if (typeof o == 'string')
+ if (typeof o == 'string' || SVG.Color.isRgb(o) || SVG.Color.isHsb(o))
this.attr(method, o)
else