Schedulejs viewer gantt

Construire un ActivityRenderer

Le moteur de rendu de l'activité Gantt est le moteur de rendu principal du ScheduleJS Viewer. Cet article explique comment il est construit et quelles sont les spécificités de ce moteur de rendu d'activité.

November 23rd, 2022 - 6 minutes read

Comment construire une classe de rendu personnalisée

La première étape de la construction d'une classe de moteur de rendu consiste à hériter des attributs et des méthodes en étendant une classe de cadre d'ordre supérieur.

Nous voulons représenter les tâches uniquement par leurs heures de début et de fin. La classe de rendu de base de ScheduleJS pour ce faire est la classe ActivityBarRenderer classe.

Nous devons fournir les arguments de type personnalisé à la fonction ActivityBarRenderer de sorte que les attributs et les méthodes fournis par notre classe de Row et un projet Activity seront accessibles à l'aide de l'API de la classe de base.

Créons le ScheduleJsViewerTaskActivityRenderer pour dessiner chaque ScheduleJsViewerTaskActivity dans leur respectif ScheduleJsViewerTaskRow.

// Import the base ActivityBarRenderer class from ScheduleJS
import {ActivityBarRenderer} from "schedule";

// Import our custom Activity and Row types
import {ScheduleJsViewerTaskActivity} from "...";
import {ScheduleJsViewerTaskRow} from "...";

// Create our custom renderer by extending the ActivityBarRenderer class
export class ScheduleJsViewerTaskActivityRenderer extends ActivityBarRenderer<ScheduleJsViewerTaskActivity, ScheduleJsViewerTaskRow> { }

Tel quel, le moteur de rendu peut déjà être enregistré pour dessiner nos activités en utilisant le comportement par défaut de la méthode ActivityBarRenderer. Voyons maintenant comment le personnaliser.

L'architecture de base

Dans ScheduleJS, un ActivityRenderer est une classe que nous enregistrons par programme à l'aide de la fonction Graphics API pour dessiner un Activity on its Row. Pour organiser notre ScheduleJsViewerTaskActivityRenderer, nous allons séparer son code en trois sections :

  • Le attributes contiendra des variables qui nous permettront de modifier le comportement d'une procédure de dessin spécifique.  
  • Le constructor nous permet de définir un état par défaut pour le moteur de rendu.
  • Le drawing Les méthodes contiennent toutes les instructions pour dessiner nos activités sur la toile.

Attributs

Les attributs sont des constantes qui seront réutilisées dans le moteur de rendu. En l'état, ces propriétés ne seront modifiées que directement dans le code du moteur de rendu. Nous pouvons imaginer un écran spécifique où l'utilisateur pourrait modifier ces paramètres directement dans l'interface utilisateur.

// Attributes

// Pixels sizings
private readonly _parentActivityTrianglesWidthPx: number = 5;
private readonly _parentActivityTrianglesHeightPx: number = 8;
private readonly _defaultLineWidthPx: number = 0.5;

// Colors palette
private readonly _parentActivityColor: string = Color.GRAY.toCssString();
private readonly _strokeColor: string = Color.BLACK.toCssString();
private readonly _defaultActivityGreen: Color = Color.rgb(28, 187, 158);
private readonly _defaultActivityBlue: Color = Color.rgb(53, 152, 214);
private readonly _onHoverFillColor: string = Color.ORANGE.toCssString();

// Opacity ratio for baseline activities
private readonly _baselineOpacityRatio: number = 0.6;

Constructeur

Le constructeur est étroitement lié à notre méthode de cycle de vie du moteur de rendu. Dans le ScheduleJS Viewer, nous avons décidé d'instancier le moteur de rendu à chaque fois que l'utilisateur change d'écran afin de définir des spécificités et de réutiliser notre code dans chaque onglet qui implémente ce moteur de rendu. Cela signifie que la fonction constructeur est exécutée à chaque fois que l'utilisateur sélectionne un écran utilisant ce moteur de rendu.

// Constructor

