”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 构建一个 ActivityRenderer

构建一个 ActivityRenderer

发布于2024-08-05
浏览:723

甘特图活动渲染器是 ScheduleJS 查看器的主要渲染器。本文将讨论它的构建方式以及该活动渲染器的特殊性。

如何构建自定义渲染器类

构建渲染器类的第一步是通过扩展高阶框架类来继承属性和方法。

我们只想通过任务的开始和结束时间维度来表示任务。用于执行此操作的 ScheduleJS 基本渲染器类是 ActivityBarRenderer 类。

我们需要向 ActivityBarRenderer 类提供自定义类型参数,以便我们的自定义 RowActivity 类提供的属性和方法可以访问使用基类 API。

让我们创建 ScheduleJsViewerTaskActivityRenderer 类来绘制每个 ScheduleJsViewerTaskActivity 各自的 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 { }

照原样,渲染器已经可以注册为使用 ActivityBarRenderer 的默认行为来绘制我们的活动。现在让我们深入探讨如何自定义它。

基础架构

在 ScheduleJS 中,ActivityRenderer 是我们使用 Graphics API 以编程方式注册的类,用于在其 Row 上绘制特定的 Activity。为了组织我们的ScheduleJsViewerTaskActivityRenderer,我们将其代码分成三个部分:

  • 属性将保存允许我们更改特定绘图过程的行为的变量。
  • 构造函数将让我们定义渲染器的默认状态。
  • 绘图方法将保存在画布上绘制我们的活动的所有指令。

属性

属性是将在整个渲染器中重用的常量。按原样,这些属性只能在渲染器代码中直接编辑。我们可以想象一个特定的屏幕,用户可以在其中直接在 UI 中修改这些设置。

// 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;

构造函数

构造函数与我们的渲染器生命周期方法紧密耦合。在 ScheduleJS 查看器中,我们决定在用户​​切换屏幕时实例化渲染器以定义特殊性,并在实现此渲染器的每个选项卡中重用我们的代码。这意味着每次用户选择具有此渲染器的屏幕时都会运行构造函数。

// Constructor

// The renderer requires the graphics and the current tab variable
constructor(graphics: GraphicsBase,
            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;
  }

}

setFill、setStroke、setFillHover、setStrokeHover、setLineWidth 和 setBarHeight 是继承的,用于更改 ActivityBarRenderer 类的默认渲染特性。

该渲染器的默认功能如下:

  • 悬停活动时的自定义颜色
  • 黑线描边(用于活动边框)
  • 笔划线粗细为0.5像素
  • 活动栏高度为 8 像素
  • 条件填充颜色: 在 WBS 选项卡中,蓝色代表儿童,棕色代表父母 其他选项卡中的儿童为绿色,家长为灰色

绘画

框架会自动调用drawActivity方法来在画布上渲染我们的活动。它的所有参数都是动态填充的,允许您对活动的当前状态做出实时反应。

// Main drawing method

drawActivity(activityRef: ActivityRef,
             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);
  }

绘图将以这种方式发生:

  • 使用 ActivityRef API 获取有关当前 ActivityRow 的信息
  • 使用我们的 _setActivityColor 方法动态设置颜色
  • 使用我们的 _drawActivityText 方法绘制活动文本
  • 使用两种方法绘制活动本身: _drawParentActivity 方法绘制父母 super.drawActivity默认的ActivityBarRenderer方法来绘制children

自定义活动绘制方法

让我们仔细看看如何通过使用 _drawParentActivity 方法设计自己的方法来自由绘制您的 Activity。

// Draw the parent activity

private _drawParentActivity(activityRef: ActivityRef,
                            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);
  }

这里我们直接使用HTMLCanvas API通过设置CanvasRenderingContex2D来定义我们的绘制策略。此方法中完成的唯一与框架相关的操作是为当前父级 Activity 创建一些新的 ActivityBounds

框架使用ActivityBounds在底层创建一个地图来注册屏幕上的所有活动。该地图通过提供类似元素的逻辑来帮助开发人员构建基于准确信息的高级用户体验,同时利用 HTMLCanvas API

诸如 _drawParentActivityStartTriangle 之类的绘制元素方法依赖于 CanvasRenderingContext2D API 在像素级别进行绘制。

