rewrote shadow logic. Shadows can now be applied to images and sprites, even if they have transparent pixels

This commit is contained in:
Eric Rowell
2012-05-26 20:34:36 -07:00
parent af31e1ee83
commit e075a725a1
13 changed files with 326 additions and 220 deletions

View File

@@ -36,6 +36,7 @@ Kinetic.Shape = function(config) {
this.data = [];
this.nodeType = 'Shape';
this.appliedShadow = false;
// call super constructor
Kinetic.Node.apply(this, [config]);
@@ -66,12 +67,19 @@ Kinetic.Shape.prototype = {
return this.tempLayer.getCanvas();
},
/**
* helper method to stroke shape
* helper method to stroke the shape and apply
* shadows if needed
*/
stroke: function() {
var appliedShadow = false;
var context = this.getContext();
context.save();
if(!!this.attrs.stroke || !!this.attrs.strokeWidth) {
if(!this.appliedShadow) {
appliedShadow = this._applyShadow();
}
var stroke = !!this.attrs.stroke ? this.attrs.stroke : 'black';
var strokeWidth = !!this.attrs.strokeWidth ? this.attrs.strokeWidth : 2;
@@ -79,36 +87,28 @@ Kinetic.Shape.prototype = {
context.strokeStyle = stroke;
context.stroke();
}
},
/**
* applies shadow, fill, and stroke
*/
applyStyles: function() {
var context = this.getContext();
/*
* if fill is defined, apply shadow to
* fill only and not the stroke
*/
if(!!this.attrs.fill) {
this.applyShadow(this.fill);
context.restore();
if(appliedShadow) {
this.stroke();
}
/*
* if fill is not defined, apply the shadow
* to the stroke
*/
else {
this.applyShadow(this.stroke);
}
},
/**
* helper method to fill the shape with a color, linear gradient,
* radial gradient, or pattern
*/
* radial gradient, or pattern, and also apply shadows if needed
* */
fill: function() {
var appliedShadow = false;
var context = this.getContext();
context.save();
var fill = this.attrs.fill;
if(!!fill) {
if(!this.appliedShadow) {
appliedShadow = this._applyShadow();
}
var s = fill.start;
var e = fill.end;
var f = null;
@@ -168,6 +168,91 @@ Kinetic.Shape.prototype = {
context.fill();
}
}
context.restore();
if(appliedShadow) {
this.fill();
}
},
/**
* helper method to fill text and appy shadows if needed
*/
fillText: function(text, x, y) {
var appliedShadow = false;
var context = this.getContext();
context.save();
if(this.attrs.textFill !== undefined) {
if(!this.appliedShadow) {
appliedShadow = this._applyShadow();
}
context.fillStyle = this.attrs.textFill;
context.fillText(text, x, y);
}
context.restore();
if(appliedShadow) {
this.fillText(text, x, y);
}
},
/**
* helper method to stroke text and apply shadows
* if needed
*/
strokeText: function(text, x, y) {
var appliedShadow = false;
var context = this.getContext();
context.save();
if(this.attrs.textStroke !== undefined || this.attrs.textStrokeWidth !== undefined) {
if(!this.appliedShadow) {
appliedShadow = this._applyShadow();
}
// defaults
if(this.attrs.textStroke === undefined) {
this.attrs.textStroke = 'black';
}
else if(this.attrs.textStrokeWidth === undefined) {
this.attrs.textStrokeWidth = 2;
}
context.lineWidth = this.attrs.textStrokeWidth;
context.strokeStyle = this.attrs.textStroke;
context.strokeText(text, x, y);
}
context.restore();
if(appliedShadow) {
this.strokeText(text, x, y);
}
},
/**
* helper method to draw an image and apply
* a shadow if neede
*/
drawImage: function() {
var appliedShadow = false;
var context = this.getContext();
context.save();
var a = arguments;
if(a.length === 5 || a.length === 9) {
if(!this.appliedShadow) {
appliedShadow = this._applyShadow();
}
switch(a.length) {
case 5:
context.drawImage(a[0], a[1], a[2], a[3], a[4]);
break;
case 9:
context.drawImage(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
break;
}
}
context.restore();
if(appliedShadow) {
this.drawImage.apply(this, arguments);
}
},
/**
* helper method to set the line join of a shape
@@ -179,45 +264,23 @@ Kinetic.Shape.prototype = {
context.lineJoin = this.attrs.lineJoin;
}
},
/**
* apply shadow based on shadow color, blur,
* and offset properties
* @param {Function} draw function. Will typically be this.fill(), this.stroke(),
* this.fillText(), or this.strokeText()
*/
applyShadow: function(drawFunc) {
_applyShadow: function() {
var context = this.getContext();
var s = this.attrs.shadow;
var aa = this.getAbsoluteAlpha();
var sa = this.attrs.shadow.alpha;
context.save();
if(sa === 1) {
this._applyShadow();
drawFunc.call(this);
}
else {
context.save();
context.globalAlpha = sa * aa;
this._applyShadow();
drawFunc.call(this);
context.restore();
drawFunc.call(this);
}
context.restore();
},
_applyShadow: function() {
var context = this.getContext();
var s = this.attrs.shadow;
if(s.color !== undefined) {
context.globalAlpha = sa * aa;
context.shadowColor = s.color;
context.shadowBlur = s.blur;
context.shadowOffsetX = s.offset.x;
context.shadowOffsetY = s.offset.y;
this.appliedShadow = true;
return true;
}
return false;
},
/**
* set fill which can be a color, linear gradient object,
@@ -379,6 +442,7 @@ Kinetic.Shape.prototype = {
this.applyLineJoin();
// draw the shape
this.appliedShadow = false;
this.attrs.drawFunc.call(this);
context.restore();
}

View File

@@ -20,7 +20,8 @@ Kinetic.Circle = function(config) {
context.beginPath();
context.arc(0, 0, this.attrs.radius, 0, Math.PI * 2, true);
context.closePath();
this.applyStyles();
this.fill();
this.stroke();
};
// call super constructor
Kinetic.Shape.apply(this, [config]);

View File

@@ -32,15 +32,16 @@ Kinetic.Image = function(config) {
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
this.applyStyles();
this.fill();
this.stroke();
// if cropping
if(cropWidth !== undefined && cropHeight !== undefined) {
context.drawImage(this.attrs.image, cropX, cropY, cropWidth, cropHeight, 0, 0, width, height);
this.drawImage(this.attrs.image, cropX, cropY, cropWidth, cropHeight, 0, 0, width, height);
}
// no cropping
else {
context.drawImage(this.attrs.image, 0, 0, width, height);
this.drawImage(this.attrs.image, 0, 0, width, height);
}
}
};

View File

@@ -41,7 +41,8 @@ Kinetic.Line = function(config) {
if(!!this.attrs.lineCap) {
context.lineCap = this.attrs.lineCap;
}
this.applyStyles();
this.fill();
this.stroke();
};
// call super constructor
Kinetic.Shape.apply(this, [config]);

View File

@@ -21,7 +21,8 @@ Kinetic.Polygon = function(config) {
context.lineTo(this.attrs.points[n].x, this.attrs.points[n].y);
}
context.closePath();
this.applyStyles();
this.fill();
this.stroke();
};
// call super constructor
Kinetic.Shape.apply(this, [config]);

View File

@@ -36,7 +36,9 @@ Kinetic.Rect = function(config) {
context.arc(this.attrs.cornerRadius, this.attrs.cornerRadius, this.attrs.cornerRadius, Math.PI, Math.PI * 3 / 2, false);
}
context.closePath();
this.applyStyles();
this.fill();
this.stroke();
};
// call super constructor
Kinetic.Shape.apply(this, [config]);

View File

@@ -25,7 +25,8 @@ Kinetic.RegularPolygon = function(config) {
context.lineTo(x, y);
}
context.closePath();
this.applyStyles();
this.fill();
this.stroke();
};
// call super constructor
Kinetic.Shape.apply(this, [config]);

View File

@@ -23,7 +23,8 @@ Kinetic.Sprite = function(config) {
context.beginPath();
context.rect(0, 0, f.width, f.height);
context.closePath();
context.drawImage(this.attrs.image, f.x, f.y, f.width, f.height, 0, 0, f.width, f.height);
this.drawImage(this.attrs.image, f.x, f.y, f.width, f.height, 0, 0, f.width, f.height);
}
};
// call super constructor

View File

@@ -27,7 +27,9 @@ Kinetic.Star = function(config) {
context.lineTo(x, y);
}
context.closePath();
this.applyStyles();
this.fill();
this.stroke();
};
// call super constructor
Kinetic.Shape.apply(this, [config]);

View File

@@ -30,7 +30,6 @@ Kinetic.Text = function(config) {
var p = this.attrs.padding;
var x = 0;
var y = 0;
var appliedShadow = false;
var that = this;
switch (this.attrs.align) {
@@ -57,10 +56,8 @@ Kinetic.Text = function(config) {
context.rect(x, y, textWidth + p * 2, textHeight + p * 2);
context.closePath();
if(this.attrs.fill !== undefined || this.attrs.stroke !== undefined) {
this.applyStyles(this.fill);
appliedShadow = true;
}
this.fill();
this.stroke();
context.restore();
@@ -77,46 +74,9 @@ Kinetic.Text = function(config) {
}
// draw text
var s = this.attrs.shadow;
if(this.attrs.textFill !== undefined) {
context.save();
context.fillStyle = this.attrs.textFill;
if(s !== undefined && !appliedShadow) {
this.applyShadow(function() {
context.fillText(that.attrs.text, tx, ty);
});
appliedShadow = true;
}
else {
context.fillText(this.attrs.text, tx, ty);
}
context.restore();
}
if(this.attrs.textStroke !== undefined || this.attrs.textStrokeWidth !== undefined) {
// defaults
if(this.attrs.textStroke === undefined) {
this.attrs.textStroke = 'black';
}
else if(this.attrs.textStrokeWidth === undefined) {
this.attrs.textStrokeWidth = 2;
}
context.lineWidth = this.attrs.textStrokeWidth;
context.strokeStyle = this.attrs.textStroke;
if(s !== undefined && !appliedShadow) {
this.applyShadow(function() {
context.strokeText(that.attrs.text, tx, ty);
});
appliedShadow = true;
}
else {
context.strokeText(this.attrs.text, tx, ty);
}
}
this.fillText(this.attrs.text, tx, ty);
this.strokeText(this.attrs.text, tx, ty);
context.restore();
};
// call super constructor