WIP: Added multi-layers

This commit is contained in:
Ray 2023-10-18 14:44:49 +01:00
parent f064d12e4c
commit e48dca5da1
6 changed files with 733 additions and 289 deletions

View File

@ -0,0 +1,175 @@
class BBTimelineBackgroundCanvas extends BBTimelineCanvas {
constructor(parentEl, el) {
super(parentEl, el);
const a = this;
a.GraphRectangle = { X: 0, Y: 0, W: 0, H: 0 };
a.Margin = 0;
a.StepHeight = 0;
a.NoStep = 0;
a.initialiseComponents();
}
initialiseComponents() {
super.initialiseComponents();
const a = this;
a.ClientRectangle = {
X: a.Parent.Padding.Left,
Y: a.Parent.Padding.Top,
W: (a.Parent.Size.W - (a.Parent.Padding.Left + a.Parent.Padding.Right)),
H: (a.Parent.Size.H - (a.Parent.Padding.Top + a.Parent.Padding.Bottom))
};
a.GraphRectangle = {
X: a.ClientRectangle.X,
Y: a.ClientRectangle.Y,
W: a.ClientRectangle.W,
H: (a.ClientRectangle.H - a.calcXAxisHeight())
};
a.Margin = (a.Parent.Marker.BorderWidth * 2);
a.StepHeight = a.Parent.Marker.Width + a.Margin;
a.NoStep = Math.floor(a.GraphRectangle.H / a.StepHeight);
a.Invalidate();
}
Invalidate() {
const a = this;
a.Clear();
a.drawAxis();
a.drawXAxisTicks();
a.drawXAxisLabels();
if (a.Parent.Debug) a.drawRectangle(a.ClientRectangle, "red");
if (a.Parent.Debug) a.drawRectangle(a.GraphRectangle, "red");
}
drawAxis() {
const a = this;
a.CTX.beginPath();
a.CTX.moveTo(a.GraphRectangle.X, a.GraphRectangle.Y);
a.CTX.lineTo(a.GraphRectangle.X, (a.GraphRectangle.H + a.GraphRectangle.Y));
a.CTX.lineTo((a.GraphRectangle.W + a.GraphRectangle.X), (a.GraphRectangle.H + a.GraphRectangle.Y));
a.CTX.lineWidth = a.Parent.Axis.LineWidth;
a.CTX.strokeStyle = a.Parent.Axis.LineColour1;
a.CTX.stroke();
}
drawXAxisTicks() {
const a = this;
let startPosX = a.GraphRectangle.X;
const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W);
const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Parent.Axis.LineWidth;
let i = 0;
while (true) {
if (startPosX >= endPosX) {
break;
}
a.CTX.beginPath();
a.CTX.moveTo(startPosX, posY);
if ((i % a.Parent.Axis.X.NoPartPerDay) == 0) {
a.CTX.lineTo(startPosX, (posY + a.Parent.Axis.X.DayLineHeight));
a.CTX.strokeStyle = a.Parent.Axis.X.DayLineColour;
} else {
a.CTX.lineTo(startPosX, (posY + a.Parent.Axis.X.HourLineHeight));
a.CTX.strokeStyle = a.Parent.Axis.X.HourLineColour;
}
a.CTX.lineWidth = a.Parent.Axis.LineWidth;
a.CTX.stroke();
startPosX += a.Parent.Axis.X.HourLineSpace;
i++;
}
}
drawXAxisLabels() {
const a = this;
const result = a.calcXAxisPositions();
const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Parent.Axis.LineWidth;
result.forEach(function(e, i) {
const date = a.Parent.ConvertToDate(e.Date);
let writeLabel = false;
if ((i == 0)) {
// Don't label first entry if too close to the next month
if (date.getDate() < 25) {
writeLabel = true;
}
} else if (date.getDate() == 1) {
writeLabel = true;
}
// if (i == 0) {
// return;
// }
const labelSize = a.drawText(e.X, (posY + a.Parent.Axis.X.DayLineHeight), a.Parent.DateToString(date, "dd"), a.Parent.Axis.Font, a.Parent.Axis.LabelColour, "center");
// Write month on first of the month
if (writeLabel) {
a.drawText(e.X, (posY + a.Parent.Axis.X.DayLineHeight + labelSize.Height + a.Parent.Axis.LabelSpacing), a.Parent.DateToString(date, "MMMM yyyy"), a.Parent.Axis.Font, a.Parent.Axis.LabelColour, "left");
}
});
}
calcXAxisPositions() {
const a = this;
const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W);
let result = [];
let x = a.GraphRectangle.X;
let date = a.Parent.ConvertToDate(a.Parent.ShowDate);
// Rollback one day
date.setDate(date.getDate() - 1);
while (true) {
if (x >= endPosX) {
break;
}
result.push({
Date: a.Parent.DateToString(date, a.Parent.DateParsePattern),
X: x
});
x += (a.Parent.Axis.X.HourLineSpace * a.Parent.Axis.X.NoPartPerDay);
date.setDate(date.getDate() + 1);
}
return result;
}
calcXAxisHeight() {
const a = this;
const labelSize = a.measureText(a.Parent.Axis.Font, "0");
return labelSize.Height + a.Parent.Axis.LabelSpacing + (a.Parent.Axis.X.DayLineHeight * 2);
}
// OnMouseDown(sender, e, event) {
// }
// OnClick(sender, e, event) {
// }
}