// 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();
}

最后结果

要注册全新的渲染器,请使用graphics.setActivityRenderer方法:

// Register the renderer

graphics.setActivityRenderer(ScheduleJsViewerTaskActivity, GanttLayout, new ScheduleJsViewerTaskActivityRenderer(graphics, currentRibbonMenuTab));

brand-new renderer

查看最终结果的视频可以去看:Building an ActivityRenderer

版本声明 本文转载于:https://dev.to/lenormor/building-an-activityrenderer-3o0?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • TypeScript 如何使 JavaScript 在大型项目中更加可靠。
    TypeScript 如何使 JavaScript 在大型项目中更加可靠。
    介绍 JavaScript 广泛应用于 Web 开发,现在也被应用于不同行业的大型项目中。然而,随着这些项目的增长,管理 JavaScript 代码变得更加困难。数据类型不匹配、运行时意外错误以及代码不清晰等问题可能会导致查找和修复错误变得困难。 这就是TypeScript介入的地...
    编程 发布于2024-11-05
  • 如何使用PHP的password_verify函数安全地验证用户密码?
    如何使用PHP的password_verify函数安全地验证用户密码?
    使用 PHP 解密加密密码许多应用程序使用密码哈希等加密算法安全地存储用户密码。然而,在验证登录尝试时,将输入密码与加密的存储版本进行比较非常重要。加密问题password_hash 使用 Bcrypt,一种一元加密算法方式哈希算法,意味着加密的密码无法逆转或解密。这是一项安全功能,可确保即使数据库...
    编程 发布于2024-11-05
  • 学习 Vue 部分 构建天气应用程序
    学习 Vue 部分 构建天气应用程序
    深入研究 Vue.js 就像在 DIY 工具包中发现了一个新的最喜欢的工具——直观、灵活,而且功能强大得惊人。我接触 Vue 的第一个副业项目是一个天气应用程序,它教会了我很多关于框架功能以及一般 Web 开发的知识。这是我到目前为止所学到的。 1. Vue 入门:简单与强大 Vue...
    编程 发布于2024-11-05
  • NFT 预览卡组件
    NFT 预览卡组件
    ?刚刚完成了我的最新项目:使用 HTML 和 CSS 的“NFT 预览卡组件”! ?查看并探索 GitHub 上的代码。欢迎反馈! ? GitHub:[https://github.com/khanimran17/NFT-preview-card-component] ?现场演示:[https://...
    编程 发布于2024-11-05
  • Android 应用程序如何连接到 Microsoft SQL Server 2008?
    Android 应用程序如何连接到 Microsoft SQL Server 2008?
    将 Android 应用程序连接到 Microsoft SQL Server 2008Android 应用程序可以无缝连接到中央数据库服务器,包括 Microsoft SQL Server 2008。这种连接允许开发人员从其移动应用程序访问和管理存储在远程服务器上的数据。连接方法虽然提供的示例代码侧...
    编程 发布于2024-11-05
  • 以下是一些基于问题的标题选项,重点关注核心问题:

* C++ std::可选:为什么没有对引用类型进行专门化? (直接、切题)
* C++ std::option 中的引用类型
    以下是一些基于问题的标题选项,重点关注核心问题: * C++ std::可选:为什么没有对引用类型进行专门化? (直接、切题) * C++ std::option 中的引用类型
    C 中的可选:为什么没有对引用类型进行专门化?尽管在像 Boost 这样的库中存在对引用类型的专门化,C标准库的 std::Optional 不提供这样的功能。这一决定引发了对其理由和潜在替代机制的询问。遗漏背后的理由在讨论 n3406(可选提案)期间,有人提出了担忧关于包含可选参考文献。认识到这些...
    编程 发布于2024-11-05
  • 评估机器学习分类模型
    评估机器学习分类模型
    大纲 模型评估的目标是什么? 模型评估的目的是什么,有哪些 常见的评估程序? 分类准确率有什么用,它的作用是什么 限制? 混淆矩阵如何描述一个 分类器? 可以从混淆矩阵计算哪些指标? T模型评估的目标是回答问题; 不同型号如何选择? 评估机器学习的过程有助于...
    编程 发布于2024-11-05
  • 如何消除 Eval-Base64_Decode PHP 病毒并保护您的网站?
    如何消除 Eval-Base64_Decode PHP 病毒并保护您的网站?
    如何像 PHP 病毒文件一样删除 Eval-Base64_Decode采用 eval-base64_decode 技术的病毒,例如您的病毒已经描述过,可能会很麻烦。我们将帮助您了解该病毒的性质及其潜在漏洞,并提供有关如何消除该病毒的全面指南。了解病毒此特定病毒用 eval-base64_decode...
    编程 发布于2024-11-05
  • 如何在 Serp 中排名 4
    如何在 Serp 中排名 4
    搜索引擎排名页面 (SERP) 是网站争夺可见性和流量的地方。到 2024 年,在 Google 和其他搜索引擎上的高排名仍然对在线成功至关重要。然而,SEO(搜索引擎优化)多年来已经发生了变化,并将继续发展。如果您想知道如何在 2024 年提高 SERP 排名,这里有一个简单的指南可以帮助您了解最...
    编程 发布于2024-11-05
  • 如何使用多处理在 Python 进程之间共享锁
    如何使用多处理在 Python 进程之间共享锁
    在 Python 中的进程之间共享锁当尝试使用 pool.map() 来定位具有多个参数(包括 Lock() 对象)的函数时,它是对于解决子进程之间共享锁的问题至关重要。由于 pickling 限制,传统的 multiprocessing.Lock() 无法直接传递给 Pool 方法。选项 1:使用...
    编程 发布于2024-11-05
  • Type Script 中 readonly 和 const 的区别
    Type Script 中 readonly 和 const 的区别
    这两个功能的相似之处在于它们都是不可分配的。 能具体解释一下吗? 在这篇文章中,我将分享它们之间的区别。 const 防止重新分配给变量。 在这种情况下,hisName 是一个不能重新分配的变量。 const hisName = 'Michael Scofield' hisName ...
    编程 发布于2024-11-05
  • 如何使用 Range 函数在 Python 中复制 C/C++ 循环语法?
    如何使用 Range 函数在 Python 中复制 C/C++ 循环语法?
    Python 中的 for 循环:扩展 C/C 循环语法在编程中,for 循环是迭代序列的基本结构。虽然 C/C 采用特定的循环初始化语法,但 Python 提供了更简洁的方法。不过,Python 中有一种模仿 C/C 循环风格的方法。实现循环操作:for (int k = 1; k <= c...
    编程 发布于2024-11-05
  • TechEazy Consulting 推出全面的 Java、Spring Boot 和 AWS 培训计划并提供免费实习机会
    TechEazy Consulting 推出全面的 Java、Spring Boot 和 AWS 培训计划并提供免费实习机会
    TechEazy Consulting 很高兴地宣布推出我们的综合培训计划,专为希望转向后端开发使用Java、Spring Boot的初学者、新手和专业人士而设计,以及 AWS。 此4个月的带薪培训计划之后是2个月的无薪实习,您可以在实际项目中应用您的新技能——无需任何额外的培训费用。对于那些希望填...
    编程 发布于2024-11-05
  • Polyfills——填充物还是缝隙? (第 1 部分)
    Polyfills——填充物还是缝隙? (第 1 部分)
    几天前,我们在组织的 Teams 聊天中收到一条优先消息,内容如下:发现安全漏洞 - 检测到 Polyfill JavaScript - HIGH。 举个例子,我在一家大型银行公司工作,你必须知道,银行和安全漏洞就像主要的敌人。因此,我们开始深入研究这个问题,并在几个小时内解决了这个问题,我将在下面...
    编程 发布于2024-11-05
  • 移位运算符和按位简写赋值
    移位运算符和按位简写赋值
    1。移位运算符 :向右移动。 >>>:无符号右移(零填充)。 2.移位运算符的一般语法 value > num-bits:将值位向右移动,保留符号位。 value >>> num-bits:通过在左侧插入零将值位向右移动。 3.左移 每次左移都会导致该值的所有位向左移动一位。 右侧插入0位。 效果:...
    编程 发布于2024-11-05

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3