From 353e94c7e30a9b185d4595ca34672fc0b4b78c63 Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Sun, 23 May 2010 07:48:32 -0600 Subject: [PATCH] Implement rendering of multiple box-shadows. Closes #4. --- documentation/properties.html | 3 - sources/BackgroundAndBorderRenderer.js | 6 +- sources/BoxShadowRenderer.js | 151 +++++++++++-------------- sources/RendererBase.js | 7 +- 4 files changed, 72 insertions(+), 95 deletions(-) diff --git a/documentation/properties.html b/documentation/properties.html index 52fcabc..1c2eae1 100644 --- a/documentation/properties.html +++ b/documentation/properties.html @@ -81,9 +81,6 @@

box-shadow

rounding, particularly when the blur is very small (this should not be more than a one-pixel difference).

-

PIE does not currently support multiple box shadows, but support is -planned in a future version (see issue #4.

-

PIE does not currently support the 'inset' keyword, but support is planned in a future version (see issue #3.

diff --git a/sources/BackgroundAndBorderRenderer.js b/sources/BackgroundAndBorderRenderer.js index a2547f3..749c72e 100644 --- a/sources/BackgroundAndBorderRenderer.js +++ b/sources/BackgroundAndBorderRenderer.js @@ -62,7 +62,7 @@ PIE.BackgroundAndBorderRenderer = (function() { if( color && color !== 'transparent' ) { this.hideBackground(); - shape = this.getShape( 'bgColor', 'fill', 1 ); + shape = this.getShape( 'bgColor', 'fill', this.getBox(), 1 ); w = el.offsetWidth; h = el.offsetHeight; shape.stroked = false; @@ -101,7 +101,7 @@ PIE.BackgroundAndBorderRenderer = (function() { i = images.length; while( i-- ) { img = images[i]; - shape = this.getShape( 'bgImage' + i, 'fill', 2 ); + shape = this.getShape( 'bgImage' + i, 'fill', this.getBox(), 2 ); shape.stroked = false; shape.fill.type = 'tile'; @@ -385,7 +385,7 @@ PIE.BackgroundAndBorderRenderer = (function() { segments = this.getBorderSegments( 2 ); for( i = 0, len = segments.length; i < len; i++) { seg = segments[i]; - shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill', 3 ); + shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill', this.getBox(), 3 ); shape.coordsize = w * 2 + ',' + h * 2; shape.coordorigin = '1,1'; shape.path = seg.path; diff --git a/sources/BoxShadowRenderer.js b/sources/BoxShadowRenderer.js index 8238782..5f3a244 100644 --- a/sources/BoxShadowRenderer.js +++ b/sources/BoxShadowRenderer.js @@ -27,37 +27,65 @@ PIE.BoxShadowRenderer = (function() { updateSize: function() { if( this.isActive() ) { - var box = this.getBox(), - shape = box.firstChild, - s, - el = this.element, - bs = this.styleInfos.boxShadowInfo.getProps()[0], - spread = bs.spread.pixels( el ), - shrink = bs.blur.pixels( el ) > 0 ? 4 : 0, + var el = this.element, + shadowInfos = this.styleInfos.boxShadowInfo.getProps(), + i = shadowInfos.length, w = el.offsetWidth, - h = el.offsetHeight; - - /*if( bs.inset ) { - // if inset, the width does not include any element border - s = el.currentStyle; - w -= ( parseInt( s.borderLeftWidth, 10 ) || 0 ) + ( parseInt( s.borderRightWidth, 10 ) || 0 ); - h -= ( parseInt( s.borderTopWidth, 10 ) || 0 ) + ( parseInt( s.borderBottomWidth, 10 ) || 0 ); - - // update width of inner element - s = box.firstChild.style; - s.width = w - spread * 2; - s.height = h - spread * 2; - } else { - }*/ - - s = shape.style; - s.width = w; - s.height = h; - - // Blurred shadows end up slightly too wide; shrink them down - shape.coordsize = ( w * 2 + shrink ) + ',' + ( h * 2 + shrink ); - shape.coordorigin = '1,1'; - shape.path = this.getBoxPath( spread ? { t: -spread, r: -spread, b: -spread, l: -spread } : 0, 2 ); + h = el.offsetHeight, + shadowInfo, box, shape, ss, xOff, yOff, spread, blur, shrink, halfBlur, filter, alpha; + + while( i-- ) { + shadowInfo = shadowInfos[ i ]; + + box = this.getBox( shadowInfo.inset ); + shape = this.getShape( 'shadow' + i, 'fill', box ); + ss = shape.style; + + xOff = shadowInfo.xOffset.pixels( el ); + yOff = shadowInfo.yOffset.pixels( el ); + spread = shadowInfo.spread.pixels( el ), + blur = shadowInfo.blur.pixels( el ); + + // Adjust the blur value so it's always an even number + halfBlur = Math.ceil( blur / 2 ); + blur = halfBlur * 2; + + // Apply blur filter to the shape. Applying the blur filter twice with + // half the pixel value gives a shadow nearly identical to other browsers. + if( blur > 0 ) { + filter = 'progid:DXImageTransform.Microsoft.blur(pixelRadius=' + halfBlur + ')'; + ss.filter = filter + ' ' + filter; + } + + // Position and size + ss.left = xOff - blur; + ss.top = yOff - blur; + ss.width = w; + ss.height = h; + + // Color and opacity + shape.stroked = false; + shape.filled = true; + shape.fillcolor = shadowInfo.color.value( el ); + + alpha = shadowInfo.color.alpha(); + if( alpha < 1 ) { + shape.fill.opacity = alpha; + } + + // Blurred shadows end up slightly too wide; shrink them down + shrink = blur > 0 ? 4 : 0, + shape.coordsize = ( w * 2 + shrink ) + ',' + ( h * 2 + shrink ); + + shape.coordorigin = '1,1'; + shape.path = this.getBoxPath( spread ? { t: -spread, r: -spread, b: -spread, l: -spread } : 0, 2 ); + + box.appendChild( shape ); + } + + // remove any previously-created border shapes which didn't get used above + i = shadowInfos.length; + while( this.deleteShape( 'shadow' + i++ ) ) {} } else { this.destroy(); } @@ -68,62 +96,16 @@ PIE.BoxShadowRenderer = (function() { this.updateSize(); }, - getBox: function() { - var box = this._box, s, ss, cs, bs, xOff, yOff, blur, halfBlur, shape, el, filter, alpha; + getBox: function( isInset ) { + var zIndex = isInset ? this.insetZIndex : this.outsetZIndex, + box = this.parent.getLayer( zIndex ); if( !box ) { - el = this.element; - box = this._box = el.document.createElement( 'box-shadow' ); - bs = this.styleInfos.boxShadowInfo.getProps()[0]; - xOff = bs.xOffset.pixels( el ); - yOff = bs.yOffset.pixels( el ); - blur = bs.blur.pixels( el ); - - // Adjust the blur value so it's always an even number - halfBlur = Math.ceil( blur / 2 ); - blur = halfBlur * 2; - - s = box.style; - s.position = 'absolute'; - - shape = this.getShape( 'shadow', 'fill' ); - ss = shape.style; - shape.stroked = false; - - /*if( bs.inset ) { - cs = this.element.currentStyle; - s.overflow = 'hidden'; - s.left = parseInt( cs.borderLeftWidth, 10 ) || 0; - s.top = parseInt( cs.borderTopWidth, 10 ) || 0; - - s = shape.style; - s.position = 'absolute'; - - //TODO handle wider border if needed due to very large offsets or spread - s.left = xOff - 20 + spread - blur; - s.top = yOff - 20 + spread - blur; - s.border = '20px solid ' + bs.color.value( el ); - } else {*/ - s.left = xOff - blur; - s.top = yOff - blur; - - shape.filled = true; - shape.fillcolor = bs.color.value( el ); - - alpha = bs.color.alpha(); - if( alpha < 1 ) { - shape.fill.opacity = alpha; - } - /*}*/ - - // Apply blur filter to the outer or inner element. Applying the blur filter twice with - // half the pixel value gives a shadow nearly identical to other browsers. - if( blur > 0 ) { - filter = 'progid:DXImageTransform.Microsoft.blur(pixelRadius=' + halfBlur + ')'; - ss.filter = filter + ' ' + filter; - } + box = this.element.document.createElement( ( isInset ? 'inset' : 'outset' ) + '-box-shadow' ); + box.style.position = 'absolute'; + this.parent.addLayer( zIndex, box ); - this.parent.addLayer( bs.inset ? this.insetZIndex : this.outsetZIndex, box ); - if( bs.inset ) { + // Temporarily hide inset shadows, until they are properly implemented + if( isInset ) { box.style.display = 'none'; } } @@ -133,7 +115,6 @@ PIE.BoxShadowRenderer = (function() { destroy: function() { this.parent.removeLayer( this.insetZIndex ); this.parent.removeLayer( this.outsetZIndex ); - delete this._box; delete this._shapes; } diff --git a/sources/RendererBase.js b/sources/RendererBase.js index 87a6b74..96550ff 100644 --- a/sources/RendererBase.js +++ b/sources/RendererBase.js @@ -80,13 +80,14 @@ PIE.RendererBase = { * Get a VML shape by name, creating it if necessary. * @param {string} name A name identifying the element * @param {string=} subElName If specified a subelement of the shape will be created with this tag name + * @param {Element} parent The parent element for the shape; will be ignored if 'group' is specified * @param {number=} group If specified, an ordinal group for the shape. 1 or greater. Groups are rendered * using container elements in the correct order, to get correct z stacking without z-index. */ - getShape: function( name, subElName, group ) { + getShape: function( name, subElName, parent, group ) { var shapes = this._shapes || ( this._shapes = {} ), shape = shapes[ name ], - s, parent; + s; if( !shape ) { shape = shapes[ name ] = PIE.Util.createVmlElement( 'shape' ); @@ -100,8 +101,6 @@ PIE.RendererBase = { this.addLayer( group, this.element.document.createElement( 'group' + group ) ); parent = this.getLayer( group ); } - } else { - parent = this.getBox(); } parent.appendChild( shape );