Permalink
Browse files

Improve shadows

Can now be cast by any shape. More config options. No longer have issues with
strokeless shapes.
  • Loading branch information...
1 parent f21763d commit eeeff8ade3262e49585a36ba63b5159e5fb9eaa6 @kemayo committed Sep 2, 2011
Showing with 91 additions and 31 deletions.
  1. +91 −31 jquery.maphilight.js
View
@@ -22,53 +22,104 @@
c.getContext("2d").clearRect(0, 0, c.width, c.height);
return c;
};
- add_shape_to = function(canvas, shape, coords, options, name) {
- var i, context = canvas.getContext('2d');
+ var draw_shape = function(context, shape, coords, x_shift, y_shift) {
+ x_shift = x_shift || 0;
+ y_shift = y_shift || 0;
+
context.beginPath();
if(shape == 'rect') {
- context.rect(coords[0], coords[1], coords[2] - coords[0], coords[3] - coords[1]);
+ // x, y, width, height
+ context.rect(coords[0] + x_shift, coords[1] + y_shift, coords[2] - coords[0], coords[3] - coords[1]);
} else if(shape == 'poly') {
- context.moveTo(coords[0], coords[1]);
+ context.moveTo(coords[0] + x_shift, coords[1] + y_shift);
for(i=2; i < coords.length; i+=2) {
- context.lineTo(coords[i], coords[i+1]);
+ context.lineTo(coords[i] + x_shift, coords[i+1] + y_shift);
}
} else if(shape == 'circ') {
- context.arc(coords[0], coords[1], coords[2], 0, Math.PI * 2, false);
+ // x, y, radius, startAngle, endAngle, anticlockwise
+ context.arc(coords[0] + x_shift, coords[1] + y_shift, coords[2], 0, Math.PI * 2, false);
}
context.closePath();
+ }
+ add_shape_to = function(canvas, shape, coords, options, name) {
+ var i, context = canvas.getContext('2d');
+
+ // Because I don't want to worry about setting things back to a base state
+
+ // Shadow has to happen first, since it's on the bottom, and it does some clip /
+ // fill operations which would interfere with what comes next.
+ if(options.shadow) {
+ context.save();
+ if(options.shadowPosition == "inside") {
+ // Cause the following stroke to only apply to the inside of the path
+ draw_shape(context, shape, coords);
+ context.clip();
+ }
+
+ // Redraw the shape shifted off the canvas massively so we can cast a shadow
+ // onto the canvas without having to worry about the stroke or fill (which
+ // cannot have 0 opacity or width, since they're what cast the shadow).
+ var x_shift = canvas.width * 100;
+ var y_shift = canvas.height * 100;
+ draw_shape(context, shape, coords, x_shift, y_shift);
+
+ context.shadowOffsetX = options.shadowX - x_shift;
+ context.shadowOffsetY = options.shadowY - y_shift;
+ context.shadowBlur = options.shadowRadius;
+ context.shadowColor = css3color(options.shadowColor, options.shadowOpacity);
+
+ // Now, work out where to cast the shadow from! It looks better if it's cast
+ // from a fill when it's an outside shadow or a stroke when it's an interior
+ // shadow. Allow the user to override this if they need to.
+ var shadowFrom = options.shadowFrom;
+ if (!shadowFrom) {
+ if (options.shadowPosition == 'outside') {
+ shadowFrom = 'fill';
+ } else {
+ shadowFrom = 'stroke';
+ }
+ }
+ if (shadowFrom == 'stroke') {
+ context.strokeStyle = "rgba(0,0,0,1)";
+ context.stroke();
+ } else if (shadowFrom == 'fill') {
+ context.fillStyle = "rgba(0,0,0,1)";
+ context.fill();
+ }
+ context.restore();
+
+ // and now we clean up
+ if(options.shadowPosition == "outside") {
+ context.save();
+ // Clear out the center
+ draw_shape(context, shape, coords);
+ context.globalCompositeOperation = "destination-out";
+ context.fillStyle = "rgba(0,0,0,1);";
+ context.fill();
+ context.restore();
+ }
+ }
+
+ context.save();
+
+ draw_shape(context, shape, coords);
+
+ // fill has to come after shadow, otherwise the shadow will be drawn over the fill,
+ // which mostly looks weird when the shadow has a high opacity
if(options.fill) {
context.fillStyle = css3color(options.fillColor, options.fillOpacity);
context.fill();
}
+ // Likewise, stroke has to come at the very end, or it'll wind up under bits of the
+ // shadow or the shadow-background if it's present.
if(options.stroke) {
context.strokeStyle = css3color(options.strokeColor, options.strokeOpacity);
context.lineWidth = options.strokeWidth;
context.stroke();
}
- if(options.shadow && shape == 'rect') {
- context.save(); // store canvas state
-
- // define clipping region
- context.beginPath();
- context.rect(0, 0, canvas.width, canvas.height);
- context.rect(coords[0], coords[1]+(coords[3] - coords[1]), coords[2] - coords[0], -(coords[3] - coords[1]));
- context.closePath();
- context.clip()
-
- // draw shadow
- context.fillStyle = 'rgb(255,255,255)';
- context.beginPath();
- context.rect(coords[0], coords[1]+(coords[3] - coords[1]), coords[2] - coords[0], -(coords[3] - coords[1]));
- context.closePath();
- context.shadowOffsetX = options.shadowX;
- context.shadowOffsetY = options.shadowY;
- context.shadowBlur = options.shadowRadius;
- context.shadowColor = css3color(options.shadowColor, options.shadowOpacity);
- context.fill();
- context.stroke();
-
- context.restore(); // restore canvas state
- }
+
+ context.restore();
+
if(options.fade) {
$(canvas).css('opacity', 0).animate({opacity: 1}, 100);
}
@@ -285,6 +336,15 @@
alwaysOn: false,
neverOn: false,
groupBy: false,
- wrapClass: true
+ wrapClass: true,
+ // plenty of shadow:
+ shadow: false,
+ shadowX: 0,
+ shadowY: 0,
+ shadowRadius: 6,
+ shadowColor: '000000',
+ shadowOpacity: 0.8,
+ shadowPosition: 'outside',
+ shadowFrom: false
};
})(jQuery);

0 comments on commit eeeff8a

Please sign in to comment.