Canvas Inner Shadow on Polygon

Inner Shadow effect that supports polygons, even with holes.

by warriormole

HTML

<script src="https://rawgit.com/MadLittleMods/4dd235b9cdcd79c57efd/raw/22aa676be65567b99536fb79e2b6b724495bde43/clipper-6-1-3a.js"></script>
<div>
    Created by <a href="http://ericeastwood.com/">Eric Eastwood</a>
    <br />
    Inner Shadow effect
</div>
<div>
Using this technique: <a href="http://www.rgraph.net/blog/2013/april/an-example-of-html5-canvas-shadows.html">RGraph: example of using canvas shadows</a>
</div>
<div>
Using <a href="http://sourceforge.net/projects/jsclipper/">jsClipper</a> for offsetting polygons.
</div>

<canvas class="main-canvas" width="720" height="480"></canvas>

CSS

body
{
    background: #333333;
    
    color: #eeeeee;
    font-family: Arial;
    font-size: 14px;
}

a
{
    color: inherit;
}

JavaScript

function polygon_offset(pointList, offset)
{
	// Scale up the path so it is easier to see
	//var scale = 100;
	//ClipperLib.JS.ScaleUpPath(holePointList, scale);

	// Possibly ClipperLib.Clipper.SimplifyPolygons() here
	// Possibly ClipperLib.Clipper.CleanPolygons() here

	// MiterLimit: http://sourceforge.net/p/jsclipper/wiki/documentation/#clipperlibclipperoffsetmiterlimit
	// roundPrecision
	var co = new ClipperLib.ClipperOffset(20, 0.25);

	co.AddPath(pointList, ClipperLib.JoinType.jtMiter, ClipperLib.EndType.etClosedPolygon);
	var offsetted_paths = new ClipperLib.Paths();
	co.Execute(offsetted_paths, offset);


	if(offsetted_paths.length)
	{
		var resultant_point_list = [];
		offsetted_paths[0].forEach(function(point, index, array) {
			// Clipper uses a slightly differnt format
			resultant_point_list.push(new Vector2(point.X, point.Y));
		});
	}

	return resultant_point_list || false;
}



function Vector2(x, y) 
{
	this.x = x;
	this.X = x;
	this.y = y;
	this.Y = y;
}
Vector2.prototype.add = function(other) {
	return new Vector2(this.x + other.x, this.y + other.y);
}
Vector2.prototype.subtract = function(other) {
	return new Vector2(this.x - other.x, this.y - other.y);
}
Vector2.prototype.scale = function(scalar) {
	return new Vector2(this.x*scalar, this.y*scalar);
}
Vector2.prototype.normalized = function() {
	var magnitude = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
	return new Vector2(this.x/magnitude, this.y/magnitude);
}
Vector2.prototype.dot = function(other) {
	return this.x*other.x + this.y*other.y;
}
Vector2.prototype.closestPointOnLine = function(pt1, pt2)
{
	var A = this.subtract(pt1);
	var u = pt2.subtract(pt1).normalized();
	
	return pt1.add(u.scale(A.dot(u)));
}
Vector2.prototype.vector2Args = function(x, y) {
	x = x || 0;
	y = y || 0;
	return [this.x + x, this.y + y];
}
		
function Rect(position, width, height)
{
	this.position = position;
	this.width = width;
	this.height = height;
}
Rect.prototype.rectArgs = function(x, y,...