gantt-canvas-chart 1.5.3 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.5.3
2
+ * gantt-canvas-chart v1.5.4
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -197,6 +197,7 @@ class GanttChart {
197
197
  offsetTop: 0,
198
198
  offsetLeft: 0,
199
199
  scrollEdgeThresholds: 10,
200
+ xGap: 0,
200
201
  enabledLoadMore: [],
201
202
  viewFactors: { Day: 80, Week: 20, Month: 15, Year: 6 },
202
203
  planBorderColor: "#C1EFCF",
@@ -553,8 +554,17 @@ class GanttChart {
553
554
  canvas.style.height = `${height}px`;
554
555
  const ctx = canvas.getContext("2d");
555
556
  ctx.scale(this.devicePixelRatio, this.devicePixelRatio);
557
+ ctx.textBaseline = "middle";
558
+ ctx.imageSmoothingEnabled = false;
556
559
  return ctx;
557
560
  }
561
+ /**
562
+ * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
563
+ * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
564
+ */
565
+ snap(val) {
566
+ return Math.floor(val) + 0.5;
567
+ }
558
568
  calculateAllTaskPositions() {
559
569
  this.taskPositions.clear();
560
570
  let visibleRowIndex = 0;
@@ -640,9 +650,9 @@ class GanttChart {
640
650
  const h = this.config.headerHeight;
641
651
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
642
652
  ctx.save();
643
- ctx.translate(-this.scrollLeft, 0);
653
+ ctx.translate(-Math.round(this.scrollLeft), 0);
644
654
  ctx.fillStyle = this.config.headerBgColor;
645
- ctx.fillRect(this.scrollLeft, 0, this.viewportWidth, h);
655
+ ctx.fillRect(Math.round(this.scrollLeft), 0, this.viewportWidth, h);
646
656
  ctx.textBaseline = "middle";
647
657
  ctx.textRendering = "optimizeLegibility";
648
658
  let currentDate = new Date(this.visibleDateRange.start);
@@ -720,20 +730,20 @@ class GanttChart {
720
730
  }
721
731
  const groupedBlocks = this.groupConsecutiveBlocks(visibleBlocks);
722
732
  ctx.fillStyle = "#333";
723
- ctx.font = 'bold 14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
733
+ ctx.font = "bold 14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
724
734
  ctx.textAlign = "left";
725
735
  groupedBlocks.forEach((group) => {
726
736
  const visibleStart = Math.max(group.startX, this.scrollLeft);
727
737
  const visibleEnd = Math.min(group.endX, this.scrollLeft + this.viewportWidth);
728
738
  if (visibleEnd > visibleStart) {
729
739
  ctx.fillStyle = this.config.headerBgColor;
730
- ctx.fillRect(visibleStart, 0, visibleEnd - visibleStart, h * 0.5);
740
+ ctx.fillRect(Math.round(visibleStart), 0, Math.round(visibleEnd - visibleStart), Math.round(h * 0.5));
731
741
  ctx.fillStyle = "#333";
732
- ctx.fillText(group.text, visibleStart + 5, group.yPos);
742
+ ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos));
733
743
  }
734
744
  });
735
745
  while (currentDateForLower <= this.visibleDateRange.end) {
736
- const x = this.dateToX(currentDateForLower);
746
+ const x = this.snap(this.dateToX(currentDateForLower));
737
747
  let lowerText = "";
738
748
  let nextDate;
739
749
  switch (this.config.viewMode) {
@@ -768,7 +778,7 @@ class GanttChart {
768
778
  }
769
779
  const unitWidth = this.dateToX(nextDate) - x;
770
780
  ctx.fillStyle = "#000412";
771
- ctx.font = '14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
781
+ ctx.font = "14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
772
782
  ctx.textAlign = "center";
773
783
  ctx.fillText(lowerText, Math.round(x + unitWidth / 2), Math.round(h * 0.7));
774
784
  ctx.beginPath();
@@ -782,11 +792,6 @@ class GanttChart {
782
792
  currentDateForLower = nextDate;
783
793
  }
784
794
  }
785
- ctx.beginPath();
786
- ctx.moveTo(this.scrollLeft, h - 0.5);
787
- ctx.lineTo(this.scrollLeft + this.viewportWidth, h - 0.5);
788
- ctx.strokeStyle = "#e0e0e0";
789
- ctx.stroke();
790
795
  ctx.restore();
791
796
  }
792
797
  // Helper method to group consecutive blocks with same text
@@ -809,7 +814,7 @@ class GanttChart {
809
814
  const ctx = this.mainCtx;
810
815
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
811
816
  ctx.save();
812
- ctx.translate(-this.scrollLeft, -this.scrollTop);
817
+ ctx.translate(-Math.round(this.scrollLeft), -Math.round(this.scrollTop));
813
818
  const { start: startDate, end: endDate } = this.visibleDateRange;
814
819
  this.drawGrid(ctx, startDate, endDate);
815
820
  this.drawToday(ctx);
@@ -898,7 +903,7 @@ class GanttChart {
898
903
  }
899
904
  drawAllTasks(ctx) {
900
905
  ctx.textBaseline = "middle";
901
- ctx.font = '12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
906
+ ctx.font = "12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
902
907
  ctx.textRendering = "optimizeSpeed";
903
908
  ctx.imageSmoothingEnabled = false;
904
909
  let visibleRowIndex = 0;
@@ -938,8 +943,9 @@ class GanttChart {
938
943
  for (let i = 0; i <= visibleRowCount; i++) {
939
944
  const y = i * this.config.rowHeight;
940
945
  if (y < this.scrollTop || y > this.scrollTop + this.viewportHeight) continue;
941
- ctx.moveTo(this.scrollLeft, y);
942
- ctx.lineTo(this.scrollLeft + this.viewportWidth, y);
946
+ const sharpY = this.snap(y);
947
+ ctx.moveTo(this.scrollLeft, sharpY);
948
+ ctx.lineTo(this.scrollLeft + this.viewportWidth, sharpY);
943
949
  }
944
950
  }
945
951
  if (this.config.showColLines) {
@@ -980,7 +986,7 @@ class GanttChart {
980
986
  currentDate = nextDate;
981
987
  }
982
988
  while (currentDate <= endDate) {
983
- const x = this.dateToX(currentDate);
989
+ const x = this.snap(this.dateToX(currentDate));
984
990
  ctx.moveTo(x, this.scrollTop);
985
991
  ctx.lineTo(x, this.scrollTop + this.viewportHeight);
986
992
  switch (this.config.viewMode) {
@@ -1029,14 +1035,14 @@ class GanttChart {
1029
1035
  const [offsetX_actual, percent_actual] = this.config.viewMode === "Day" && task.actualOffsetPercent ? task.actualOffsetPercent : [0, 1];
1030
1036
  if (this.config.showActual && pos.x_actual_start) {
1031
1037
  ctx.fillStyle = task.actualBgColor ? task.actualBgColor : this.config.actualBgColor;
1032
- ctx.fillRect(pos.offset_x_actual_start, Math.round(taskY + 2), Math.round(pos.x_actual_width * percent_actual), Math.round(taskHeight - 2));
1038
+ ctx.fillRect(pos.offset_x_actual_start + this.config.xGap, Math.round(taskY + 2), Math.round(pos.x_actual_width * percent_actual - this.config.xGap), Math.round(taskHeight - 2));
1033
1039
  }
1034
1040
  if (this.config.showPlan && pos.x_plan_start && pos.x_plan_end) {
1035
1041
  ctx.strokeStyle = task.planBorderColor ? task.planBorderColor : this.config.planBorderColor;
1036
1042
  ctx.lineWidth = 4;
1037
1043
  ctx.beginPath();
1038
- ctx.moveTo(pos.offset_x_plan_start + offset / 2, taskY);
1039
- ctx.lineTo(pos.offset_x_plan_end - offset / 2, taskY);
1044
+ ctx.moveTo(pos.offset_x_plan_start + offset / 2 + this.config.xGap, taskY);
1045
+ ctx.lineTo(pos.offset_x_plan_end - offset / 2 - this.config.xGap, taskY);
1040
1046
  ctx.stroke();
1041
1047
  }
1042
1048
  ctx.fillStyle = "#000";
package/dist/index.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.5.3
2
+ * gantt-canvas-chart v1.5.4
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -10,7 +10,8 @@
10
10
  overflow: hidden;
11
11
  position: relative;
12
12
  background: white;
13
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
13
+ font-family: Roboto, PingFang SC, Noto Sans SC, Microsoft YaHei UI, Microsoft YaHei, Segoe UI, Helvetica Neue,
14
+ Helvetica, Arial, sans-serif;
14
15
  }
15
16
 
16
17
  .__gantt-chart-container {
@@ -19,7 +20,8 @@
19
20
  position: relative;
20
21
  background: #ffffff;
21
22
  height: 100%;
22
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
23
+ font-family: Roboto, PingFang SC, Noto Sans SC, Microsoft YaHei UI, Microsoft YaHei, Segoe UI, Helvetica Neue,
24
+ Helvetica, Arial, sans-serif;
23
25
  }
24
26
 
25
27
  .__gantt-scroll-dummy {
package/dist/index.d.ts CHANGED
@@ -118,6 +118,11 @@ export declare class GanttChart {
118
118
  private updateVirtualRanges;
119
119
  private updateDimensions;
120
120
  private setupCanvas;
121
+ /**
122
+ * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
123
+ * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
124
+ */
125
+ snap(val: number): number;
121
126
  private calculateAllTaskPositions;
122
127
  private getIterationStartDate;
123
128
  render(scrolling?: boolean): void;
@@ -178,6 +183,7 @@ export declare interface GanttConfig {
178
183
  offsetTop?: number;
179
184
  offsetLeft?: number;
180
185
  scrollEdgeThresholds?: number;
186
+ xGap?: number;
181
187
  enabledLoadMore?: [LoadMoreDirection?, LoadMoreDirection?, LoadMoreDirection?];
182
188
  viewFactors?: {
183
189
  Day: number;
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.5.3
2
+ * gantt-canvas-chart v1.5.4
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -195,6 +195,7 @@ class GanttChart {
195
195
  offsetTop: 0,
196
196
  offsetLeft: 0,
197
197
  scrollEdgeThresholds: 10,
198
+ xGap: 0,
198
199
  enabledLoadMore: [],
199
200
  viewFactors: { Day: 80, Week: 20, Month: 15, Year: 6 },
200
201
  planBorderColor: "#C1EFCF",
@@ -551,8 +552,17 @@ class GanttChart {
551
552
  canvas.style.height = `${height}px`;
552
553
  const ctx = canvas.getContext("2d");
553
554
  ctx.scale(this.devicePixelRatio, this.devicePixelRatio);
555
+ ctx.textBaseline = "middle";
556
+ ctx.imageSmoothingEnabled = false;
554
557
  return ctx;
555
558
  }
559
+ /**
560
+ * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
561
+ * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
562
+ */
563
+ snap(val) {
564
+ return Math.floor(val) + 0.5;
565
+ }
556
566
  calculateAllTaskPositions() {
557
567
  this.taskPositions.clear();
558
568
  let visibleRowIndex = 0;
@@ -638,9 +648,9 @@ class GanttChart {
638
648
  const h = this.config.headerHeight;
639
649
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
640
650
  ctx.save();
641
- ctx.translate(-this.scrollLeft, 0);
651
+ ctx.translate(-Math.round(this.scrollLeft), 0);
642
652
  ctx.fillStyle = this.config.headerBgColor;
643
- ctx.fillRect(this.scrollLeft, 0, this.viewportWidth, h);
653
+ ctx.fillRect(Math.round(this.scrollLeft), 0, this.viewportWidth, h);
644
654
  ctx.textBaseline = "middle";
645
655
  ctx.textRendering = "optimizeLegibility";
646
656
  let currentDate = new Date(this.visibleDateRange.start);
@@ -718,20 +728,20 @@ class GanttChart {
718
728
  }
719
729
  const groupedBlocks = this.groupConsecutiveBlocks(visibleBlocks);
720
730
  ctx.fillStyle = "#333";
721
- ctx.font = 'bold 14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
731
+ ctx.font = "bold 14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
722
732
  ctx.textAlign = "left";
723
733
  groupedBlocks.forEach((group) => {
724
734
  const visibleStart = Math.max(group.startX, this.scrollLeft);
725
735
  const visibleEnd = Math.min(group.endX, this.scrollLeft + this.viewportWidth);
726
736
  if (visibleEnd > visibleStart) {
727
737
  ctx.fillStyle = this.config.headerBgColor;
728
- ctx.fillRect(visibleStart, 0, visibleEnd - visibleStart, h * 0.5);
738
+ ctx.fillRect(Math.round(visibleStart), 0, Math.round(visibleEnd - visibleStart), Math.round(h * 0.5));
729
739
  ctx.fillStyle = "#333";
730
- ctx.fillText(group.text, visibleStart + 5, group.yPos);
740
+ ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos));
731
741
  }
732
742
  });
733
743
  while (currentDateForLower <= this.visibleDateRange.end) {
734
- const x = this.dateToX(currentDateForLower);
744
+ const x = this.snap(this.dateToX(currentDateForLower));
735
745
  let lowerText = "";
736
746
  let nextDate;
737
747
  switch (this.config.viewMode) {
@@ -766,7 +776,7 @@ class GanttChart {
766
776
  }
767
777
  const unitWidth = this.dateToX(nextDate) - x;
768
778
  ctx.fillStyle = "#000412";
769
- ctx.font = '14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
779
+ ctx.font = "14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
770
780
  ctx.textAlign = "center";
771
781
  ctx.fillText(lowerText, Math.round(x + unitWidth / 2), Math.round(h * 0.7));
772
782
  ctx.beginPath();
@@ -780,11 +790,6 @@ class GanttChart {
780
790
  currentDateForLower = nextDate;
781
791
  }
782
792
  }
783
- ctx.beginPath();
784
- ctx.moveTo(this.scrollLeft, h - 0.5);
785
- ctx.lineTo(this.scrollLeft + this.viewportWidth, h - 0.5);
786
- ctx.strokeStyle = "#e0e0e0";
787
- ctx.stroke();
788
793
  ctx.restore();
789
794
  }
790
795
  // Helper method to group consecutive blocks with same text
@@ -807,7 +812,7 @@ class GanttChart {
807
812
  const ctx = this.mainCtx;
808
813
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
809
814
  ctx.save();
810
- ctx.translate(-this.scrollLeft, -this.scrollTop);
815
+ ctx.translate(-Math.round(this.scrollLeft), -Math.round(this.scrollTop));
811
816
  const { start: startDate, end: endDate } = this.visibleDateRange;
812
817
  this.drawGrid(ctx, startDate, endDate);
813
818
  this.drawToday(ctx);
@@ -896,7 +901,7 @@ class GanttChart {
896
901
  }
897
902
  drawAllTasks(ctx) {
898
903
  ctx.textBaseline = "middle";
899
- ctx.font = '12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
904
+ ctx.font = "12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
900
905
  ctx.textRendering = "optimizeSpeed";
901
906
  ctx.imageSmoothingEnabled = false;
902
907
  let visibleRowIndex = 0;
@@ -936,8 +941,9 @@ class GanttChart {
936
941
  for (let i = 0; i <= visibleRowCount; i++) {
937
942
  const y = i * this.config.rowHeight;
938
943
  if (y < this.scrollTop || y > this.scrollTop + this.viewportHeight) continue;
939
- ctx.moveTo(this.scrollLeft, y);
940
- ctx.lineTo(this.scrollLeft + this.viewportWidth, y);
944
+ const sharpY = this.snap(y);
945
+ ctx.moveTo(this.scrollLeft, sharpY);
946
+ ctx.lineTo(this.scrollLeft + this.viewportWidth, sharpY);
941
947
  }
942
948
  }
943
949
  if (this.config.showColLines) {
@@ -978,7 +984,7 @@ class GanttChart {
978
984
  currentDate = nextDate;
979
985
  }
980
986
  while (currentDate <= endDate) {
981
- const x = this.dateToX(currentDate);
987
+ const x = this.snap(this.dateToX(currentDate));
982
988
  ctx.moveTo(x, this.scrollTop);
983
989
  ctx.lineTo(x, this.scrollTop + this.viewportHeight);
984
990
  switch (this.config.viewMode) {
@@ -1027,14 +1033,14 @@ class GanttChart {
1027
1033
  const [offsetX_actual, percent_actual] = this.config.viewMode === "Day" && task.actualOffsetPercent ? task.actualOffsetPercent : [0, 1];
1028
1034
  if (this.config.showActual && pos.x_actual_start) {
1029
1035
  ctx.fillStyle = task.actualBgColor ? task.actualBgColor : this.config.actualBgColor;
1030
- ctx.fillRect(pos.offset_x_actual_start, Math.round(taskY + 2), Math.round(pos.x_actual_width * percent_actual), Math.round(taskHeight - 2));
1036
+ ctx.fillRect(pos.offset_x_actual_start + this.config.xGap, Math.round(taskY + 2), Math.round(pos.x_actual_width * percent_actual - this.config.xGap), Math.round(taskHeight - 2));
1031
1037
  }
1032
1038
  if (this.config.showPlan && pos.x_plan_start && pos.x_plan_end) {
1033
1039
  ctx.strokeStyle = task.planBorderColor ? task.planBorderColor : this.config.planBorderColor;
1034
1040
  ctx.lineWidth = 4;
1035
1041
  ctx.beginPath();
1036
- ctx.moveTo(pos.offset_x_plan_start + offset / 2, taskY);
1037
- ctx.lineTo(pos.offset_x_plan_end - offset / 2, taskY);
1042
+ ctx.moveTo(pos.offset_x_plan_start + offset / 2 + this.config.xGap, taskY);
1043
+ ctx.lineTo(pos.offset_x_plan_end - offset / 2 - this.config.xGap, taskY);
1038
1044
  ctx.stroke();
1039
1045
  }
1040
1046
  ctx.fillStyle = "#000";
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.5.3
2
+ * gantt-canvas-chart v1.5.4
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -199,6 +199,7 @@
199
199
  offsetTop: 0,
200
200
  offsetLeft: 0,
201
201
  scrollEdgeThresholds: 10,
202
+ xGap: 0,
202
203
  enabledLoadMore: [],
203
204
  viewFactors: { Day: 80, Week: 20, Month: 15, Year: 6 },
204
205
  planBorderColor: "#C1EFCF",
@@ -555,8 +556,17 @@
555
556
  canvas.style.height = `${height}px`;
556
557
  const ctx = canvas.getContext("2d");
557
558
  ctx.scale(this.devicePixelRatio, this.devicePixelRatio);
559
+ ctx.textBaseline = "middle";
560
+ ctx.imageSmoothingEnabled = false;
558
561
  return ctx;
559
562
  }
563
+ /**
564
+ * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
565
+ * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
566
+ */
567
+ snap(val) {
568
+ return Math.floor(val) + 0.5;
569
+ }
560
570
  calculateAllTaskPositions() {
561
571
  this.taskPositions.clear();
562
572
  let visibleRowIndex = 0;
@@ -642,9 +652,9 @@
642
652
  const h = this.config.headerHeight;
643
653
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
644
654
  ctx.save();
645
- ctx.translate(-this.scrollLeft, 0);
655
+ ctx.translate(-Math.round(this.scrollLeft), 0);
646
656
  ctx.fillStyle = this.config.headerBgColor;
647
- ctx.fillRect(this.scrollLeft, 0, this.viewportWidth, h);
657
+ ctx.fillRect(Math.round(this.scrollLeft), 0, this.viewportWidth, h);
648
658
  ctx.textBaseline = "middle";
649
659
  ctx.textRendering = "optimizeLegibility";
650
660
  let currentDate = new Date(this.visibleDateRange.start);
@@ -722,20 +732,20 @@
722
732
  }
723
733
  const groupedBlocks = this.groupConsecutiveBlocks(visibleBlocks);
724
734
  ctx.fillStyle = "#333";
725
- ctx.font = 'bold 14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
735
+ ctx.font = "bold 14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
726
736
  ctx.textAlign = "left";
727
737
  groupedBlocks.forEach((group) => {
728
738
  const visibleStart = Math.max(group.startX, this.scrollLeft);
729
739
  const visibleEnd = Math.min(group.endX, this.scrollLeft + this.viewportWidth);
730
740
  if (visibleEnd > visibleStart) {
731
741
  ctx.fillStyle = this.config.headerBgColor;
732
- ctx.fillRect(visibleStart, 0, visibleEnd - visibleStart, h * 0.5);
742
+ ctx.fillRect(Math.round(visibleStart), 0, Math.round(visibleEnd - visibleStart), Math.round(h * 0.5));
733
743
  ctx.fillStyle = "#333";
734
- ctx.fillText(group.text, visibleStart + 5, group.yPos);
744
+ ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos));
735
745
  }
736
746
  });
737
747
  while (currentDateForLower <= this.visibleDateRange.end) {
738
- const x = this.dateToX(currentDateForLower);
748
+ const x = this.snap(this.dateToX(currentDateForLower));
739
749
  let lowerText = "";
740
750
  let nextDate;
741
751
  switch (this.config.viewMode) {
@@ -770,7 +780,7 @@
770
780
  }
771
781
  const unitWidth = this.dateToX(nextDate) - x;
772
782
  ctx.fillStyle = "#000412";
773
- ctx.font = '14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
783
+ ctx.font = "14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
774
784
  ctx.textAlign = "center";
775
785
  ctx.fillText(lowerText, Math.round(x + unitWidth / 2), Math.round(h * 0.7));
776
786
  ctx.beginPath();
@@ -784,11 +794,6 @@
784
794
  currentDateForLower = nextDate;
785
795
  }
786
796
  }
787
- ctx.beginPath();
788
- ctx.moveTo(this.scrollLeft, h - 0.5);
789
- ctx.lineTo(this.scrollLeft + this.viewportWidth, h - 0.5);
790
- ctx.strokeStyle = "#e0e0e0";
791
- ctx.stroke();
792
797
  ctx.restore();
793
798
  }
794
799
  // Helper method to group consecutive blocks with same text
@@ -811,7 +816,7 @@
811
816
  const ctx = this.mainCtx;
812
817
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
813
818
  ctx.save();
814
- ctx.translate(-this.scrollLeft, -this.scrollTop);
819
+ ctx.translate(-Math.round(this.scrollLeft), -Math.round(this.scrollTop));
815
820
  const { start: startDate, end: endDate } = this.visibleDateRange;
816
821
  this.drawGrid(ctx, startDate, endDate);
817
822
  this.drawToday(ctx);
@@ -900,7 +905,7 @@
900
905
  }
901
906
  drawAllTasks(ctx) {
902
907
  ctx.textBaseline = "middle";
903
- ctx.font = '12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
908
+ ctx.font = "12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
904
909
  ctx.textRendering = "optimizeSpeed";
905
910
  ctx.imageSmoothingEnabled = false;
906
911
  let visibleRowIndex = 0;
@@ -940,8 +945,9 @@
940
945
  for (let i = 0; i <= visibleRowCount; i++) {
941
946
  const y = i * this.config.rowHeight;
942
947
  if (y < this.scrollTop || y > this.scrollTop + this.viewportHeight) continue;
943
- ctx.moveTo(this.scrollLeft, y);
944
- ctx.lineTo(this.scrollLeft + this.viewportWidth, y);
948
+ const sharpY = this.snap(y);
949
+ ctx.moveTo(this.scrollLeft, sharpY);
950
+ ctx.lineTo(this.scrollLeft + this.viewportWidth, sharpY);
945
951
  }
946
952
  }
947
953
  if (this.config.showColLines) {
@@ -982,7 +988,7 @@
982
988
  currentDate = nextDate;
983
989
  }
984
990
  while (currentDate <= endDate) {
985
- const x = this.dateToX(currentDate);
991
+ const x = this.snap(this.dateToX(currentDate));
986
992
  ctx.moveTo(x, this.scrollTop);
987
993
  ctx.lineTo(x, this.scrollTop + this.viewportHeight);
988
994
  switch (this.config.viewMode) {
@@ -1031,14 +1037,14 @@
1031
1037
  const [offsetX_actual, percent_actual] = this.config.viewMode === "Day" && task.actualOffsetPercent ? task.actualOffsetPercent : [0, 1];
1032
1038
  if (this.config.showActual && pos.x_actual_start) {
1033
1039
  ctx.fillStyle = task.actualBgColor ? task.actualBgColor : this.config.actualBgColor;
1034
- ctx.fillRect(pos.offset_x_actual_start, Math.round(taskY + 2), Math.round(pos.x_actual_width * percent_actual), Math.round(taskHeight - 2));
1040
+ ctx.fillRect(pos.offset_x_actual_start + this.config.xGap, Math.round(taskY + 2), Math.round(pos.x_actual_width * percent_actual - this.config.xGap), Math.round(taskHeight - 2));
1035
1041
  }
1036
1042
  if (this.config.showPlan && pos.x_plan_start && pos.x_plan_end) {
1037
1043
  ctx.strokeStyle = task.planBorderColor ? task.planBorderColor : this.config.planBorderColor;
1038
1044
  ctx.lineWidth = 4;
1039
1045
  ctx.beginPath();
1040
- ctx.moveTo(pos.offset_x_plan_start + offset / 2, taskY);
1041
- ctx.lineTo(pos.offset_x_plan_end - offset / 2, taskY);
1046
+ ctx.moveTo(pos.offset_x_plan_start + offset / 2 + this.config.xGap, taskY);
1047
+ ctx.lineTo(pos.offset_x_plan_end - offset / 2 - this.config.xGap, taskY);
1042
1048
  ctx.stroke();
1043
1049
  }
1044
1050
  ctx.fillStyle = "#000";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gantt-canvas-chart",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "High performance Gantt chart component based on Canvas, can be applied to any framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.es.js",