diff --git a/modes/canvas/base.js b/modes/canvas/base.js index 592f0e8..f50bad2 100644 --- a/modes/canvas/base.js +++ b/modes/canvas/base.js @@ -156,6 +156,16 @@ var Base = Class(Node, { return this.invalidate(); }, + shadow: function(color, blur, x, y) { + var resolution = typeof window !== 'undefined' && window.devicePixelRatio || 1; + + this._shadow = color ? new Color(color).toString() : null; + this._shadowBlur = ((blur != null) ? blur : 3) * resolution; + this._shadowOffsetX = ((x != null) ? x : 0) * resolution; + this._shadowOffsetY = ((y != null) ? y: 0) * resolution; + return this.invalidate(); + }, + // Rendering element_renderTo: Node.prototype.renderTo, diff --git a/modes/canvas/shape.js b/modes/canvas/shape.js index a896523..1b5b9a6 100644 --- a/modes/canvas/shape.js +++ b/modes/canvas/shape.js @@ -46,6 +46,7 @@ module.exports = Class(Base, { var commands = this._commands, fill = this._fill, stroke = this._stroke, + shadow = this._shadow, dash = this._strokeDash; context.beginPath(); @@ -68,6 +69,13 @@ module.exports = Class(Base, { for (var i = 0, l = commands.length; i < l; i++) commands[i](context); + + if (shadow) { + context.shadowColor = shadow; + context.shadowBlur = this._shadowBlur; + context.shadowOffsetX = this._shadowOffsetX; + context.shadowOffsetY = this._shadowOffsetY; + } if (fill){ var m = this._fillTransform; @@ -82,6 +90,7 @@ module.exports = Class(Base, { context.fill(); } } + if (stroke){ context.strokeStyle = stroke; context.lineWidth = this._strokeWidth; @@ -89,6 +98,13 @@ module.exports = Class(Base, { context.lineJoin = this._strokeJoin; context.stroke(); } + + if (shadow) { + context.shadowColor = 0; + context.shadowOffsetX = 0; + context.shadowOffsetY = 0; + context.shadowBlur = 0; + } } }); diff --git a/modes/svg/base.js b/modes/svg/base.js index d445fe7..5274d22 100644 --- a/modes/svg/base.js +++ b/modes/svg/base.js @@ -19,13 +19,27 @@ module.exports = Class(Node, { if (this.parentNode){ this._injectBrush('fill'); this._injectBrush('stroke'); + this._injectShadow(); } else { this._ejectBrush('fill'); this._ejectBrush('stroke'); + this._ejectShadow(); } return this; }, + _injectShadow: function() { + if (!this.parentNode) return; + if (this.shadowFilter) this.parentNode.defs.appendChild(this.shadowFilter); + }, + + _ejectShadow: function() { + var shadowFilter = this.shadowFilter; + if (shadowFilter && shadowFilter.parentNode) { + shadowFilter.parentNode.removeChild(shadowFilter); + } + }, + _injectBrush: function(type){ if (!this.parentNode) return; var brush = type == 'fill' ? this.fillBrush : this.strokeBrush; @@ -225,6 +239,45 @@ module.exports = Class(Node, { element.setAttribute('stroke-dasharray', dash.join(',')); } this._setColor('stroke', color); + return this; + }, + + shadow: function(color, blur, x, y) { + var element = this.element; + + this._ejectShadow(); + + var filter = createElement('filter'); + filter.setAttribute('id', 'shadowfilter' + this.uid); + filter.setAttribute('x', '-20%'); + filter.setAttribute('y', '-20%'); + filter.setAttribute('width', '200%'); + filter.setAttribute('height', '200%'); + + var offset = createElement('feOffset'); + offset.setAttribute('result', 'offOut'); + offset.setAttribute('in', 'SourceAlpha'); + offset.setAttribute('dx', x / 2); + offset.setAttribute('dy', y / 2); + + var gaussianBlur = createElement('feGaussianBlur'); + gaussianBlur.setAttribute('result', 'blurOut'); + gaussianBlur.setAttribute('in', 'offOut'); + gaussianBlur.setAttribute('stdDeviation', blur / 2); + + var blend = createElement('feBlend'); + blend.setAttribute('in', 'SourceGraphic'); + blend.setAttribute('in2', 'blurOut'); + blend.setAttribute('mode', 'normal'); + filter.appendChild(offset); + filter.appendChild(gaussianBlur); + filter.appendChild(blend); + + this.shadowFilter = filter; + this._injectShadow(); + + element.setAttribute('filter', 'url(#shadowfilter' + this.uid + ')'); + return this; }