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? I.e. can this be done with only css?
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;
})();