// The renderer requires the graphics and the current tab variable
constructor(graphics: GraphicsBase<ScheduleJsViewerTaskRow>,
            private _currentRibbonMenuTab: ScheduleJsViewerRibbonMenuTabsEnum) {
            
  // The ActivityBarRenderer class requires the graphics and a name for the renderer
  super(graphics, ScheduleJsViewerRenderingConstants.taskActivityRendererName);
  
  // Default fill color when hovering an activity
  this.setFillHover(Color.web(this._onHoverFillColor));
  
  // Default stroke color when hovering an activity
  this.setStrokeHover(Color.BLACK);
  
  // Default stroke color
  this.setStroke(Color.BLACK);
  
  // Default thickness
  this.setLineWidth(this._defaultLineWidthPx);
  
  // Default bar height
  this.setBarHeight(8);
  
  // Default fill color based on current tab 
  switch (_currentRibbonMenuTab) {
    // Change color for the WBS tab
    case ScheduleJsViewerRibbonMenuTabsEnum.WBS:
      this._parentActivityColor = ScheduleJsViewerColors.brown;
      this.setFill(this._defaultActivityBlue);
      break;
    default:
      this._parentActivityColor = Color.GRAY.toCssString();
      this.setFill(this._defaultActivityGreen);
      break;
  }
  
}

Le setFill, setStroke, setFillHover, setStrokeHover, setLineWidth, et un projet setBarHeight sont héritées et utilisées pour modifier les caractéristiques de rendu par défaut de l'élément ActivityBarRenderer classe.

The default features of this renderer are the following:

  • Une couleur personnalisée lors du survol des activités
  • Un trait noir (pour les limites de l'activité)
  • Une épaisseur de trait de 0,5 pixel
  • Une barre d'activité d'une hauteur de 8 pixels
  • Une couleur de remplissage conditionnelle :
    • Bleu pour les enfants et marron pour les parents dans l'onglet OTP
    • Vert pour les enfants et gris pour les parents dans les autres onglets

Dessin

Le cadre appellera automatiquement la fonction drawActivity pour rendre nos activités sur le canevas. Tous ses paramètres sont remplis dynamiquement, ce qui vous permet de réagir en temps réel à l'état actuel de vos activités.

// Main drawing method

drawActivity(activityRef: ActivityRef<ScheduleJsViewerTaskActivity>,
             position: ViewPosition,
             ctx: CanvasRenderingContext2D,
             x: number,
             y: number,
             w: number,
             h: number,
             selected: boolean,    
             hover: boolean,
             highlighted: boolean,
             pressed: boolean     
            ): ActivityBounds {    // This method has to return ActivityBounds
             
    // True if current activity includes a comparison task
    const hasModifications = !!activityRef.getActivity().diffTask;
    
    // True if current row has children
    const isParent = activityRef.getRow().getChildren().length;
    
    // Set colors dynamically
    this._setActivityColor(activityRef, hasModifications);
    
    // Draw text
    this._drawActivityText(activityRef, ctx, x, y, w, h, hasModifications);
    
    // Run a custom method to draw parent activities or delegate to the default method
    return isParent
      ? this._drawParentActivity(activityRef, ctx, x, y, w, h, hover, hasModifications)
      : super.drawActivity(activityRef, position, ctx, x, y, w, h, selected, hover, highlighted, pressed);
  }

Le dessin se fera de cette manière :

  • Obtenir des informations sur le Activity et un projet Row en utilisant le ActivityRef API
  • Définir les couleurs de manière dynamique à l'aide de notre _setActivityColor method
  • Dessinez le texte de l'activité à l'aide de notre _drawActivityText method
  • Dessinez l'activité elle-même en vous basant sur deux méthodes :
    • Le _drawParentActivity pour dessiner les parents
    • Le super.drawActivity par default ActivityBarRenderer pour dessiner les enfants

Méthodes de dessin d'activités personnalisées

Regardons de plus près comment dessiner librement votre activité en concevant vos propres méthodes à l'aide de la méthode _drawParentActivity methode.

// Draw the parent activity

private _drawParentActivity(activityRef: ActivityRef<ScheduleJsViewerTaskActivity>,
                            ctx: CanvasRenderingContext2D,
                            x: number,
                            y: number,
                            w: number,
                            h: number,
                            hover: boolean,
                            hasModifications: boolean
                           ): ActivityBounds {

    // Set padding
    const topPadding = h / 3.5;
    const leftPadding = 1;
    
    // Set CanvasRenderingContext2D
    ctx.lineWidth = this._defaultLineWidthPx;
    if (hover) {
      ctx.fillStyle = this._onHoverFillColor;
      ctx.strokeStyle = ScheduleJsViewerColors.brown;
    } else if (hasModifications) {
      ctx.fillStyle = Color.web(this._parentActivityColor).withOpacity(this._baselineOpacityRatio).toCssString();
      ctx.strokeStyle = `rgba(0,0,0,${this._baselineOpacityRatio})`;
    } else {
      ctx.fillStyle = this._parentActivityColor;
      ctx.strokeStyle = this._strokeColor;
    }
    
    // Draw elements
    ScheduleJsViewerTaskActivityRenderer._drawParentActivityStartTriangle(ctx, x + leftPadding, y + topPadding, this._parentActivityTrianglesWidthPx, this._parentActivityTrianglesHeightPx);
    ScheduleJsViewerTaskActivityRenderer._drawParentActivityBody(ctx, x + leftPadding, y + topPadding, w, this._parentActivityTrianglesWidthPx, this._parentActivityTrianglesHeightPx);
    ScheduleJsViewerTaskActivityRenderer._drawParentActivityEndTriangle(ctx, x + leftPadding, y + topPadding, w, this._parentActivityTrianglesWidthPx, this._parentActivityTrianglesHeightPx);
    
    // Return positions to update where your activity should be responsive
    return new ActivityBounds(activityRef, x, y, w, h);
  }

Ici, nous utilisons directement la fonction HTMLCanvas API de définir notre stratégie de dessin en mettant en place le CanvasRenderingContex2D. La seule opération liée au cadre effectuée dans cette méthode consiste à créer de nouvelles ActivityBounds pour le parent actuel Activity.

Le cadre crée une carte à l'aide de ActivityBounds sous le capot pour enregistrer toutes les activités à l'écran. Cette carte aide le développeur en lui fournissant une logique semblable à celle d'un élément pour construire des expériences utilisateur avancées basées sur des informations précises tout en tirant parti de la performance de HTMLCanvas API.

Les méthodes des éléments de dessin comme _drawParentActivityStartTriangle s'appuyer sur la CanvasRenderingContext2D API pour dessiner au niveau du pixel.

// Draw the start triangle element of the parent activity

private static _drawParentActivityStartTriangle(ctx: CanvasRenderingContext2D,
                                                x: number,
                                                y: number,
                                                triangleWidth: number,
                                                triangleHeight: number): void {
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x , y + triangleHeight);
    ctx.lineTo(x + triangleWidth, y);
    ctx.lineTo(x, y);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
}

