From 5637d8d3b989b6282ca82448a19c93a0686be394 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 20 Oct 2023 00:22:29 +0100 Subject: [PATCH] Implemented multiple canvas layers --- bbtimeline-background-canvas.js | 143 ++++---- bbtimeline-canvas.js | 21 +- bbtimeline-flourish-canvas.js | 22 ++ bbtimeline-foreground-canvas.js | 98 +++++- bbtimeline.js | 587 +++++++------------------------- bbtimeline.min.js | 1 - demo-test.html | 39 ++- 7 files changed, 348 insertions(+), 563 deletions(-) create mode 100644 bbtimeline-flourish-canvas.js diff --git a/bbtimeline-background-canvas.js b/bbtimeline-background-canvas.js index a678a0a..05a3708 100644 --- a/bbtimeline-background-canvas.js +++ b/bbtimeline-background-canvas.js @@ -28,7 +28,7 @@ class BBTimelineBackgroundCanvas extends BBTimelineCanvas { X: a.ClientRectangle.X, Y: a.ClientRectangle.Y, W: a.ClientRectangle.W, - H: (a.ClientRectangle.H - a.calcXAxisHeight()) + H: (a.ClientRectangle.H - a.XAxisHeight) }; a.Margin = (a.Parent.Marker.BorderWidth * 2); @@ -38,6 +38,43 @@ class BBTimelineBackgroundCanvas extends BBTimelineCanvas { a.Invalidate(); } + + get XAxisHeight() { + 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); + } + + get XAxisPositions() { + 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.DateToInternalString(date), + X: x + }); + + x += (a.Parent.Axis.X.HourLineSpace * a.Parent.Axis.X.NoPartPerDay); + date.setDate(date.getDate() + 1); + } + + return result; + } + + Invalidate() { const a = this; @@ -64,6 +101,36 @@ class BBTimelineBackgroundCanvas extends BBTimelineCanvas { a.CTX.stroke(); } + drawXAxisLabels() { + const a = this; + const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Parent.Axis.LineWidth; + + a.XAxisPositions.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"); + } + }); + } + drawXAxisTicks() { const a = this; @@ -98,78 +165,4 @@ class BBTimelineBackgroundCanvas extends BBTimelineCanvas { } } - 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) { - // } - - } \ No newline at end of file diff --git a/bbtimeline-canvas.js b/bbtimeline-canvas.js index 97f49a0..6f2a5d3 100644 --- a/bbtimeline-canvas.js +++ b/bbtimeline-canvas.js @@ -11,16 +11,6 @@ class BBTimelineCanvas { 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; @@ -36,6 +26,17 @@ class BBTimelineCanvas { } + Clear() { + const a = this; + + a.CTX.clearRect(0, 0, a.CTX.canvas.width, a.CTX.canvas.height); + } + + Invalidate() { + // placeholder + } + + drawText(x, y, label, font, foreColour, align) { const a = this; diff --git a/bbtimeline-flourish-canvas.js b/bbtimeline-flourish-canvas.js new file mode 100644 index 0000000..ef38b88 --- /dev/null +++ b/bbtimeline-flourish-canvas.js @@ -0,0 +1,22 @@ +class BBTimelineFlourishCanvas extends BBTimelineCanvas { + constructor(parentEl, el) { + super(parentEl, el); + + } + + DrawVerticalLine(x) { + const a = this; + const rect = a.Parent.Layer.Background.GraphRectangle; + const linePosY = (rect.Y + rect.H); + + a.Clear(); + + a.CTX.beginPath(); + a.CTX.moveTo(x, rect.Y); + a.CTX.lineTo(x, (linePosY - a.Parent.Marker.Line.Width)); + a.CTX.lineWidth = 1; + a.CTX.strokeStyle = a.Parent.HotTrack.Colour; + a.CTX.stroke(); + } + +} \ No newline at end of file diff --git a/bbtimeline-foreground-canvas.js b/bbtimeline-foreground-canvas.js index 0e4af99..31fe2fb 100644 --- a/bbtimeline-foreground-canvas.js +++ b/bbtimeline-foreground-canvas.js @@ -4,7 +4,7 @@ class BBTimelineForegroundCanvas extends BBTimelineCanvas { const a = this; - a.initialiseComponents(); + // a.initialiseComponents(); } initialiseComponents() { @@ -12,6 +12,75 @@ class BBTimelineForegroundCanvas extends BBTimelineCanvas { const a = this; + a.CTX.canvas.addEventListener('mousedown', function (e) { + if (!a.Parent.Enabled) { + return; + } + + var event = a.Parent.FindEventsByCoords(e.offsetX, e.offsetY); + if (event == null) { + return; + } + + if (a.Parent.Debug) console.log(event); + + a.Parent.OnMouseDown(this, e, event); + }); + + a.CTX.canvas.addEventListener('click', function (e) { + if (!a.Parent.Enabled) { + return; + } + + var event = a.Parent.FindEventsByCoords(e.offsetX, e.offsetY); + if (event == null) { + return; + } + + if (a.Parent.Debug) console.log(event); + + a.Parent.OnClick(this, e, event); + }); + + a.CTX.canvas.addEventListener('dblclick', function (e) { + if (!a.Parent.Enabled) { + return; + } + + var event = a.Parent.FindEventsByCoords(e.offsetX, e.offsetY); + if (event == null) { + return; + } + + if (a.Parent.Debug) console.log(event); + + a.Parent.OnDblClick(this, e, event); + }); + + if (a.Parent.EnableHotTracking) { + a.CTX.canvas.addEventListener('mousemove', function (e) { + if (!a.Parent.Enabled) { + return; + } + + if (!a.Parent.EnableHotTracking) { + return; + } + + const point = { X: e.offsetX, Y: e.offsetY }; + if (a.Parent.HasInterception(a.Parent.Layer.Background.GraphRectangle, point)) { + if (a.Parent.Debug) console.log(point); + + a.Parent.DrawHotTracking(point.X); + + a.Parent.OnMouseMove(this, e, point); + } else { + // Clear hot tracking + a.Parent.DrawHotTracking(-1); + } + }); + } + a.Invalidate(); } @@ -24,7 +93,7 @@ class BBTimelineForegroundCanvas extends BBTimelineCanvas { const startPosY = (rect.Y + a.Parent.Marker.Width); - const visibleEvents = a.Parent.FindVisibleEvents(); + const visibleEvents = a.Parent.VisibleEvents; visibleEvents.forEach(function (e, i) { // Calculate Y position let posY = a.calcMarkerPosition(e.Position.X, startPosY); @@ -48,6 +117,21 @@ class BBTimelineForegroundCanvas extends BBTimelineCanvas { }); } + OnMouseDown(e) { + if (!a.Parent.Enabled) { + return; + } + + var event = a.Parent.FindEventsByCoords(e.offsetX, e.offsetY); + if (event == null) { + return; + } + + if (a.Parent.Debug) console.log(event); + + console.log("!"); + a.Parent.OnMouseDown(this, e, event); + } calcMarkerPosition(x, y) { const a = this; @@ -92,18 +176,17 @@ class BBTimelineForegroundCanvas extends BBTimelineCanvas { 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); + y = (rect.Y + a.Parent.Marker.Line.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.lineTo(x, (linePosY - a.Parent.Marker.Line.Width)); + a.CTX.lineWidth = a.Parent.Marker.Line.Width; + a.CTX.strokeStyle = a.Parent.Marker.Line.Colour; a.CTX.stroke(); } @@ -121,5 +204,4 @@ class BBTimelineForegroundCanvas extends BBTimelineCanvas { return result; } - } \ No newline at end of file diff --git a/bbtimeline.js b/bbtimeline.js index 51088b6..df6cb3b 100644 --- a/bbtimeline.js +++ b/bbtimeline.js @@ -48,33 +48,112 @@ class BBTimeline { Width: 10, ForeColour: "#3A5D9C", Font: "9pt Arial", - CollisionMargin: 8 + CollisionMargin: 8, + Line: { + Colour: "#A6A6A6", + Width: 1, + } }; - a.HighlightLine = { - Colour: "#A6A6A6", - Width: 1, + a.HotTrack = { + Colour: "#1D7F1D" }; a.Events = []; - a.StartDate = a.DateToString(new Date(), a.DateParsePattern); + a.StartDate = a.DateToInternalString(new Date()); a.ShowDate = a.StartDate; - // a.GraphRectangle = a.calcGraphRectangle(); a.Enabled = false; a.Debug = false; + a.EnableHotTracking = true; a.initialiseComponents(); } + + get CTX() { + return a.Layer.Markers.CTX; + } + + get NewEvent() { + const a = this; + + return { + Date: "", + Label: "", + Position: { X: 0, Y: 0 }, + Events: [], + HitBox: null, + BorderColour: a.Marker.BorderColour, + BackColour: a.Marker.BackColour + }; + } + + get NewEventItem() { + return { + Title: "", + Description: "", + Link: "", + Tag: null + }; + } + + get VisibleDays() { + const a = this; + const clientWidth = (a.Size.W - (a.Padding.Left + a.Padding.Right)); + + return Math.floor(clientWidth / (a.Axis.X.NoPartPerDay * a.Axis.X.HourLineSpace)); + } + + get VisibleStartDate() { + const a = this; + + return a.ConvertToDate(a.ShowDate); + } + + get VisibleEndDate() { + const a = this; + + let date = a.ConvertToDate(a.ShowDate); + date.setDate(date.getDate() + a.VisibleDays); + + // Minus one for lead up + date.setDate(date.getDate() - 1); + + return a.DateToString(date, a.DateParsePattern); + } + + get VisibleEvents() { + const a = this; + let result = []; + + a.Layer.Background.XAxisPositions.forEach(function (e) { + const event = a.FindEvent(e.Date); + if (event == null) { + return; + } + + // Set offsetX on current view + event.Position.X = e.X; + + result.push(event); + }); + + return result; + } + + AddEvent(date, label, options) { const a = this; - const _options = Object.assign(a.GenerateEventItem(), options); + const _options = Object.assign(a.NewEventItem, options); let event = a.FindEvent(date); if (event == null) { - a.Events.push(a.GenerateEvent(date)); + let newEvent = a.NewEvent; + newEvent.Date = date; + + a.Events.push(newEvent); event = a.FindEvent(date); } @@ -86,20 +165,6 @@ class BBTimeline { event.Events.push(_options); } - CalcEndDate() { - const a = this; - const clientWidth = (a.Size.W - (a.Padding.Left + a.Padding.Right)); - const calcdays = Math.floor(clientWidth / (a.Axis.X.NoPartPerDay * a.Axis.X.HourLineSpace)); - - let date = a.ConvertToDate(a.ShowDate); - date.setDate(date.getDate() + calcdays); - - // Minus one for lead up - date.setDate(date.getDate() - 1); - - return a.DateToString(date, a.DateParsePattern); - } - Clear() { const a = this; @@ -107,7 +172,7 @@ class BBTimeline { a.Layer.Flourish.Clear(); a.Layer.Markers.Clear(); - a.StartDate = a.DateToString(new Date(), a.DateParsePattern); + a.StartDate = a.DateToInternalString(new Date()); a.ShowDate = a.StartDate; a.Enabled = false; a.Events = []; @@ -128,8 +193,8 @@ class BBTimeline { FindDatePosition(date) { const a = this; + const points = a.Layer.Background.XAxisPositions; - const points = a.Layer.Background.calcXAxisPositions(); for (let i=0; i= endPosX) { - // break; - // } - - // a.ctx.beginPath(); - // a.ctx.moveTo(startPosX, posY); - - // if ((i % a.Axis.X.NoPartPerDay) == 0) { - // a.ctx.lineTo(startPosX, (posY + a.Axis.X.DayLineHeight)); - // a.ctx.strokeStyle = a.Axis.X.DayLineColour; - // } else { - // a.ctx.lineTo(startPosX, (posY + a.Axis.X.HourLineHeight)); - // a.ctx.strokeStyle = a.Axis.X.HourLineColour; - // } - - // a.ctx.lineWidth = a.Axis.LineWidth; - // a.ctx.stroke(); - - // startPosX += a.Axis.X.HourLineSpace; - - // i++; - // } - // } - - // drawXAxisLabels() { - // const a = this; - - // const result = a.getXAxis(); - // const posY = (a.GraphRectangle.Y + a.GraphRectangle.H) + a.Axis.LineWidth; - - // result.forEach(function(e, i) { - // const date = a.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.Axis.X.DayLineHeight), a.DateToString(date, "dd"), a.Axis.Font, a.Axis.LabelColour, "center"); - - // // Write month on first of the month - // 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"); - // } - // }); - // } - - // drawMarker(x, y, borderColour, backColour) { - // const a = this; - // const width = a.Marker.Width - (a.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.Marker.BorderWidth; - // a.ctx.strokeStyle = borderColour; - // a.ctx.stroke(); - - // return a.measureMarker(x, y); - // } - - // 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) { - // 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 = 'red'; - // a.ctx.stroke(); - // } - - // drawVerticalLine(x, y) { - // const a = this; - // const linePosY = (a.GraphRectangle.Y + a.GraphRectangle.H); - - // if (y <= 0) { - // y = (a.GraphRectangle.Y + a.HighlightLine.Width); - // } - - // a.ctx.beginPath(); - // a.ctx.moveTo(x, y); - // a.ctx.lineTo(x, (linePosY - a.HighlightLine.Width)); - // a.ctx.lineWidth = a.HighlightLine.Width; - // a.ctx.strokeStyle = a.HighlightLine.Colour; - // a.ctx.stroke(); - // } - - // getXAxis() { - // const a = this; - // const endPosX = (a.GraphRectangle.X + a.GraphRectangle.W); - - // let result = []; - // let x = a.GraphRectangle.X; - // let date = a.ConvertToDate(a.ShowDate); - - // // Rollback one day - // date.setDate(date.getDate() - 1); - - // while (true) { - // if (x >= endPosX) { - // break; - // } - - // result.push({ - // Date: a.DateToString(date, a.DateParsePattern), - // X: x - // }); - - // x += (a.Axis.X.HourLineSpace * a.Axis.X.NoPartPerDay); - // date.setDate(date.getDate() + 1); - // } - - // return result; - // } - - // half(value) { - // return (value / 2); - // } - - // measureMarker(x, y) { - // const a = this; - // const offset = a.half(a.Marker.Width); - - // const result = { - // X: x - (offset + a.Marker.BorderWidth), - // Y: y - (offset + a.Marker.BorderWidth), - // W: (a.Marker.Width + (a.Marker.BorderWidth * 2)), - // H: (a.Marker.Width + (a.Marker.BorderWidth * 2)) - // }; - - // return result; - // } - - // 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) - // }; - // } - ConvertToDate(value) { return new Date(Date.parse(value)); } + HasInterception(rect, point) { + const x2 = (rect.X + rect.W); + const y2 = (rect.Y + rect.H); + return ((point.X >= rect.X) && (point.X <= x2) && (point.Y >= rect.Y) && (point.Y <= y2)); + } + + + OnMouseDown(sender, e, event) { /* delegate */ } + + OnMouseMove(sender, e, event) { /* delegate */ } + + OnClick(sender, e, event) { /* delegate */ } + + OnDblClick(sender, e, event) { /* delegate */ } initialiseComponents() { @@ -735,7 +408,7 @@ class BBTimeline { const canvasList = a.Container.getElementsByTagName("canvas"); a.Layer.Background = new BBTimelineBackgroundCanvas(a, canvasList[0]); - a.Layer.Flourish = new BBTimelineCanvas(a, canvasList[1]); + a.Layer.Flourish = new BBTimelineFlourishCanvas(a, canvasList[1]); a.Layer.Markers = new BBTimelineForegroundCanvas(a, canvasList[2]); } diff --git a/bbtimeline.min.js b/bbtimeline.min.js index 14f7735..fd5144b 100644 --- a/bbtimeline.min.js +++ b/bbtimeline.min.js @@ -2,4 +2,3 @@ * BBTimeline * @version v0.1.0.089 beta (2023/10/14 1658) */ -class BBTimeline{constructor(el){const a=this;a.Container=document.getElementById(el),a.DateParsePattern="yyyy-MM-dd",a.Debug=!1,a.Padding={Left:20,Top:20,Right:20,Bottom:0},a.Size={W:a.Container.innerWidth||a.Container.clientWidth,H:a.Container.innerHeight||a.Container.clientHeight},a.ctx=a.Container.getContext("2d"),a.ctx.canvas.width=a.Size.W,a.ctx.canvas.height=a.Size.H,a.Axis={LineColour1:"#CFCFCF",LineWidth:1,Font:"8pt Arial",LabelColour:"#000000",LabelSpacing:6,X:{NoPartPerDay:4,HourLineSpace:6,HourLineHeight:10,HourLineColour:"#A6A6A6",DayLineHeight:20,DayLineColour:"#282828"}},a.Marker={BorderColour:"#3A5D9C",BorderWidth:2,BackColour:"#D4DEEF",Width:10,ForeColour:"#3A5D9C",Font:"9pt Arial",CollisionMargin:8},a.HighlightLine={Colour:"#A6A6A6",Width:1},a.Events=[],a.StartDate=a.DateToString(new Date,a.DateParsePattern),a.ShowDate=a.StartDate,a.GraphRectangle=a.calcGraphRectangle(),a.Enabled=!1,a.initialiseComponents()}AddEvent(date,label,options){const a=this,_options=Object.assign(a.GenerateEventItem(),options);let event=a.FindEvent(date);null==event&&(a.Events.push(a.GenerateEvent(date)),event=a.FindEvent(date)),null!=label&&(event.Label=label),event.Events.push(_options)}CalcEndDate(){const a=this,calcdays=Math.floor(a.GraphRectangle.W/(a.Axis.X.NoPartPerDay*a.Axis.X.HourLineSpace));let date=a.ConvertToDate(a.ShowDate);return date.setDate(date.getDate()+calcdays),date.setDate(date.getDate()-1),a.DateToString(date,a.DateParsePattern)}Clear(){const a=this;a.ctx.clearRect(0,0,a.ctx.canvas.width,a.ctx.canvas.height),a.StartDate=a.DateToString(new Date,a.DateParsePattern),a.ShowDate=a.StartDate,a.Enabled=!1,a.Events=[]}DeleteMarker(date){const a=this;for(let i=0;i=e.X&&x<=x2&&y>=e.Y&&y<=y2)return a.Events[i]}return null}Load(startDate){const a=this;a.StartDate=startDate,a.Show(startDate)}Show(date){const a=this;a.ConvertToDate(date)12?date.getHours()-12:date.getHours()).toString().padStart(2,"0")),result=result.replace("mm",date.getMinutes().toString().padStart(2,"0")),result=result.replace("ss",date.getSeconds().toString().padStart(2,"0")),result=result.replace("ff",date.getMilliseconds().toString().padStart(2,"0")),result=result.replace("tt","{5}"),result=result.replace("zz",""),result=result.replace("y",date.getFullYear().toString()),result=result.replace("M",(date.getMonth()+1).toString()),result=result.replace("d",date.getDate().toString()),result=result.replace("H",date.getHours().toString()),result=result.replace("h",(date.getHours()>12?date.getHours()-12:date.getHours()).toString()),result=result.replace("m",date.getMinutes().toString()),result=result.replace("s",date.getSeconds().toString()),result=result.replace("z",""),result=result.replace("t","{6}"),result=result.replace("Z",""),result=result.replace("{1}",date.toLocaleString("default",{month:"long"})),result=result.replace("{2}",date.toLocaleString("default",{weekday:"long"})),result=result.replace("{3}",date.toLocaleString("default",{month:"short"})),result=result.replace("{4}",date.toLocaleString("default",{weekday:"short"})),result=result.replace("{5}",date.getHours()>=12?"PM":"AM"),result=result.replace("{6}",date.getHours()>=12?"P":"A"),result}OnMouseDown(sender,e,event){}OnClick(sender,e,event){}calcGraphRectangle(){const a=this,xAxisHeight=a.calcXAxisHeight();let result={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)-xAxisHeight,Margin:2*a.Marker.BorderWidth};return result.StepHeight=a.Marker.Width+result.Margin,result.NoStep=Math.floor(result.H/result.StepHeight),result}calcXAxisHeight(){const a=this,labelSize=undefined,result=undefined;return a.measureText(a.Axis.Font,"0").Height+a.Axis.LabelSpacing+2*a.Axis.X.DayLineHeight}calcMarkerPosition(x,y){const a=this;let hasMoved=!1,posY=y;for(let i=0;i=endPosX);)a.ctx.beginPath(),a.ctx.moveTo(startPosX,posY),i%a.Axis.X.NoPartPerDay==0?(a.ctx.lineTo(startPosX,posY+a.Axis.X.DayLineHeight),a.ctx.strokeStyle=a.Axis.X.DayLineColour):(a.ctx.lineTo(startPosX,posY+a.Axis.X.HourLineHeight),a.ctx.strokeStyle=a.Axis.X.HourLineColour),a.ctx.lineWidth=a.Axis.LineWidth,a.ctx.stroke(),startPosX+=a.Axis.X.HourLineSpace,i++}drawXAxisLabels(){const a=this,result=a.getXAxis(),posY=a.GraphRectangle.Y+a.GraphRectangle.H+a.Axis.LineWidth;result.forEach((function(e,i){const date=a.ConvertToDate(e.Date);let writeLabel=!1;0==i?date.getDate()<25&&(writeLabel=!0):1==date.getDate()&&(writeLabel=!0);const labelSize=a.drawText(e.X,posY+a.Axis.X.DayLineHeight,a.DateToString(date,"dd"),a.Axis.Font,a.Axis.LabelColour,"center");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")}))}drawMarker(x,y,borderColour,backColour){const a=this,width=a.Marker.Width-2*a.Marker.BorderWidth;return a.ctx.beginPath(),a.ctx.arc(x,y,width,0,2*Math.PI,!1),a.ctx.fillStyle=backColour,a.ctx.fill(),a.ctx.lineWidth=a.Marker.BorderWidth,a.ctx.strokeStyle=borderColour,a.ctx.stroke(),a.measureMarker(x,y)}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-=size.OffsetLeft;break;case"right":x-=size.Width}return a.ctx.fillText(label,x,y+size.Height),size}drawRectangle(rectangle){const a=this;a.ctx.beginPath(),a.ctx.rect(rectangle.X,rectangle.Y,rectangle.W,rectangle.H),a.ctx.lineWidth=1,a.ctx.strokeStyle="red",a.ctx.stroke()}drawVerticalLine(x,y){const a=this,linePosY=a.GraphRectangle.Y+a.GraphRectangle.H;y<=0&&(y=a.GraphRectangle.Y+a.HighlightLine.Width),a.ctx.beginPath(),a.ctx.moveTo(x,y),a.ctx.lineTo(x,linePosY-a.HighlightLine.Width),a.ctx.lineWidth=a.HighlightLine.Width,a.ctx.strokeStyle=a.HighlightLine.Colour,a.ctx.stroke()}getXAxis(){const a=this,endPosX=a.GraphRectangle.X+a.GraphRectangle.W;let result=[],x=a.GraphRectangle.X,date=a.ConvertToDate(a.ShowDate);for(date.setDate(date.getDate()-1);!(x>=endPosX);)result.push({Date:a.DateToString(date,a.DateParsePattern),X:x}),x+=a.Axis.X.HourLineSpace*a.Axis.X.NoPartPerDay,date.setDate(date.getDate()+1);return result}half(value){return value/2}measureMarker(x,y){const a=this,offset=a.half(a.Marker.Width),result=undefined;return{X:x-(offset+a.Marker.BorderWidth),Y:y-(offset+a.Marker.BorderWidth),W:a.Marker.Width+2*a.Marker.BorderWidth,H:a.Marker.Width+2*a.Marker.BorderWidth}}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)}}ConvertToDate(value){return new Date(Date.parse(value))}} \ No newline at end of file diff --git a/demo-test.html b/demo-test.html index 1a5feb6..4a80229 100644 --- a/demo-test.html +++ b/demo-test.html @@ -13,6 +13,7 @@ + @@ -34,6 +35,7 @@

+


@@ -50,8 +52,8 @@

- - + +


@@ -145,6 +147,14 @@ timeline1.OnClick = function(sender, e, event) { LogInfo(JSON.stringify(event)); LogInfo(""); } +timeline1.OnDblClick = function(sender, e, event) { + LogInfo(""); + LogInfo("OnDblClick"); + LogInfo(JSON.stringify(sender)); + LogInfo(JSON.stringify(e)); + LogInfo(JSON.stringify(event)); + LogInfo(""); +} SetToday(); @@ -161,17 +171,22 @@ function ToggleDebug() { timeline1.Invalidate(true, true); } +function ToggleHotTracking() { + timeline1.EnableHotTracking = !timeline1.EnableHotTracking; + timeline1.Invalidate(true, true); +} + function Refresh() { timeline1.Invalidate(true, true); } function SetToday() { const msPerDay = 1000 * 60 * 60 * 24; - const startDate = timeline1.DateToString(new Date(), "yyyy-MM-dd"); + const startDate = timeline1.DateToInternalString(new Date()); timeline1.Load(startDate); - const endDate = timeline1.CalcEndDate(); + const endDate = timeline1.VisibleEndDate; const noDays = Math.floor((timeline1.ConvertToDate(endDate) - timeline1.ConvertToDate(startDate)) / msPerDay); LogInfo("Set start date to today (" + startDate + ")"); @@ -180,7 +195,7 @@ function SetToday() { function GenerateRandomMarker() { const msPerDay = 1000 * 60 * 60 * 24; - const endDate = timeline1.CalcEndDate(); + const endDate = timeline1.VisibleEndDate; const noDays = Math.floor((timeline1.ConvertToDate(endDate) - timeline1.ConvertToDate(timeline1.ShowDate)) / msPerDay); let randomDay = GetRandy(1, (noDays - 1)); @@ -188,7 +203,7 @@ function GenerateRandomMarker() { let date = timeline1.ConvertToDate(timeline1.ShowDate); date.setDate(date.getDate() + randomDay); - const markerDate = timeline1.DateToString(date, timeline1.DateParsePattern); + const markerDate = timeline1.DateToInternalString(date); const markerName = "Random Marker #" + GetRandy(10000, 99999); @@ -204,20 +219,20 @@ function GoToToday() { LogInfo("Go to " + timeline1.ShowDate); } -function GoToPreviousMonth() { +function GoToPrevious() { timeline1.ShowPrevious(); LogInfo("Go to " + timeline1.ShowDate); } -function GoToNextMonth() { +function GoToNext() { timeline1.ShowNext(); LogInfo("Go to " + timeline1.ShowDate); } function UpdateLabel() { - const visibleMarkers = timeline1.FindVisibleEvents(); + const visibleMarkers = timeline1.VisibleEvents; if (visibleMarkers.length <= 0) { LogInfo("No visible markers"); return; @@ -232,7 +247,7 @@ function UpdateLabel() { } function UpdateMarker() { - const visibleMarkers = timeline1.FindVisibleEvents(); + const visibleMarkers = timeline1.VisibleEvents; if (visibleMarkers.length <= 0) { LogInfo("No visible markers"); return; @@ -246,7 +261,7 @@ function UpdateMarker() { } function DeleteMarker() { - const visibleMarkers = timeline1.FindVisibleEvents(); + const visibleMarkers = timeline1.VisibleEvents; if (visibleMarkers.length <= 0) { LogInfo("No visible markers"); return; @@ -261,7 +276,7 @@ function DeleteMarker() { } function FindVisibleEvents() { - const visibleMarkers = timeline1.FindVisibleEvents(); + const visibleMarkers = timeline1.VisibleEvents; LogInfo(""); LogInfo(JSON.stringify(visibleMarkers));