95
bbtimeline-canvas.js Normal file
View File

@ -0,0 +1,95 @@
class BBTimelineCanvas {
constructor(parentEl, el) {
const a = this;
a.Parent = parentEl;
a.Container = el;
a.CTX = a.Container.getContext("2d");
a.ClientRectangle = { X: 0, Y: 0, W: 0, H: 0 };
a.initialiseComponents();
}
Clear() {
const a = this;
a.CTX.clearRect(0, 0, a.CTX.canvas.width, a.CTX.canvas.height);
}
Invalidate() {
// placeholder
}
initialiseComponents() {
const a = this;
a.Container.style.width = a.Parent.Size.W + "px";
a.Container.style.height = a.Parent.Size.H + "px";
a.Container.style.position = 'absolute';
a.Container.style.border = 'none';
a.CTX.canvas.width = a.Parent.Size.W;
a.CTX.canvas.height = a.Parent.Size.H;
a.Clear();
}
drawText(x, y, label, font, foreColour, align) {
const a = this;
a.CTX.font = font;
a.CTX.fillStyle = foreColour;
const size = a.measureText(font, label);
switch (align) {
case "center":
x = (x - size.OffsetLeft);
break;
case "right":
x = (x - size.Width);
break;
case "left":
default:
// do nothing
break;
}
a.CTX.fillText(label, x, (y + size.Height));
return size;
}
drawRectangle(rectangle, colour) {
const a = this;
a.CTX.beginPath();
a.CTX.rect(rectangle.X, rectangle.Y, rectangle.W, rectangle.H);
//a.ctx.fillStyle = 'yellow';
//a.ctx.fill();
a.CTX.lineWidth = 1;
a.CTX.strokeStyle = colour;
a.CTX.stroke();
}
half(value) {
return (value / 2);
}
measureText(font, value) {
const a = this;
a.CTX.font = font;
const size = a.CTX.measureText(value);
return {
Width: size.width,
Height: size.fontBoundingBoxAscent,
OffsetLeft: a.half(size.width),
OffsetTop: a.half(size.fontBoundingBoxAscent)
};
}
}

View File

@ -0,0 +1,125 @@
class BBTimelineForegroundCanvas extends BBTimelineCanvas {
constructor(parentEl, el) {
super(parentEl, el);
const a = this;
a.initialiseComponents();
}
initialiseComponents() {
super.initialiseComponents();
const a = this;
a.Invalidate();
}
Invalidate() {
const a = this;
const rect = a.Parent.Layer.Background.GraphRectangle;
const margin = a.Parent.Layer.Background.Margin;
a.Clear();
const startPosY = (rect.Y + a.Parent.Marker.Width);
const visibleEvents = a.Parent.FindVisibleEvents();
visibleEvents.forEach(function (e, i) {
// Calculate Y position
let posY = a.calcMarkerPosition(e.Position.X, startPosY);
a.drawVerticalLine(e.Position.X, posY);
const markerRectangle = a.drawMarker(e.Position.X, posY, e.BorderColour, e.BackColour);
const labelSize = a.drawText((markerRectangle.X + markerRectangle.W + margin), markerRectangle.Y, e.Label, a.Parent.Marker.Font, a.Parent.Marker.ForeColour, "left");
e.Position = { X: e.Position.X, Y: posY };
e.HitBox = {
X: markerRectangle.X,
Y: markerRectangle.Y,
W: (markerRectangle.W + margin + labelSize.Width + a.Parent.Marker.CollisionMargin),
H: markerRectangle.H
};
if (a.Parent.Debug) a.drawRectangle(e.HitBox, "red");
if (a.Parent.Debug) console.log(e);
});
}
calcMarkerPosition(x, y) {
const a = this;
const rect = a.Parent.Layer.Background.GraphRectangle;
// Calculate Y position
let hasMoved = false;
let posY = y;
for (let i=0; i<a.Parent.Layer.Background.NoStep; i++)
{
posY = y + (a.Parent.Layer.Background.StepHeight * i);
var clippedEvent = a.Parent.FindEventsByCoords(x, posY);
if (clippedEvent == null) {
hasMoved = true;
break;
}
}
if (!hasMoved) {
posY = y;
}
return posY;
}
drawMarker(x, y, borderColour, backColour) {
const a = this;
const width = a.Parent.Marker.Width - (a.Parent.Marker.BorderWidth * 2);
a.CTX.beginPath();
a.CTX.arc(x, y, width, 0, 2 * Math.PI, false);
a.CTX.fillStyle = backColour;
a.CTX.fill();
a.CTX.lineWidth = a.Parent.Marker.BorderWidth;
a.CTX.strokeStyle = borderColour;
a.CTX.stroke();
return a.measureMarker(x, y);
}
drawVerticalLine(x, y) {
const a = this;
const rect = a.Parent.Layer.Background.GraphRectangle;
const highlightLine = a.Parent.HighlightLine;
const linePosY = (rect.Y + rect.H);
if (y <= 0) {
y = (rect.Y + highlightLine.Width);
}
a.CTX.beginPath();
a.CTX.moveTo(x, y);
a.CTX.lineTo(x, (linePosY - highlightLine.Width));
a.CTX.lineWidth = highlightLine.Width;
a.CTX.strokeStyle = highlightLine.Colour;
a.CTX.stroke();
}
measureMarker(x, y) {
const a = this;
const offset = a.half(a.Parent.Marker.Width);
const result = {
X: x - (offset + a.Parent.Marker.BorderWidth),
Y: y - (offset + a.Parent.Marker.BorderWidth),
W: (a.Parent.Marker.Width + (a.Parent.Marker.BorderWidth * 2)),
H: (a.Parent.Marker.Width + (a.Parent.Marker.BorderWidth * 2))
};
return result;
}
}