Résultat final

Pour enregistrer votre nouveau moteur de rendu, utilisez la fonction graphics.setActivityRenderer methode:

// Register the renderer

graphics.setActivityRenderer(ScheduleJsViewerTaskActivity, GanttLayout, new ScheduleJsViewerTaskActivityRenderer(graphics, currentRibbonMenuTab));
demo schedulejs viewer
The ScheduleJsViewerTaskActivityRenderer

Plus d'articles liés à ScheduleJS Viewer

Schedulejs viewer gantt

Cet article montre comment mettre en œuvre un rendu dynamique en fonction du niveau de zoom actuel des graphiques.

schedulejs viewer

Cet article vous montrera comment a été construite l'architecture de l'arbre de Gantt parent-enfant dans le ScheduleJS Viewer.

Schedulejs viewer gantt

Cet article montre comment le moteur de rendu de l'activité principale du ScheduleJS Viewer a été construit à l'aide d'exemples de code.

Schedulejs viewer gantt

Cet article présente les principales fonctionnalités du ScheduleJS Viewer. Une toute nouvelle visionneuse de projets basée sur le web.

S’abonner
Notification pour
guest
1 Commentaire
Le plus ancien
Le plus récent Le plus populaire
Commentaires
Show all comments
gasco
gasco
4 months

I particularly liked the way you explained how to build the renderer. The explanations on creating classes and managing drawing methods are very clear.”

1
0
We would love to to have your toughts on this. Please leave a comment below!x