Permalink
Browse files

Major refactoring: VML is now constructed as a single string of marku…

…p that gets inserted all at once rather than via many DOM insertions. A new VmlShape abstraction handles switching to incremental DOM updates after the shapes have initially been created. This improves initial rendering performance, and also allows use of the o:opacity2 attribute which can only be set via markup. Updated box-shadow rendering to use o:opacity2: it now supports rgba with blur and no longer has to create quadrant shapes for large blurs. Also unified updateProps and updateSize since they are almost always the same; updateProps no longer forces a destroy so we can reuse shapes more intelligently.
  • Loading branch information...
1 parent f07d54b commit 9fcfa80164579f64e5dfc19672c047a1a9f86756 Jason Johnston committed Nov 6, 2011
View
@@ -41,6 +41,7 @@
<fileset file="${src_dir}/BorderImageStyleInfo.js" />
<fileset file="${src_dir}/BoxShadowStyleInfo.js" />
<fileset file="${src_dir}/VisibilityStyleInfo.js" />
+ <fileset file="${src_dir}/VmlShape.js" />
<fileset file="${src_dir}/RendererBase.js" />
<fileset file="${src_dir}/RendererBase_IE678.js" />
<fileset file="${src_dir}/RootRenderer.js" />
@@ -7,8 +7,7 @@
*/
PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
- boxZIndex: 2,
- boxName: 'background',
+ shapeZIndex: 2,
needsUpdate: function() {
var si = this.styleInfos;
@@ -47,21 +46,17 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
if( color && color.alpha() > 0 ) {
this.hideBackground();
- shape = this.getShape( 'bgColor', 'fill', this.getBox(), 1 );
- w = bounds.w;
- h = bounds.h;
- shape.stroked = false;
- shape.coordsize = w * 2 + ',' + h * 2;
- shape.coordorigin = '1,1';
- shape.path = this.getBoxPath( null, 2 );
- s = shape.style;
- s.width = w;
- s.height = h;
- shape.fill.color = color.colorValue( el );
+ shape = this.getShape( 'bgColor', this.shapeZIndex );
+ shape.setSize( bounds.w, bounds.h );
+ shape.setAttrs(
+ 'stroked', false,
+ 'path', this.getBoxPath( 0, 2 )
+ );
+ shape.setFillAttrs( 'color', color.colorValue( el ) );
alpha = color.alpha();
if( alpha < 1 ) {
- shape.fill.opacity = alpha;
+ shape.setFillAttrs( 'opacity', alpha );
}
} else {
this.deleteShape( 'bgColor' );
@@ -86,24 +81,23 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
i = images.length;
while( i-- ) {
img = images[i];
- shape = this.getShape( 'bgImage' + i, 'fill', this.getBox(), 2 );
-
- shape.stroked = false;
- shape.fill.type = 'tile';
- shape.fillcolor = 'none';
- shape.coordsize = w * 2 + ',' + h * 2;
- shape.coordorigin = '1,1';
- shape.path = this.getBoxPath( 0, 2 );
- s = shape.style;
- s.width = w;
- s.height = h;
+ shape = this.getShape( 'bgImage' + i, this.shapeZIndex + ( .5 - i / 1000 ) );
+
+ shape.setAttrs(
+ 'stroked', false,
+ 'path', this.getBoxPath( 0, 2 )
+ );
+ shape.setSize( w, h );
if( img.imgType === 'linear-gradient' ) {
this.addLinearGradient( shape, img );
}
else {
- shape.fill.src = img.imgUrl;
- this.positionBgImage( shape, i );
+ shape.setFillAttrs(
+ 'type', 'tile',
+ 'color', 'none'
+ );
+ this.positionBgImage( shape, img.imgUrl, i );
}
}
}
@@ -117,21 +111,21 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
/**
* Set the position and clipping of the background image for a layer
* @param {Element} shape
+ * @param {String} src
* @param {number} index
*/
- positionBgImage: function( shape, index ) {
- var me = this;
- PIE.Util.withImageSize( shape.fill.src, function( imgSize ) {
- var el = me.targetElement,
+ positionBgImage: function( shape, src, index ) {
+ PIE.Util.withImageSize( src, function( imgSize ) {
+ var me = this,
+ el = me.targetElement,
bounds = me.boundsInfo.getBounds(),
elW = bounds.w,
elH = bounds.h;
// It's possible that the element dimensions are zero now but weren't when the original
// update executed, make sure that's not the case to avoid divide-by-zero error
if( elW && elH ) {
- var fill = shape.fill,
- bgInfo = me.styleInfos.backgroundInfo,
+ var bgInfo = me.styleInfos.backgroundInfo,
bg = bgInfo.getProps().bgImages[ index ],
bgAreaSize = bgInfo.getBgAreaSize( bg.bgOrigin, me.boundsInfo, me.styleInfos.borderInfo ),
adjustedImgSize = ( bg.bgSize || PIE.BgSize.DEFAULT ).pixels(
@@ -150,12 +144,16 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
// needed to fix antialiasing but makes the bg image fuzzy.
pxX = Math.round( bgOriginXY.x + bgPos.x ) + 0.5;
pxY = Math.round( bgOriginXY.y + bgPos.y ) + 0.5;
- fill.position = ( pxX / elW ) + ',' + ( pxY / elH );
+ shape.setFillAttrs(
+ 'src', src,
+ 'position', ( pxX / elW ) + ',' + ( pxY / elH ),
- // Set the size of the image. We have to actually set it to px values otherwise it will not honor
- // the user's browser zoom level and always display at its natural screen size.
- fill['size']['x'] = 1; //Can be any value, just has to be set to "prime" it so the next line works. Weird!
- fill['size'] = adjustedImgSize.w + 'px,' + adjustedImgSize.h + 'px';
+ // Set the size of the image. We only set it if the image is scaled via background-size or by
+ // the user changing the browser zoom level, to avoid fuzzy images at normal size. For some reason
+ // using px units doesn't work in VML markup so we must convert to pt.
+ 'size', ( adjustedImgSize.w !== imgSize.w || adjustedImgSize.h !== imgSize.h || screen['logicalXDPI'] / screen['deviceXDPI'] !== 1 ) ?
+ PIE.Length.pxToPt( adjustedImgSize.w ) + 'pt,' + PIE.Length.pxToPt( adjustedImgSize.h ) + 'pt' : ''
+ );
// Repeating - clip the image shape
if( repeat && repeat !== 'repeat' ) {
@@ -167,10 +165,10 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
clipL = pxX + 1;
clipR = pxX + adjustedImgSize.w + clipAdjust;
}
- shape.style.clip = 'rect(' + clipT + 'px,' + clipR + 'px,' + clipB + 'px,' + clipL + 'px)';
+ shape.setStyles( 'clip', 'rect(' + clipT + 'px,' + clipR + 'px,' + clipB + 'px,' + clipL + 'px)' );
}
}
- } );
+ }, this );
},
@@ -215,7 +213,6 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
bounds = this.boundsInfo.getBounds(),
w = bounds.w,
h = bounds.h,
- fill = shape.fill,
stops = info.stops,
stopCount = stops.length,
PI = Math.PI,
@@ -289,16 +286,14 @@ PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
// Now, finally, we're ready to render the gradient fill. Set the start and end colors to
// the first and last stop colors; this just sets outer bounds for the gradient.
- fill['angle'] = vmlAngle;
- fill['type'] = 'gradient';
- fill['method'] = 'sigma';
- fill['color'] = stops[0].color.colorValue( el );
- fill['color2'] = stops[stopCount - 1].color.colorValue( el );
- if( fill['colors'] ) { //sometimes the colors object isn't initialized so we have to assign it directly (?)
- fill['colors'].value = vmlColors.join( ',' );
- } else {
- fill['colors'] = vmlColors.join( ',' );
- }
+ shape.setFillAttrs(
+ 'angle', vmlAngle,
+ 'type', 'gradient',
+ 'method', 'sigma',
+ 'color', stops[0].color.colorValue( el ),
+ 'color2', stops[stopCount - 1].color.colorValue( el ),
+ 'colors', vmlColors.join( ',' )
+ );
},
@@ -7,8 +7,7 @@
*/
PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
- boxZIndex: 5,
- pieceNames: [ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl', 'c' ],
+ shapeZIndex: 5,
needsUpdate: function() {
return this.styleInfos.borderImageInfo.changed();
@@ -19,13 +18,11 @@ PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
},
draw: function() {
- this.getBox(); //make sure pieces are created
-
- var props = this.styleInfos.borderImageInfo.getProps(),
- borderProps = this.styleInfos.borderInfo.getProps(),
- bounds = this.boundsInfo.getBounds(),
- el = this.targetElement,
- pieces = this.pieces;
+ var me = this,
+ props = me.styleInfos.borderImageInfo.getProps(),
+ borderProps = me.styleInfos.borderInfo.getProps(),
+ bounds = me.boundsInfo.getBounds(),
+ el = me.targetElement;
PIE.Util.withImageSize( props.src, function( imgSize ) {
var elW = bounds.w,
@@ -44,12 +41,13 @@ PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
// Piece positions and sizes
function setSizeAndPos( piece, w, h, x, y ) {
- var s = pieces[piece].style,
- max = Math.max;
- s.width = max(w, 0);
- s.height = max(h, 0);
- s.left = x;
- s.top = y;
+ var max = Math.max;
+ me.getRect( piece ).setStyles(
+ 'width', max( w, 0 ) + 'px',
+ 'height', max( h, 0 ) + 'px',
+ 'left', x + 'px',
+ 'top', y + 'px'
+ );
}
setSizeAndPos( 'tl', widthL, widthT, 0, 0 );
setSizeAndPos( 't', elW - widthL - widthR, widthT, widthL, 0 );
@@ -64,8 +62,13 @@ PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
// image croppings
function setCrops( sides, crop, val ) {
- for( var i=0, len=sides.length; i < len; i++ ) {
- pieces[ sides[i] ]['imagedata'][ crop ] = val;
+ var src = props.src,
+ i = 0, len = sides.length;
+ for( ; i < len; i++ ) {
+ me.getRect( sides[i] ).setImageDataAttrs(
+ 'src', src,
+ crop, val
+ );
}
}
@@ -87,40 +90,20 @@ PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
//}
// center fill
- pieces['c'].style.display = props.fill ? '' : 'none';
- }, this );
+ me.getRect( 'c' ).setStyles(
+ 'display', props.fill ? '' : 'none'
+ );
+ }, me );
},
- getBox: function() {
- var box = this.parent.getLayer( this.boxZIndex ),
- s, piece, i,
- pieceNames = this.pieceNames,
- len = pieceNames.length;
-
- if( !box ) {
- box = doc.createElement( 'border-image' );
- s = box.style;
- s.position = 'absolute';
-
- this.pieces = {};
-
- for( i = 0; i < len; i++ ) {
- piece = this.pieces[ pieceNames[i] ] = this.createVmlElement( 'rect' );
- piece.appendChild( this.createVmlElement( 'imagedata' ) );
- s = piece.style;
- s['behavior'] = 'url(#default#VML)';
- s.position = "absolute";
- s.top = s.left = 0;
- piece['imagedata'].src = this.styleInfos.borderImageInfo.getProps().src;
- piece.stroked = false;
- piece.filled = false;
- box.appendChild( piece );
- }
-
- this.parent.addLayer( this.boxZIndex, box );
- }
-
- return box;
+ getRect: function( name ) {
+ var shape = this.getShape( 'borderImage' + name, this.shapeZIndex );
+ shape.tagName = 'rect';
+ shape.setAttrs(
+ 'stroked', false,
+ 'filled', false
+ );
+ return shape;
},
prepareUpdate: function() {
@@ -153,7 +136,7 @@ PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
if (me.finalized || !me.styleInfos.borderInfo.isActive()) {
rs.borderColor = rs.borderWidth = '';
}
- PIE.RendererBase.destroy.call( this );
+ PIE.RendererBase.destroy.call( me );
}
} );
View
@@ -7,8 +7,7 @@
*/
PIE.BorderRenderer = PIE.RendererBase.newRenderer( {
- boxZIndex: 4,
- boxName: 'border',
+ shapeZIndex: 4,
needsUpdate: function() {
var si = this.styleInfos;
@@ -27,43 +26,46 @@ PIE.BorderRenderer = PIE.RendererBase.newRenderer( {
* Draw the border shape(s)
*/
draw: function() {
- var el = this.targetElement,
- props = this.styleInfos.borderInfo.getProps(),
- bounds = this.boundsInfo.getBounds(),
+ var me = this,
+ el = me.targetElement,
+ props = me.styleInfos.borderInfo.getProps(),
+ bounds = me.boundsInfo.getBounds(),
w = bounds.w,
h = bounds.h,
- shape, stroke, s,
+ shape,
segments, seg, i, len;
if( props ) {
- this.hideBorder();
+ me.hideBorder();
- segments = this.getBorderSegments( 2 );
+ segments = me.getBorderSegments( 2 );
for( i = 0, len = segments.length; i < len; i++) {
seg = segments[i];
- shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill', this.getBox() );
- shape.coordsize = w * 2 + ',' + h * 2;
- shape.coordorigin = '1,1';
- shape.path = seg.path;
- s = shape.style;
- s.width = w;
- s.height = h;
-
- shape.filled = !!seg.fill;
- shape.stroked = !!seg.stroke;
+ shape = me.getShape( 'borderPiece' + i, me.shapeZIndex );
+ shape.setSize( w, h );
+
+ shape.setAttrs(
+ 'path', seg.path,
+ 'filled', !!seg.fill,
+ 'stroked', !!seg.stroke
+ );
+
if( seg.stroke ) {
- stroke = shape.stroke;
- stroke['weight'] = seg.weight + 'px';
- stroke.color = seg.color.colorValue( el );
- stroke['dashstyle'] = seg.stroke === 'dashed' ? '2 2' : seg.stroke === 'dotted' ? '1 1' : 'solid';
- stroke['linestyle'] = seg.stroke === 'double' && seg.weight > 2 ? 'ThinThin' : 'Single';
+ shape.setStrokeAttrs(
+ 'weight', seg.weight + 'px',
+ 'color', seg.color.colorValue( el ),
+ 'dashstyle', seg.stroke === 'dashed' ? '2 2' : seg.stroke === 'dotted' ? '1 1' : 'solid',
+ 'linestyle', seg.stroke === 'double' && seg.weight > 2 ? 'ThinThin' : 'Single'
+ );
} else {
- shape.fill.color = seg.fill.colorValue( el );
+ shape.setFillAttrs(
+ 'color', seg.fill.colorValue( el )
+ );
}
}
// remove any previously-created border shapes which didn't get used above
- while( this.deleteShape( 'borderPiece' + i++ ) ) {}
+ while( me.deleteShape( 'borderPiece' + i++ ) ) {}
}
},
Oops, something went wrong.

0 comments on commit 9fcfa80

Please sign in to comment.