View File

@ -1,14 +1,12 @@
/** /**
* BBTimeline * BBTimeline
* @version v0.1.0.089 beta (2023/10/14 1658) * @version v0.1.1.121 beta (2023/10/18 2058)
*/ */
class BBTimeline { class BBTimeline {
constructor(el) { constructor(el) {
const a = this; const a = this;
a.Container = document.getElementById(el); a.Container = document.getElementById(el);
a.DateParsePattern = "yyyy-MM-dd";
a.Debug = false;
a.Padding = { a.Padding = {
Left: 20, Left: 20,
Top: 20, Top: 20,
@ -19,10 +17,13 @@ class BBTimeline {
W: a.Container.innerWidth || a.Container.clientWidth, W: a.Container.innerWidth || a.Container.clientWidth,
H: a.Container.innerHeight || a.Container.clientHeight H: a.Container.innerHeight || a.Container.clientHeight
}; };
a.Layer = {
Background: null,
Flourish: null,
Markers: null
};
a.ctx = a.Container.getContext("2d"); a.DateParsePattern = "yyyy-MM-dd";
a.ctx.canvas.width = a.Size.W;
a.ctx.canvas.height = a.Size.H;
a.Axis = { a.Axis = {
LineColour1: "#CFCFCF", LineColour1: "#CFCFCF",
@ -39,6 +40,7 @@ class BBTimeline {
DayLineColour: "#282828" DayLineColour: "#282828"
} }
}; };
a.Marker = { a.Marker = {
BorderColour: "#3A5D9C", BorderColour: "#3A5D9C",
BorderWidth: 2, BorderWidth: 2,
@ -48,15 +50,19 @@ class BBTimeline {
Font: "9pt Arial", Font: "9pt Arial",
CollisionMargin: 8 CollisionMargin: 8
}; };
a.HighlightLine = { a.HighlightLine = {
Colour: "#A6A6A6", Colour: "#A6A6A6",
Width: 1, Width: 1,
}; };
a.Events = []; a.Events = [];
a.StartDate = a.DateToString(new Date(), a.DateParsePattern); a.StartDate = a.DateToString(new Date(), a.DateParsePattern);
a.ShowDate = a.StartDate; a.ShowDate = a.StartDate;
a.GraphRectangle = a.calcGraphRectangle(); // a.GraphRectangle = a.calcGraphRectangle();
a.Enabled = false; a.Enabled = false;
a.Debug = false;
a.initialiseComponents(); a.initialiseComponents();
} }
@ -82,8 +88,8 @@ class BBTimeline {
CalcEndDate() { CalcEndDate() {
const a = this; const a = this;
const clientWidth = (a.Size.W - (a.Padding.Left + a.Padding.Right));
const calcdays = Math.floor(a.GraphRectangle.W / (a.Axis.X.NoPartPerDay * a.Axis.X.HourLineSpace)); const calcdays = Math.floor(clientWidth / (a.Axis.X.NoPartPerDay * a.Axis.X.HourLineSpace));
let date = a.ConvertToDate(a.ShowDate); let date = a.ConvertToDate(a.ShowDate);
date.setDate(date.getDate() + calcdays); date.setDate(date.getDate() + calcdays);
@ -97,7 +103,9 @@ class BBTimeline {
Clear() { Clear() {
const a = this; const a = this;
a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height); a.Layer.Background.Clear();
a.Layer.Flourish.Clear();
a.Layer.Markers.Clear();
a.StartDate = a.DateToString(new Date(), a.DateParsePattern); a.StartDate = a.DateToString(new Date(), a.DateParsePattern);
a.ShowDate = a.StartDate; a.ShowDate = a.StartDate;
@ -121,7 +129,7 @@ class BBTimeline {
FindDatePosition(date) { FindDatePosition(date) {
const a = this; const a = this;
const points = a.getXAxis(); const points = a.Layer.Background.calcXAxisPositions();
for (let i=0; i<points.length; i++) { for (let i=0; i<points.length; i++) {
if (points[i].Date == date){ if (points[i].Date == date){
return points[i]; return points[i];
@ -135,7 +143,8 @@ class BBTimeline {
const a = this; const a = this;
let result = []; let result = [];
const availableX = a.getXAxis(); const availableX = a.Layer.Background.calcXAxisPositions();
availableX.forEach(function (e) { availableX.forEach(function (e) {
const event = a.FindEvent(e.Date); const event = a.FindEvent(e.Date);
if (event == null) { if (event == null) {
@ -204,23 +213,23 @@ class BBTimeline {
a.Invalidate(true, true); a.Invalidate(true, true);
} }
ShowNext() { // ShowNext() {
const a = this; // const a = this;
let date = a.ConvertToDate(a.ShowDate); // let date = a.ConvertToDate(a.ShowDate);
date.setMonth(date.getMonth() + 1); // date.setMonth(date.getMonth() + 1);
a.Show(a.DateToString(date, a.DateParsePattern)); // a.Show(a.DateToString(date, a.DateParsePattern));
} // }
ShowPrevious() { // ShowPrevious() {
const a = this; // const a = this;
let date = a.ConvertToDate(a.ShowDate); // let date = a.ConvertToDate(a.ShowDate);
date.setMonth(date.getMonth() - 1); // date.setMonth(date.getMonth() - 1);
a.Show(a.DateToString(date, a.DateParsePattern)); // a.Show(a.DateToString(date, a.DateParsePattern));
} // }
UpdateLabel(date, label) { UpdateLabel(date, label) {
const a = this; const a = this;
@ -249,43 +258,54 @@ class BBTimeline {
a.Invalidate(false, true); a.Invalidate(false, true);
} }
initialiseComponents() { // initialiseComponents2() {
const a = this; // const a = this;
a.ctx.canvas.addEventListener('mousedown', function (e) { // a.ctx.canvas.addEventListener('mousedown', function (e) {
if (!a.Enabled) { // if (!a.Enabled) {
return; // return;
} // }
var event = a.FindEventsByCoords(e.offsetX, e.offsetY); // var event = a.FindEventsByCoords(e.offsetX, e.offsetY);
if (event == null) { // if (event == null) {
return; // return;
} // }
if (a.Debug) console.log(event); // if (a.Debug) console.log(event);
a.OnMouseDown(this, e, event); // a.OnMouseDown(this, e, event);
}); // });
a.ctx.canvas.addEventListener('click', function (e) { // a.ctx.canvas.addEventListener('click', function (e) {
if (!a.Enabled) { // if (!a.Enabled) {
return; // return;
} // }
var event = a.FindEventsByCoords(e.offsetX, e.offsetY); // var event = a.FindEventsByCoords(e.offsetX, e.offsetY);
if (event == null) { // if (event == null) {
return; // return;
} // }
if (a.Debug) console.log(event); // if (a.Debug) console.log(event);
a.OnClick(this, e, event); // a.OnClick(this, e, event);
}); // });
} // }
Invalidate(redrawAxis, redrawMarkers) { Invalidate(redrawAxis, redrawMarkers) {
const a = this; const a = this;
if (redrawAxis) {
a.Layer.Background.Invalidate();
}
if (redrawMarkers) {
a.Layer.Markers.Invalidate();
}
return;
if (redrawAxis) { if (redrawAxis) {
a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height); a.ctx.clearRect(0, 0, a.ctx.canvas.width, a.ctx.canvas.height);
@ -295,12 +315,12 @@ class BBTimeline {
} }
if (redrawMarkers) { if (redrawMarkers) {
a.clearChart(); // a.clearChart();
const startPosY = (a.GraphRectangle.Y + a.Marker.Width); // const startPosY = (a.GraphRectangle.Y + a.Marker.Width);
const visibleEvents = a.FindVisibleEvents(); // const visibleEvents = a.FindVisibleEvents();
if (a.Debug) console.log(visibleEvents); // if (a.Debug) console.log(visibleEvents);
visibleEvents.forEach(function (e, i) { visibleEvents.forEach(function (e, i) {
// Calculate Y position // Calculate Y position
@ -405,295 +425,319 @@ class BBTimeline {
} }
calcGraphRectangle() { // calcGraphRectangle() {
const a = this; // const a = this;
const xAxisHeight = a.calcXAxisHeight(); // // const xAxisHeight = a.calcXAxisHeight();
let result = { // // let result = {
X: a.Padding.Left, // // X: a.Padding.Left,
Y: a.Padding.Top, // // Y: a.Padding.Top,
W: (a.Size.W - (a.Padding.Left + a.Padding.Right)), // // W: (a.Size.W - (a.Padding.Left + a.Padding.Right)),
H: (a.Size.H - (a.Padding.Top + a.Padding.Bottom) - xAxisHeight), // // H: (a.Size.H - (a.Padding.Top + a.Padding.Bottom) - xAxisHeight),
Margin: (a.Marker.BorderWidth * 2) // // Margin: (a.Marker.BorderWidth * 2)
}; // // };
result.StepHeight = a.Marker.Width + result.Margin; // let result = {
result.NoStep = Math.floor(result.H / result.StepHeight); // X: a.Padding.Left,
// Y: a.Padding.Top,
// W: (a.Size.W - (a.Padding.Left + a.Padding.Right)),
// H: (a.Size.H - (a.Padding.Top + a.Padding.Bottom)),
// Margin: (a.Marker.BorderWidth * 2)
// };
return result; // result.StepHeight = a.Marker.Width + result.Margin;
} // result.NoStep = Math.floor(result.H / result.StepHeight);
calcXAxisHeight() { // return result;
const a = this; // }
const labelSize = a.measureText(a.Axis.Font, "0");
const result = labelSize.Height + a.Axis.LabelSpacing + (a.Axis.X.DayLineHeight * 2);
return result; // calcXAxisHeight() {
} // const a = this;
// const labelSize = a.measureText(a.Axis.Font, "0");
// const result = labelSize.Height + a.Axis.LabelSpacing + (a.Axis.X.DayLineHeight * 2);
// return result;
// }
calcMarkerPosition(x, y) { // calcMarkerPosition(x, y) {
const a = this; // const a = this;
// Calculate Y position // // Calculate Y position
let hasMoved = false; // let hasMoved = false;
let posY = y; // let posY = y;
for (let i=0; i<a.GraphRectangle.NoStep; i++) // for (let i=0; i<a.GraphRectangle.NoStep; i++)
{ // {
posY = y + (a.GraphRectangle.StepHeight * i); // posY = y + (a.GraphRectangle.StepHeight * i);
var clippedEvent = a.FindEventsByCoords(x, posY); // var clippedEvent = a.FindEventsByCoords(x, posY);
if (clippedEvent == null) { // if (clippedEvent == null) {
hasMoved = true; // hasMoved = true;
break; // break;
} // }
} // }
if (!hasMoved) { // if (!hasMoved) {
posY = y; // posY = y;
} // }
return posY; // return posY;
} // }
clearChart() { // clearChart() {
const a = this; // const a = this;
const rect = { // const rect = {
X: a.GraphRectangle.X, // X: a.GraphRectangle.X,
Y: a.GraphRectangle.Y, // Y: a.GraphRectangle.Y,
W: a.GraphRectangle.W, // W: a.GraphRectangle.W,
H: a.GraphRectangle.H // H: a.GraphRectangle.H
}; // };
rect.X += a.Axis.LineWidth; // rect.X += a.Axis.LineWidth;
rect.Y -= a.Padding.Top; // rect.Y -= a.Padding.Top;
rect.W -= a.Axis.LineWidth; // rect.W -= a.Axis.LineWidth;
rect.W += a.Padding.Right; // rect.W += a.Padding.Right;
rect.H -= a.Axis.LineWidth; // rect.H -= a.Axis.LineWidth;
rect.H += a.Padding.Top; // rect.H += a.Padding.Top;
a.ctx.clearRect(rect.X, rect.Y, rect.W, rect.H); // a.ctx.clearRect(rect.X, rect.Y, rect.W, rect.H);
if (a.Debug) a.drawRectangle(rect); // if (a.Debug) a.drawRectangle(rect);
// Clear marker positions // // Clear marker positions
const visibleEvents = a.FindVisibleEvents(); // const visibleEvents = a.FindVisibleEvents();
visibleEvents.forEach(function (e, i) { // visibleEvents.forEach(function (e, i) {
e.Position = { X: 0, Y: 0 }; // e.Position = { X: 0, Y: 0 };
e.HitBox = null; // e.HitBox = null;
}); // });
} // }
drawAxis() { // drawAxis() {
const a = this; // const a = this;
a.ctx.beginPath(); // a.ctx.beginPath();
a.ctx.moveTo(a.GraphRectangle.X, a.GraphRectangle.Y); // a.ctx.moveTo(a.GraphRectangle.X, a.GraphRectangle.Y);
a.ctx.lineTo(a.GraphRectangle.X, (a.GraphRectangle.H + a.GraphRectangle.Y)); // a.ctx.lineTo(a.GraphRectangle.X, (a.GraphRectangle.H + a.GraphRectangle.Y));
a.ctx.lineTo((a.GraphRectangle.W + a.GraphRectangle.X), (a.GraphRectangle.H + a.GraphRectangle.Y)); // a.ctx.lineTo((a.GraphRectangle.W + a.GraphRectangle.X), (a.GraphRectangle.H + a.GraphRectangle.Y));
a.ctx.lineWidth = a.Axis.LineWidth; // a.ctx.lineWidth = a.Axis.LineWidth;
a.ctx.strokeStyle = a.Axis.LineColour1; // a.ctx.strokeStyle = a.Axis.LineColour1;
a.ctx.stroke(); // a.ctx.stroke();
} // }
drawXAxis() { // drawXAxis() {
const a = this; // const a = this;
let startPosX = a.GraphRectangle.X; // let startPosX = a.GraphRectangle.X;
const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W); // const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W);
const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Axis.LineWidth; // const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Axis.LineWidth;
let i = 0; // let i = 0;
while (true) { // while (true) {
if (startPosX >= endPosX) { // if (startPosX >= endPosX) {
break; // break;
} // }
a.ctx.beginPath(); // a.ctx.beginPath();
a.ctx.moveTo(startPosX, posY); // a.ctx.moveTo(startPosX, posY);
if ((i % a.Axis.X.NoPartPerDay) == 0) { // if ((i % a.Axis.X.NoPartPerDay) == 0) {
a.ctx.lineTo(startPosX, (posY + a.Axis.X.DayLineHeight)); // a.ctx.lineTo(startPosX, (posY + a.Axis.X.DayLineHeight));
a.ctx.strokeStyle = a.Axis.X.DayLineColour; // a.ctx.strokeStyle = a.Axis.X.DayLineColour;
} else { // } else {
a.ctx.lineTo(startPosX, (posY + a.Axis.X.HourLineHeight)); // a.ctx.lineTo(startPosX, (posY + a.Axis.X.HourLineHeight));
a.ctx.strokeStyle = a.Axis.X.HourLineColour; // a.ctx.strokeStyle = a.Axis.X.HourLineColour;
} // }
a.ctx.lineWidth = a.Axis.LineWidth; // a.ctx.lineWidth = a.Axis.LineWidth;
a.ctx.stroke(); // a.ctx.stroke();
startPosX += a.Axis.X.HourLineSpace; // startPosX += a.Axis.X.HourLineSpace;
i++; // i++;
} // }
} // }
drawXAxisLabels() { // drawXAxisLabels() {
const a = this; // const a = this;
const result = a.getXAxis(); // const result = a.getXAxis();
const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Axis.LineWidth; // const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Axis.LineWidth;
result.forEach(function(e, i) { // result.forEach(function(e, i) {
const date = a.ConvertToDate(e.Date); // const date = a.ConvertToDate(e.Date);
let writeLabel = false; // let writeLabel = false;
if ((i == 0)) { // if ((i == 0)) {
// Don't label first entry if too close to the next month // // Don't label first entry if too close to the next month
if (date.getDate() < 25) { // if (date.getDate() < 25) {
writeLabel = true; // writeLabel = true;
} // }
} else if (date.getDate() == 1) { // } else if (date.getDate() == 1) {
writeLabel = true; // writeLabel = true;
} // }
// if (i == 0) { // // if (i == 0) {
// return; // // return;
// } // // }
const labelSize = a.drawText(e.X, (posY + a.Axis.X.DayLineHeight), a.DateToString(date, "dd"), a.Axis.Font, a.Axis.LabelColour, "center"); // const labelSize = a.drawText(e.X, (posY + a.Axis.X.DayLineHeight), a.DateToString(date, "dd"), a.Axis.Font, a.Axis.LabelColour, "center");
// Write month on first of the month // // Write month on first of the month
if (writeLabel) { // if (writeLabel) {
a.drawText(e.X, (posY + a.Axis.X.DayLineHeight + labelSize.Height + a.Axis.LabelSpacing), a.DateToString(date, "MMMM yyyy"), a.Axis.Font, a.Axis.LabelColour, "left"); // a.drawText(e.X, (posY + a.Axis.X.DayLineHeight + labelSize.Height + a.Axis.LabelSpacing), a.DateToString(date, "MMMM yyyy"), a.Axis.Font, a.Axis.LabelColour, "left");
} // }
}); // });
} // }
drawMarker(x, y, borderColour, backColour) { // drawMarker(x, y, borderColour, backColour) {
const a = this; // const a = this;
const width = a.Marker.Width - (a.Marker.BorderWidth * 2); // const width = a.Marker.Width - (a.Marker.BorderWidth * 2);
a.ctx.beginPath(); // a.ctx.beginPath();
a.ctx.arc(x, y, width, 0, 2 * Math.PI, false); // a.ctx.arc(x, y, width, 0, 2 * Math.PI, false);
a.ctx.fillStyle = backColour; // a.ctx.fillStyle = backColour;
a.ctx.fill(); // a.ctx.fill();
a.ctx.lineWidth = a.Marker.BorderWidth; // a.ctx.lineWidth = a.Marker.BorderWidth;
a.ctx.strokeStyle = borderColour; // a.ctx.strokeStyle = borderColour;
a.ctx.stroke(); // a.ctx.stroke();
return a.measureMarker(x, y); // return a.measureMarker(x, y);
} // }
drawText(x, y, label, font, foreColour, align) { // drawText(x, y, label, font, foreColour, align) {
const a = this; // const a = this;
a.ctx.font = font; // a.ctx.font = font;
a.ctx.fillStyle = foreColour; // a.ctx.fillStyle = foreColour;
const size = a.measureText(font, label); // const size = a.measureText(font, label);
switch (align) { // switch (align) {
case "center": // case "center":
x = (x - size.OffsetLeft); // x = (x - size.OffsetLeft);
break; // break;
case "right": // case "right":
x = (x - size.Width); // x = (x - size.Width);
break; // break;
case "left": // case "left":
default: // default:
// do nothing // // do nothing
break; // break;
} // }
a.ctx.fillText(label, x, (y + size.Height)); // a.ctx.fillText(label, x, (y + size.Height));
return size; // return size;
} // }
drawRectangle(rectangle) { // drawRectangle(rectangle) {
const a = this; // const a = this;
a.ctx.beginPath(); // a.ctx.beginPath();
a.ctx.rect(rectangle.X, rectangle.Y, rectangle.W, rectangle.H); // a.ctx.rect(rectangle.X, rectangle.Y, rectangle.W, rectangle.H);
//a.ctx.fillStyle = 'yellow'; // //a.ctx.fillStyle = 'yellow';
//a.ctx.fill(); // //a.ctx.fill();
a.ctx.lineWidth = 1; // a.ctx.lineWidth = 1;
a.ctx.strokeStyle = 'red'; // a.ctx.strokeStyle = 'red';
a.ctx.stroke(); // a.ctx.stroke();
} // }
drawVerticalLine(x, y) { // drawVerticalLine(x, y) {
const a = this; // const a = this;
const linePosY = (a.GraphRectangle.Y + a.GraphRectangle.H); // const linePosY = (a.GraphRectangle.Y + a.GraphRectangle.H);
if (y <= 0) { // if (y <= 0) {
y = (a.GraphRectangle.Y + a.HighlightLine.Width); // y = (a.GraphRectangle.Y + a.HighlightLine.Width);
} // }
a.ctx.beginPath(); // a.ctx.beginPath();
a.ctx.moveTo(x, y); // a.ctx.moveTo(x, y);
a.ctx.lineTo(x, (linePosY - a.HighlightLine.Width)); // a.ctx.lineTo(x, (linePosY - a.HighlightLine.Width));
a.ctx.lineWidth = a.HighlightLine.Width; // a.ctx.lineWidth = a.HighlightLine.Width;
a.ctx.strokeStyle = a.HighlightLine.Colour; // a.ctx.strokeStyle = a.HighlightLine.Colour;
a.ctx.stroke(); // a.ctx.stroke();
} // }
getXAxis() { // getXAxis() {
const a = this; // const a = this;
const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W); // const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W);
let result = []; // let result = [];
let x = a.GraphRectangle.X; // let x = a.GraphRectangle.X;
let date = a.ConvertToDate(a.ShowDate); // let date = a.ConvertToDate(a.ShowDate);
// Rollback one day // // Rollback one day
date.setDate(date.getDate() - 1); // date.setDate(date.getDate() - 1);
while (true) { // while (true) {
if (x >= endPosX) { // if (x >= endPosX) {
break; // break;
} // }
result.push({ // result.push({
Date: a.DateToString(date, a.DateParsePattern), // Date: a.DateToString(date, a.DateParsePattern),
X: x // X: x
}); // });
x += (a.Axis.X.HourLineSpace * a.Axis.X.NoPartPerDay); // x += (a.Axis.X.HourLineSpace * a.Axis.X.NoPartPerDay);
date.setDate(date.getDate() + 1); // date.setDate(date.getDate() + 1);
} // }
return result; // return result;
} // }
half(value) { // half(value) {
return (value / 2); // return (value / 2);
} // }
measureMarker(x, y) { // measureMarker(x, y) {
const a = this; // const a = this;
const offset = a.half(a.Marker.Width); // const offset = a.half(a.Marker.Width);
const result = { // const result = {
X: x - (offset + a.Marker.BorderWidth), // X: x - (offset + a.Marker.BorderWidth),
Y: y - (offset + a.Marker.BorderWidth), // Y: y - (offset + a.Marker.BorderWidth),
W: (a.Marker.Width + (a.Marker.BorderWidth * 2)), // W: (a.Marker.Width + (a.Marker.BorderWidth * 2)),
H: (a.Marker.Width + (a.Marker.BorderWidth * 2)) // H: (a.Marker.Width + (a.Marker.BorderWidth * 2))
}; // };
return result; // return result;
} // }
measureText(font, value) { // measureText(font, value) {
const a = this; // const a = this;
a.ctx.font = font; // a.ctx.font = font;
const size = a.ctx.measureText(value); // const size = a.ctx.measureText(value);
return { // return {
Width: size.width, // Width: size.width,
Height: size.fontBoundingBoxAscent, // Height: size.fontBoundingBoxAscent,
OffsetLeft: a.half(size.width), // OffsetLeft: a.half(size.width),
OffsetTop: a.half(size.fontBoundingBoxAscent) // OffsetTop: a.half(size.fontBoundingBoxAscent)
}; // };
} // }
ConvertToDate(value) { ConvertToDate(value) {
return new Date(Date.parse(value)); return new Date(Date.parse(value));
} }
initialiseComponents() {
const a = this;
a.Container.innerHTML = "<canvas></canvas><canvas></canvas><canvas></canvas>";
const canvasList = a.Container.getElementsByTagName("canvas");
a.Layer.Background = new BBTimelineBackgroundCanvas(a, canvasList[0]);
a.Layer.Flourish = new BBTimelineCanvas(a, canvasList[1]);
a.Layer.Markers = new BBTimelineForegroundCanvas(a, canvasList[2]);
}
} }

2
bbtimeline.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,10 @@
<!-- <link href="http://cdn.hiimray.co.uk/8206c600-707c-469e-8d49-a76ae35782af/bootstrap/5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" /> --> <!-- <link href="http://cdn.hiimray.co.uk/8206c600-707c-469e-8d49-a76ae35782af/bootstrap/5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" /> -->
<script src="bbtimeline.js"></script> <script src="bbtimeline.js"></script>
<script src="bbtimeline-canvas.js"></script>
<script src="bbtimeline-background-canvas.js"></script>
<script src="bbtimeline-foreground-canvas.js"></script>
<!-- <script src="bbtimeline.min.js"></script> --> <!-- <script src="bbtimeline.min.js"></script> -->
<title></title> <title></title>
@ -19,11 +23,10 @@
<div class="row"> <div class="row">
<div class="column1"> <div class="column1">
<canvas id="myCanvas"></canvas> <div id="myCanvas"></div>
<p> <p>
<textarea id="memoBox1" readonly></textarea> <textarea id="memoBox1" readonly></textarea>
</p> </p>
</div> </div>
<div class="column2"> <div class="column2">
@ -88,16 +91,6 @@ body {
padding: 20px; padding: 20px;
} }
canvas {
border-style: solid;
border-width: 1px;
border-color: #000000;
width: 100%;
height: 300px;
padding: 0;
margin: 0;
}
textarea { textarea {
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
@ -107,6 +100,7 @@ textarea {
width: 100%; width: 100%;
} }
.column1 { .column1 {
flex: 70%; flex: 70%;
padding: 20px; padding: 20px;
@ -119,6 +113,17 @@ textarea {
display: flex; display: flex;
} }
#myCanvas {
border-style: solid;
border-width: 1px;
border-color: #000000;
width: 100%;
height: 300px;
padding: 0;
margin: 0;
}
</style> </style>
<script> <script>