540 lines
10 KiB
JavaScript
540 lines
10 KiB
JavaScript
class Canvas {
|
|
|
|
constructor(el) {
|
|
const a = this;
|
|
|
|
a.container = el;
|
|
a.ctx = null;
|
|
|
|
a.flowContainer = null;
|
|
a.canvasContainer = null;
|
|
|
|
a.autoSize = true;
|
|
a.padding = {
|
|
Top: 0,
|
|
Right: 0,
|
|
Bottom: 0,
|
|
Left: 0
|
|
};
|
|
|
|
a.initialiseComponents();
|
|
}
|
|
|
|
initialiseComponents() {
|
|
const a = this;
|
|
|
|
if (a.container == null) {
|
|
return;
|
|
}
|
|
|
|
a.container.innerHTML = "<div class='border'><canvas></canvas></div>";
|
|
|
|
if (a.container != null) {
|
|
a.container.style.width = "100%";
|
|
a.container.style.height = "100%";
|
|
|
|
a.flowContainer = a.container.getElementsByTagName("div")[0];
|
|
a.canvasContainer = a.flowContainer.getElementsByTagName("canvas")[0];
|
|
a.ctx = a.canvasContainer.getContext("2d");
|
|
}
|
|
|
|
a.AutoSize = a.AutoSize;
|
|
a.Width = a.Width;
|
|
a.Height = a.Height;
|
|
|
|
a.Clear();
|
|
}
|
|
|
|
|
|
get AutoSize() {
|
|
return this.autoSize;
|
|
}
|
|
|
|
set AutoSize(value) {
|
|
const a = this;
|
|
|
|
a.autoSize = value;
|
|
|
|
if (a.flowContainer != null) {
|
|
if (value == true) {
|
|
a.flowContainer.style.overflow = "hidden";
|
|
} else {
|
|
a.flowContainer.style.overflowX = ((a.Width >= a.ClientWidth) ? "hidden" : "auto");
|
|
a.flowContainer.style.overflowY = ((a.Height >= a.ClientHeight) ? "hidden" : "auto");
|
|
}
|
|
}
|
|
}
|
|
|
|
get ClientHeight() {
|
|
const a = this;
|
|
|
|
if (a.canvasContainer == null) {
|
|
return 0;
|
|
}
|
|
|
|
return a.getHeight(a.canvasContainer);
|
|
}
|
|
|
|
set ClientHeight(value) {
|
|
const a = this;
|
|
|
|
if (a.canvasContainer != null) a.canvasContainer.style.height = value + "px";
|
|
if (a.ctx != null) a.ctx.canvas.height = value;
|
|
}
|
|
|
|
get ClientWidth() {
|
|
const a = this;
|
|
|
|
if (a.canvasContainer == null) {
|
|
return 0;
|
|
}
|
|
|
|
return a.getWidth(a.canvasContainer);
|
|
}
|
|
|
|
set ClientWidth(value) {
|
|
const a = this;
|
|
|
|
if (a.canvasContainer != null) a.canvasContainer.style.width = value + "px";
|
|
if (a.ctx != null) a.ctx.canvas.width = value;
|
|
}
|
|
|
|
get Height() {
|
|
const a = this;
|
|
|
|
if (a.container == null) {
|
|
return 0;
|
|
}
|
|
|
|
return a.getHeight(a.container);
|
|
}
|
|
|
|
set Height(value) {
|
|
const a = this;
|
|
|
|
if (a.container != null) a.container.style.height = value + "px";
|
|
if (a.flowContainer != null) a.flowContainer.style.height = value + "px";
|
|
|
|
if (a.AutoSize == true) {
|
|
a.ClientHeight = value;
|
|
}
|
|
}
|
|
|
|
get Width() {
|
|
const a = this;
|
|
|
|
if (a.container == null) {
|
|
return 0;
|
|
}
|
|
|
|
return a.getWidth(a.container);
|
|
}
|
|
|
|
set Width(value) {
|
|
const a = this;
|
|
|
|
if (a.container != null) a.container.style.width = value + "px";
|
|
if (a.flowContainer != null) a.flowContainer.style.width = value + "px";
|
|
|
|
if (a.AutoSize == true) {
|
|
a.ClientWidth = value;
|
|
}
|
|
}
|
|
|
|
get Size() {
|
|
const a = this;
|
|
|
|
return {
|
|
W: a.Width,
|
|
H: a.Height
|
|
};
|
|
}
|
|
|
|
get ClientRectangle() {
|
|
const a = this;
|
|
|
|
return {
|
|
X: a.padding.Left,
|
|
Y: a.padding.Top,
|
|
W: (a.Width - (a.padding.Left + a.padding.Right)),
|
|
H: (a.Height - (a.padding.Top + a.padding.Bottom))
|
|
};
|
|
}
|
|
|
|
get Rectangle() {
|
|
const a = this;
|
|
|
|
return {
|
|
X: 0,
|
|
Y: 0,
|
|
W: a.Width,
|
|
H: a.Height
|
|
};
|
|
}
|
|
|
|
|
|
Clear() {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height);
|
|
}
|
|
|
|
DrawArrowS(rectangle, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: [],
|
|
FillColour: null
|
|
}, options);
|
|
|
|
// Adjust for pen discrepancy
|
|
rectangle.X += 0.5;
|
|
rectangle.Y += 0.5;
|
|
rectangle.W -= opt.LineWidth;
|
|
rectangle.H -= opt.LineWidth;
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
|
|
a.ctx.moveTo(rectangle.X, rectangle.Y);
|
|
a.ctx.lineTo((rectangle.X + rectangle.W), rectangle.Y);
|
|
a.ctx.lineTo((rectangle.X + Math.half(rectangle.W)), (rectangle.Y + rectangle.H));
|
|
|
|
a.ctx.closePath();
|
|
|
|
if (opt.FillColour != null) {
|
|
a.ctx.fillStyle = opt.FillColour;
|
|
a.ctx.fill();
|
|
}
|
|
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawCircle(x, y, width, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: [],
|
|
FillColour: null
|
|
}, options);
|
|
|
|
let rectangle = {
|
|
X: x + Math.half(width),
|
|
Y: y + Math.half(width),
|
|
W: Math.half(width),
|
|
H: Math.half(width)
|
|
};
|
|
|
|
// Adjust for pen discrepancy
|
|
rectangle.X += 0.5;
|
|
rectangle.Y += 0.5;
|
|
rectangle.W -= Math.half(opt.LineWidth);
|
|
rectangle.H -= Math.half(opt.LineWidth);
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
|
|
a.ctx.arc(rectangle.X, rectangle.Y, rectangle.W, 0, 2 * Math.PI, false);
|
|
|
|
a.ctx.closePath();
|
|
|
|
if (opt.FillColour != null) {
|
|
a.ctx.fillStyle = opt.FillColour;
|
|
a.ctx.fill();
|
|
}
|
|
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawDiamond(rectangle, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: [],
|
|
FillColour: null
|
|
}, options);
|
|
|
|
// Adjust for pen discrepancy
|
|
rectangle.X += 0.5;
|
|
rectangle.Y += 0.5;
|
|
rectangle.W -= opt.LineWidth;
|
|
rectangle.H -= opt.LineWidth;
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
|
|
a.ctx.moveTo((rectangle.X + Math.half(rectangle.W)), rectangle.Y);
|
|
a.ctx.lineTo((rectangle.X + rectangle.W), (rectangle.Y + Math.half(rectangle.H)));
|
|
a.ctx.lineTo((rectangle.X + Math.half(rectangle.W)), (rectangle.Y + rectangle.H));
|
|
a.ctx.lineTo(rectangle.X, (rectangle.Y + Math.half(rectangle.H)));
|
|
|
|
a.ctx.closePath();
|
|
|
|
if (opt.FillColour != null) {
|
|
a.ctx.fillStyle = opt.FillColour;
|
|
a.ctx.fill();
|
|
}
|
|
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawRectangle(rectangle, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: [],
|
|
FillColour: null
|
|
}, options);
|
|
|
|
// Adjust for pen discrepancy
|
|
rectangle.X += 0.5;
|
|
rectangle.Y += 0.5;
|
|
rectangle.W -= opt.LineWidth;
|
|
rectangle.H -= opt.LineWidth;
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
a.ctx.rect(rectangle.X, rectangle.Y, rectangle.W, rectangle.H);
|
|
|
|
if (opt.FillColour != null) {
|
|
a.ctx.fillStyle = opt.FillColour;
|
|
a.ctx.fill();
|
|
}
|
|
|
|
a.ctx.closePath();
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawLine(startX, startY, finishX, finishY, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: []
|
|
}, options);
|
|
|
|
startY -= 0.5;
|
|
// y -= penWidth;
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
a.ctx.moveTo(startX, startY);
|
|
a.ctx.lineTo(finishX, finishY);
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawLines(points, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
if (points == null) {
|
|
return;
|
|
}
|
|
|
|
if (points.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: []
|
|
}, options);
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
|
|
a.ctx.moveTo(points[0].X, points[0].Y);
|
|
|
|
for (let i=1; i<points.length; i++) {
|
|
a.ctx.lineTo(points[i].X, points[i].Y);
|
|
}
|
|
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawHorizontalLine(x, y, width, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: []
|
|
}, options);
|
|
|
|
y -= 0.5;
|
|
// y -= penWidth;
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
a.ctx.moveTo(x, y);
|
|
a.ctx.lineTo((x + width), y);
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
DrawText(x, y, text, font, foreColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
Align: "left"
|
|
}, options);
|
|
|
|
a.ctx.font = font;
|
|
a.ctx.fillStyle = foreColour;
|
|
a.ctx.textAlign = opt.Align;
|
|
a.ctx.textBaseline = "top";
|
|
|
|
a.ctx.fillText(text, x, y);
|
|
}
|
|
|
|
DrawVerticalLine(x, y, height, penColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
LineWidth: 1,
|
|
LineDash: []
|
|
}, options);
|
|
|
|
x -= 0.5;
|
|
y -= opt.LineWidth;
|
|
|
|
a.ctx.beginPath();
|
|
a.ctx.strokeStyle = penColour;
|
|
a.ctx.lineWidth = opt.LineWidth;
|
|
a.ctx.setLineDash(opt.LineDash);
|
|
a.ctx.moveTo(x, y);
|
|
a.ctx.lineTo(x, (y + height));
|
|
a.ctx.stroke();
|
|
}
|
|
|
|
FillText(rectangle, text, font, foreColour, options) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return;
|
|
}
|
|
|
|
const opt = Object.assign({
|
|
Align: "center",
|
|
VAlign: "middle"
|
|
}, options);
|
|
|
|
a.ctx.font = font;
|
|
a.ctx.fillStyle = foreColour;
|
|
a.ctx.textAlign = opt.Align;
|
|
// a._ctx.textBaseline = verticalAlign;
|
|
a.ctx.textBaseline = "top";
|
|
|
|
const size = a.ctx.measureText(text);
|
|
const x = rectangle.X + Math.half(rectangle.W);
|
|
let y = rectangle.Y;
|
|
|
|
switch (opt.VAlign) {
|
|
case "center":
|
|
case "middle":
|
|
y += Math.half((rectangle.H - size.fontBoundingBoxDescent)) + size.fontBoundingBoxAscent;
|
|
break;
|
|
case "bottom":
|
|
y += (rectangle.H - size.fontBoundingBoxDescent);
|
|
break;
|
|
case "top":
|
|
default:
|
|
break;
|
|
}
|
|
|
|
a.ctx.fillText(text, x, y);
|
|
}
|
|
|
|
Invalidate() {
|
|
const a = this;
|
|
|
|
a.AutoSize = a.AutoSize;
|
|
a.Width = a.Width;
|
|
a.Height = a.Height;
|
|
}
|
|
|
|
MeasureText(font, value) {
|
|
const a = this;
|
|
|
|
if (a.ctx == null) {
|
|
return null;
|
|
}
|
|
|
|
a.ctx.font = font;
|
|
|
|
const size = a.ctx.measureText(value);
|
|
|
|
return {
|
|
W: size.width,
|
|
H: Math.round(size.fontBoundingBoxDescent + size.fontBoundingBoxAscent)
|
|
};
|
|
}
|
|
|
|
getWidth(el) {
|
|
const result = (el.offsetWidth || el.innerWidth || el.clientWidth);
|
|
|
|
return result;
|
|
}
|
|
|
|
getHeight(el) {
|
|
const result = (el.offsetHeight || el.innerHeight || el.clientHeight);
|
|
|
|
return result;
|
|
}
|
|
|
|
} |