Introduction

The graphics element uses the HTML canvas API. This is due to the complex nature of a Gantt chart and due to the large data volumes often observed inside of them. Directly rendering large quantities of activities into a canvas is much faster than constantly updating the DOM and reapplying CSS styling. ScheduleJS implements a pluggable renderer architecture where renderer instances can be mapped to Activity types.

The following code is an example of how to register a custom renderer for a given "Flight" activity type. Please note that the graphics view is capable of displaying activities in different layouts, hence the layout type must also be passed to the method.

Renderer Registration
const graphics = this.gantt.getGraphics();
const activityRenderer = new ActivityBarRenderer(graphics, "FlightRenderer");

// Change the Activity bar height.
activityRenderer.setBarHeight(5);

// Register the ActivityRenderer.
graphics.setActivityRenderer(Flight, GanttLayout, activityRenderer);

We usually also pass the graphics to the renderer at construction time.

The following methods on graphics are used for working with renderers:

MethodDescription
graphics.setActivityRenderer(Activity, Layout, Renderer)

Registers a new renderer for the given activity and layout type.

graphics.getActivityRenderer(Activity, Layout)
Returns a renderer for the given activity and layout type.

Drawing

Activity renderers have a single entry point for drawing, a method called draw(). This method should not be overridden. Once invoked it will call various protected methods to perform the actual drawing:

  • draw()  →  calls 
    • protected drawActivity() → calls
      1. protected drawBackground()
      2. protected drawBorder()

Derived classes are free to override any of the three protected methods to customize the activity appearance.

Most drawing methods have the same arguments:

Arguments
activityRef: ActivityRef<Activity>     The reference Activity for current drawing.
ctx: CanvasRenderingContext2D 		   The canvas context into which to draw.
x: number 			        		   The location of the start time of the activity.
y: number                     		   The y coordinate (0 when drawn on row or line location).
w: number 					           End time location minus start time location.
h: number 					           Row or line height.
selected: boolean 	       		       Is activity currently selected?
hover: boolean 				           Is mouse cursor currently hovering over it?
highlighted: boolean 		           Is activity currently blinking?
pressed: boolean  		               Is user currently pressing on it?

Default Renderers

The following table lists the various activity renderers that are provided by default.

Renderer ClassDescription
ActivityRenderer

The most basic renderer for activities. Draws a filled rectangle at the location of the activity. All default renderers are subclasses of this type.

ActivityBarRenderer

Draws a bar instead of filling the entire area. The height of the bar can be specified.

Also supports text in several locations inside and outside the bar.

ChartActivityRenderer

Draws a ChartActivity vertically depending on its chart value.

CompletableActivityRenderer
Subclass of the bar renderer. Draws a CompletableActivity as a bar with a section of its background filled with another color. The size of the section depends on the percentage complete value of the activity.

 Here is a Flight renderer example:

import {ActivityBarRenderer, ActivityBarRendererTextPosition, ActivityBounds, ActivityRef, Color, ViewPosition} from "schedule";

import {Flight, Aircraft} from "./aircraft.model";

export class FlightRenderer extends ActivityBarRenderer<Flight, Aircraft> {

  constructor(graphics) {
    super(graphics, "Flight activity renderer");
  }

  // Override the drawActivity method.
  protected drawActivity(activityRef: ActivityRef<Flight>, position: ViewPosition, ctx: CanvasRenderingContext2D,x: number, y: number, w: number, h: number, selected: boolean, hover: boolean, highlighted: boolean, pressed: boolean): ActivityBounds {
    const layerName = activityRef.getLayer().getName(); // Get rendered Activity layer name.

    // Use ActivityBarRenderer properties to style the Activity.
    this.setBarHeight(h * 0.75);
    this.setCornersRounded(true);
    this.setStroke(Color.BLACK);

    // Conditional box and text color depending on the Layer.
    if (layerName === "Cargo") {
      this.setFill(Color.CORNFLOWERBLUE);
      this.setTextFill(Color.WHITE);
    }
    else if (layerName === "Training") {
      this.setFill(Color.ALICEBLUE);
      this.setTextFill(Color.BLACK);
    }
    else if (layerName === "Charter") {
      this.setFill(Color.ANTIQUEWHITE);
      this.setTextFill(Color.BLACK);
    }

    // Draw the rectangle and store ActivityBounds.
    const bounds = super.drawActivity(activityRef, position, ctx, x, y, w, h, selected, hover, highlighted, pressed);

    // Then draw the text.
    ctx.font = "italic 11px Verdana";
    this.drawText(activityRef, layerName, ActivityBarRendererTextPosition.CENTER, ctx, x, y, w, h, selected, hover, highlighted, pressed);

    // This method has to return the ActivityBounds.
    return bounds;
  }

}

The text has to be drawn after the rectangle because we are using HTML canvas API to stack things on one another.

Activity Bounds

Every activity renderer is responsible for returning an instance of ActivityBounds after drawing the activity. These bounds are an essential piece for the framework and many operations will only work properly if these bounds are valid. They are being used for editing activities, for hit point detection, for laying out links, for context menus, and so on. The following table lists the attributes of the ActivityBounds class.

MethodsDescription
width

The width of the Activity in pixels.

height

The height of the Activity in pixels.

minX

The minimal x coordinate of the Activity in pixels based on Layout.

maxX

The maximal x coordinate of the Activity in pixels, based on Layout.

minY

 The minimal y coordinate of the Activity in pixels, based on Layout.

maxY

The maximal y coordinate of the Activity in pixels, based on Layout.

bounds.getActivity()

The Activity for which these are the bounds.

bounds.getActivityRef()

The ActivityReference referring to the activity.

bounds.getLayer()

The Model Layer on which the activity was drawn.

bounds.getLayout()

The Layout that was used when the activity was drawn.

bounds.getLineIndex()

The index of the line on which the activity is located (-1 if activity is on the row, not a line).

bounds.getRow()

The Row where the activity was drawn.

bounds.setLayout(Layout)

Sets the Layout to use when the activity will be drawn.

Properties

All renderers define several properties that can be used to customize their appearance. Many of these properties are dependent on the "pseudo state" of the activity: hover, pressed, selected, highlighted. To make it easier to lookup the right Color at the right time several convenience methods are available:

RendererMethodDescription
Renderer
this.getFill(selected, hover, highlighted, pressed)

Returns the Color to use for the activity background depending on pseudo states passed.

ActivityRenderer
this.getStroke(selected, hover, highlighted, pressed)

Returns the Color to use for the activity border depending on pseudo states passed.

ActivityBarRenderer
this.getTextFill(selected, hover, highlighted, pressed)
Returns the Color to use for text depending on pseudo states passed.