I've not done JavaScript in quite a while, but I wanted to make it so that I can circle (in many different ways) around the bounding box of a tagged item.
Is this a good way of doing what I'm wanting?
Is there a better way? E.g. can this be done with only CSS?
Or better style? E.g. can I use better, more understandable idioms?
Did I forget anything? E.g. Am I missing a case where it won't resize the circle correctly?
Please remember that e.g. means 'for the sake of example', which means that I'm only giving an example, and that there might be something else that falls into the category specified that you might think of.
http://jsfiddle.net/adrianh/4SVHH/27/
// Creating a tag with the class 'circled' will have that element circled
// using a number of different ways by setting data-style attribute to:
// 'default' - an open ellipse (default)
// 'ellipse' - a closed ellipse
// 'circle' - a closed circle circumscribed around the bounding box.
// 'rect' - a closed rectangle
// 'vbracket' - a square bracket like [ ]
// 'hbracket' - a square bracket that is rotated 90 degrees
//
// Other attributes are:
// data-width - width of lines used to circle element. (default='2')
// data-style - can be: 'solid', 'dotted' or 'dashed'. (default='solid')
// data-color - any html color. (default='red')
//
// Note: The circle is around the bounding box of the tagged object,
// so if the object wraps, it will span the paragraph width.
//
// Note: Circle will be redrawn if window is resized.
(function()
{
// draws an ellipse
function drawEllipse(context, centerX, centerY, width, height)
{
var ratio = height/width;
context.save();
context.scale(1, ratio);
context.beginPath();
context.arc(centerX, centerY/ratio, width/2, 0, 2 * Math.PI);
context.restore();
};
// returns the appropriate array for the style requested
function getLineDash(style)
{
if (style == "solid")
return [];
if (style == 'dotted')
return [3];
if (style == 'dashed')
return [6, 3];
return [];
};
// points at object that contains all the circle canvases
var circles;
// Return a canvas given an indexed number. If the same
// number is used multiple times, then that canvas is reused.
// Canvas is positioned in the (left, top) coordinate in the
// document with the specified width and height.
function getCanvas(i, left, top, width, height)
{
var canvas;
if (typeof circles.childNodes[i] != 'undefined')
{
canvas = circles.childNodes[i];
canvas.width=width;
canvas.height=height;
canvas.style.left=left+"px";
canvas.style.top=top+"px";
}
else
{
canvas = "<canvas "+
"width='"+width+"' "+
"height='"+height+"' "+
"style='"+
"position:absolute;"+
"z-index:0;"+
"left:"+left+"px;"+
"top:"+top+"px;"+
"pointer-events:none;"+
//"border:1px solid;"+
"' />";
circles.insertAdjacentHTML('beforeend', canvas);
}
return circles.childNodes[i];
};
// draw a circle circumscribed around the rect
function circleRect(i, rect, lineWidth, lineColor, lineStyle)
{
var diameter = Math.sqrt(rect.width*rect.width+rect.height*rect.height);
var cx = (rect.right + rect.left)/2;
var cy = (rect.top + rect.bottom)/2;
var left = Math.floor(cx - diameter/2);
var top = Math.floor(cy - diameter/2);
diameter = Math.floor(diameter);
var canvas = getCanvas(i, left-1, top-1, diameter+2, diameter+2);
var ctx=canvas.getContext("2d");
ctx.beginPath();
ctx.arc(diameter/2+1,diameter/2+1,diameter/2,0,2*Math.PI);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash = getLineDash(lineStyle);
ctx.stroke();
};
// draw an open ellipse around the rect.
function defaultRect(i, rect, lineWidth, lineColor, lineStyle)
{
var quarterWidth = rect.width/4;
var quarterHeight = rect.height/4;
var eighthWidth = quarterWidth/2;
var eighthHeight = quarterHeight/2;
var padx = quarterWidth;
var pady = quarterHeight*1.5;
var width = rect.width;
var height = rect.height;
var canvas = getCanvas(i, rect.left-padx, rect.top-pady, width+padx*2, height+pady*2);
//canvas.style.border="1px solid yellow";
var ctx=canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo( padx+width/2, pady+eighthHeight);
ctx.bezierCurveTo(padx-quarterWidth, pady+quarterHeight/3,
padx, pady+height+eighthHeight,
padx+width/2-eighthWidth, pady+height+eighthHeight);
ctx.bezierCurveTo(padx+width+quarterWidth, pady+height+quarterHeight,
padx+width+eighthWidth, pady-eighthHeight*3,
padx+width/2-eighthWidth, pady-quarterHeight);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash(getLineDash(lineStyle));
ctx.stroke();
// ctx.strokeStyle = "blue";
// ctx.lineWidth = 1;
// ctx.strokeRect(padx, pady, width, height);
};
// draw an ellipse around the rect
function ellipseRect(i, rect, lineWidth, lineColor, lineStyle)
{
var padx = 200;
var pady = 200;
var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
var ctx=canvas.getContext("2d");
//canvas.style.border = "1px blue solid"
drawEllipse(ctx, rect.width/2+padx, rect.height/2+pady, rect.width, rect.height);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash(getLineDash(lineStyle));
ctx.stroke();
};
// Draw a rectangle around the rect.
function rectRect(i, rect, lineWidth, lineColor, lineStyle)
{
var half = Math.ceil(lineWidth/2);
var padx = lineWidth;
var pady = lineWidth;
var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
//canvas.style.border = "1px blue solid";
var ctx=canvas.getContext("2d");
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash(getLineDash(lineStyle));
ctx.strokeRect(padx-half, pady-half, rect.width+lineWidth/2, rect.height+lineWidth/2);
};
// Draw a rectangle around the rect.
function vbracketRect(i, rect, lineWidth, lineColor, lineStyle)
{
var half = Math.ceil(lineWidth/2);
var padx = lineWidth*2;
var pady = lineWidth*2;
var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
//canvas.style.border = "1px blue solid";
var ctx=canvas.getContext("2d");
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash(getLineDash(lineStyle));
ctx.beginPath();
ctx.moveTo(padx-half+half*3 , pady-half);
ctx.lineTo(padx-half , pady-half);
ctx.lineTo(padx-half , pady+half+rect.height);
ctx.lineTo(padx-half+half*3 , pady+half+rect.height);
ctx.moveTo(padx+half+rect.width-half*3 , pady-half);
ctx.lineTo(padx+half+rect.width , pady-half);
ctx.lineTo(padx+half+rect.width , pady+half+rect.height);
ctx.lineTo(padx+half+rect.width-half*3 , pady+half+rect.height);
ctx.stroke();
};
function hbracketRect(i, rect, lineWidth, lineColor, lineStyle)
{
var half = Math.ceil(lineWidth/2);
var padx = lineWidth*2;
var pady = lineWidth*2;
var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
//canvas.style.border = "1px blue solid";
var ctx=canvas.getContext("2d");
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash(getLineDash(lineStyle));
ctx.beginPath();
ctx.moveTo(padx-half , pady-half+half*3);
ctx.lineTo(padx-half , pady-half);
ctx.lineTo(padx+half+rect.width , pady-half);
ctx.lineTo(padx+half+rect.width , pady-half+half*3);
ctx.moveTo(padx-half , pady+half+rect.height-half*3);
ctx.lineTo(padx-half , pady+half+rect.height);
ctx.lineTo(padx+half+rect.width , pady+half+rect.height);
ctx.lineTo(padx+half+rect.width , pady+half+rect.height-half*3);
ctx.stroke();
};
// if x is null return _default, otherwise return x.
function val(x, _default)
{
return (x == null) ? _default : x;
};
// This draws the circles around all 'circled' class objects.
function drawCircles()
{
$(".circled").each(function(i, obj) {
var rect = obj.getBoundingClientRect();
var type = val(obj.getAttribute("data-type"), "default");
var width = val(obj.getAttribute("data-width"), 2);
var color = val(obj.getAttribute("data-color"), "red");
var style = val(obj.getAttribute("data-style"), "solid");
if (type == 'default')
{
defaultRect(i, rect, width, color, style);
}
else if (type == 'rect')
{
rectRect(i, rect, width, color, style);
}
else if (type == 'ellipse')
{
ellipseRect(i, rect, width, color, style);
}
else if (type == 'vbracket')
{
vbracketRect(i, rect, width, color, style);
}
else if (type == 'hbracket')
{
hbracketRect(i, rect, width, color, style);
}
else if (type == 'circle')
{
circleRect(i, rect, width, color, style);
}
});
};
// Place a div container to place all of the canvases in.
document.body.insertAdjacentHTML('afterbegin', '<div id="circles"></div>');
circles = document.getElementById("circles");
// Draw the circles upon loading the document and
// if the window extents have changed.
window.onload=drawCircles;
window.onresize=drawCircles;
})();