var ClickMap = (function () { const radius = 24, popCircleInner = 3, popCircleOuter = 24, green = new RGB(0, 255, 102), orange = new RGB(243, 156, 18), blue = new RGB(58, 152, 216); function newClickMap(tgt) { return new ClickMap(tgt); } function ClickMap(tgt) { var ctx = tgt.getContext("2d"), clicks = [], active = -1; if (!ctx) { console.error("invalid type", tgt); return null; } setCanvasRects(tgt); this.Click = click; this.List = list; this.Load = load; this.SetActive = setActive; this.GetMatch = getMatch; function click(c) { if (!isAction(c)) { // Our provided action is not a valid action, return early return; } if (c.constructor.name !== "Click") { // Our provided action is not a Click, make it a click c = new Click(c.x, c.y); } drawPopCicle(ctx, c, orange); clicks.push(c); } function list() { return clicks.slice(); } function load(arr) { arr.forEach(click); } function clear() { var rects = tgt.getClientRects()[0]; ctx.clearRect(0, 0, rects.width, rects.height); } function redraw() { var match = null; clicks.forEach(function (c, i) { if (active === i) { // We're going to set the match so we can draw this later // Since this is the active click, we want to ensure we draw it last match = c; } else { drawPopCicle(ctx, c, orange); } }); if (!!match) { drawPopCicle(ctx, match, green); } } function setActive(n) { active = n; clear(); redraw(); } function getMatch(x, y) { var delta = -1, match = -1, c = new Click(x, y); clicks.forEach(function (t, i) { var d = c.GetDelta(t); if (d > popCircleOuter) { return; } if (delta > -1 && d >= delta) { return } delta = d; match = i; }); return match; } } function Exports() { this.New = newClickMap; this.RGB = RGB; this.RGBa = RGBa; this.Click = Click; } function Click(x, y) { this.x = x; this.y = y; } Click.prototype.GetDelta = function (t) { var xd = this.x - t.x; if (xd < 0) { xd = xd * -1 } var yd = this.y - t.y; if (yd < 0) { yd = yd * -1; } if (xd > yd) { return xd; } return yd; }; function RGB(r, g, b) { this.r = r; this.g = g; this.b = b; } RGB.prototype.String = function () { return "rgb(" + this.r + ", " + this.g + ", " + this.b + ")"; }; RGB.prototype.RGBa = function (a) { return new RGBa(this.r, this.g, this.b, a); }; function RGBa(r, g, b, a) { this.r = r; this.g = g; this.b = b; this.a = a; } RGBa.prototype.String = function () { return "rgba(" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + ")"; }; function setCanvasRects(tgt) { var rects = tgt.getClientRects()[0]; tgt.width = rects.width; tgt.height = rects.height; } function drawPopCicle(ctx, c, rgb) { drawFadingCircle(ctx, c, popCircleOuter, rgb); drawCircle(ctx, c, popCircleInner, rgb); } function drawCircle(ctx, c, radius, rgb) { // Draw ctx.beginPath(); circleArc(ctx, c, radius); // Declare context styles ctx.fillStyle = rgb.RGBa(.8).String(); ctx.fill(); } function drawFadingCircle(ctx, c, radius, rgb) { // Draw ctx.beginPath(); circleArc(ctx, c, radius); // Add gradient stops var gradient = ctx.createRadialGradient(c.x, c.y, 0, c.x, c.y, radius); gradient.addColorStop(0, rgb.RGBa(.6).String()); gradient.addColorStop(1, rgb.RGBa(0).String()); ctx.fillStyle = gradient; ctx.fill() } function clearCircle(ctx, c, radius) { var diameter = radius * 2; ctx.beginPath(); circleArc(ctx, c, radius); ctx.clip(); ctx.clearRect(c.x - radius, c.y - radius, diameter, diameter); } function circleArc(ctx, c, radius) { ctx.arc(c.x, c.y, radius, 0, 2 * Math.PI, false); } function isAction(t) { if (typeof t !== "object") { return false; } return isNumber(t.x) && isNumber(t.y); } function isNumber(t) { return typeof t === "number"; } return new Exports(); })();