Frontend Development 18 min read

Implementing a Custom Calendar Component with Konva.js

This article explains how to build a canvas‑based calendar widget using Konva, covering the design of date cells, task‑range rendering, drag‑and‑drop interaction, event handling, and the public API, and provides the full source code for reference.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a Custom Calendar Component with Konva.js

The article introduces a custom calendar component built on the HTML5 canvas using the Konva library, aiming to replicate the functionality seen in large‑scale enterprise products while remaining framework‑agnostic.

Feature Overview : the component draws a month view with weekday headers, displays dates from the previous and next months, supports task‑range visualization, and provides interactive features such as dragging to adjust task periods.

Technical Choices : Konva is used directly instead of wrapper libraries like react‑konva or vue‑konva, allowing the component to be integrated into any front‑end framework.

Core Configuration Interface :

export interface KonvaCalendarConfig {
  // mode: read | edit (affects whether dates can be dragged)
  mode: 'read' | 'edit';
  // container selector
  container: string;
  // optional initial date
  initDate?: Date | 'string';
}

Instantiation example:

const bootstrap = new CanvasKonvaCalendar({
  mode: 'edit',
  container: '.smart-table-root',
  initDate: new Date('2024-10-20')
});
bootstrap.setData([
  { startTime: '2024-09-30', endTime: '2024-09-30', fill: 'rgba(49,116,173,0.8)', description: '1', id: uuid() }
]);
bootstrap.on('ADDTASKRANGE', day => console.log('Add date', day));
bootstrap.on('CLICKRANGE', day => console.log('Select date', day));

Calendar Rendering draws the year/month header, weekday titles, and then iterates over previous‑month, current‑month, and next‑month dates, creating a Konva.Group for each cell with a background rectangle, day number, and optional Chinese lunar text.

Task‑Range Rendering splits tasks that span multiple weeks into weekly chunks, assigns each chunk a unique origin identifier, and calculates the rectangle width as dayCount * cellWidth . Overlap handling uses a sizeMap (Map<number, string[][]>) to allocate separate y‑offsets for intersecting tasks.

// Example of split task data
[
  {
    "startTime": "2024-10-01",
    "endTime": "2024-10-06",
    "fill": "rgba(0,0,255,0.3)",
    "description": "3",
    "origin": "12345",
    "id": "bb47c948-6ab0-4a47-8adf-13f8ed952643",
    "day": 19
  },
  ...
]

Interaction Logic includes mouse events:

mousedown – identifies the target task group and records drag offsets.

dragMousemove – clones the target group, reduces its opacity, and moves the clone according to pointer movement.

mouseup – determines the drop cell, updates the task’s start/end dates, redraws the task layer, and cleans up temporary objects.

private mousedown(): void {
  if (this.config.mode === 'read') return;
  const result = this.findGroup(this.featureLayer, '.task-progress-group');
  if (!result) return;
  const { group, pointer, rect } = result;
  this.recordsDragGroupRect = {
    differenceX: pointer.x - rect.x,
    differenceY: pointer.y - rect.y,
    sourceX: rect.x,
    sourceY: rect.y,
    targetGroup: group,
    startX: pointer.x,
    startY: pointer.y
  };
}

The component also exposes a simple public API for updating data, registering custom events, exporting the calendar as an image, and navigating between months.

setData(ranges: Range[]): this { this.taskRanges = ranges; this.draw(); return this; }
nextMonth(): void { this.month++; if (this.month > 11) { this.month = 0; this.year++; } this.featureLayer.removeChildren(); this.draw(); }

Finally, the author notes that the implementation may not follow best practices everywhere, invites community contributions after open‑sourcing the library, and provides links to the npm package and GitHub repository.

frontendJavaScriptCanvastask schedulingKonvaUI Componentcalendar
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.