连接形状

您可以通过多种方式自定义连接显示方式,每种方式的难度各不相同。所有这些都需要创建自定义渲染程序

基本维度

具有不同维度的连接

您可以通过更改连接的宽度或高度来自定义连接,同时保持相同的基本形状。为此,您需要创建自定义常量提供程序组件,并替换某些常量。

不同的渲染程序定义和使用不同的常量,因此请查看超类的参考文档:

对于基本渲染程序,您可以替换 NOTCH_WIDTHNOTCH_HEIGHT(对于下一个和上一个连接)和 TAB_WIDTHTAB_HEIGHT(对于输入和输出连接)。

class CustomConstantProvider extends Blockly.blockRendering.ConstantProvider {
  constructor() {
    super();
    this.NOTCH_WIDTH = 20;
    this.NOTCH_HEIGHT = 10;
    this.TAB_HEIGHT = 8;
  }
}

基本形状

不同形状的连接

您可以通过替换连接的基本形状来自定义连接。基本形状具有高度、宽度和两条路径。

每条路径绘制的形状相同,但两端是相反的!

从两个方向绘制一个凹口

这是必要的,因为当抽屉式导航栏绘制块的轮廓时,它会在两个方向上绘制每种连接。例如,上一个连接从左到右绘制,而后续连接从右到左绘制。因此,您需要针对这两种情况提供路径。

块的绘制方向

您可以替换下一个和上一个连接的 makeNotch 方法,并为输入和输出连接替换 makePuzzleTab 方法。

class CustomConstantProvider extends Blockly.blockRendering.ConstantProvider {
  makePuzzleTab() {
    const width = this.TAB_WIDTH;
    const height = this.TAB_HEIGHT;
    return {
      type: this.SHAPES.PUZZLE,
      width,
      height,
      pathUp: Blockly.utils.svgPaths.line([
          Blockly.utils.svgPaths.point(-width, -height / 2),
          Blockly.utils.svgPaths.point(width, -height / 2)]),
      pathDown: Blockly.utils.svgPaths.line([
          Blockly.utils.svgPaths.point(-width, height / 2),
          Blockly.utils.svgPaths.point(width, height / 2)]),
    };
  }
}

如需了解如何定义路径字符串,请参阅 MDN SVG 路径文档Blockly.utils.svgPaths 命名空间作为这些字符串的瘦封装容器提供,以提高其可读性。

用于连接检查的形状

具有不同形状的不同连接

您可以根据连接的连接检查更改形状,以自定义连接。

这样,您就可以创建不同的形状来表示不同的数据类型。例如,字符串可以由三角形连接表示,而布尔值则由圆形连接表示。

如需为不同的连接检查提供不同的形状,您需要替换 shapeFor 方法。返回的形状应在 init 中进行初始化。

如需了解系统支持的形状类型,请参阅基本形状

export class ConstantProvider extends Blockly.blockRendering.BaseConstantProvider {
  shapeFor(connection) {
    let check = connection.getCheck();
    // For connections with no check, match any child block.
    if (!check && connection.targetConnection) {
      check = connection.targetConnection.getCheck();
    }

    if (check && check.includes('String')) return this.TRIANGULAR_TAB;
    if (check && check.includes('Boolean')) return this.ROUND_TAB;

    return super.shapeFor(connection);
  }
}

自定义输入

您可以通过创建完全自定义的输入来自定义连接形状。仅当您希望某些连接看起来与其他连接不同,但不希望它基于连接检查时,才执行此操作。

例如,如果您希望某些值输入像语句输入一样缩进,则可以创建自定义输入来支持此操作。

创建自定义输入类

按照创建自定义输入的步骤操作。

创建可衡量的

您需要创建一个可衡量值来表示您的自定义输入。

可衡量的自定义输入应继承自 Blockly.blockRendering.InputConnection。它还可以包含绘制输入形状的形状所需的任何额外测量数据。

export class CustomInputMeasurable extends Blockly.blockRendering.InputConnection {
  constructor(constants, input) {
    super(constants, input);

    // Any extra measurement data...
  }
}

实例化您的可衡量展示

您的渲染信息需要对自定义可衡量值进行实例化。为此,您需要替换 addInput_ 方法。

export class RenderInfo extends Blockly.blockRendering.RenderInfo {
  addInput_(input, activeRow) {
    if (input instanceof CustomInput) {
      activeRow.elements.push(new CustomInputMeasurable(this.constants_, input));
    }
    super.addInput_(input, activeRow);
  }
}

(可选)创建一行

默认情况下,输入不会创建新的。如果您希望输入触发行结尾,则需要替换渲染信息shouldStartNewRow_ 方法。

export class RenderInfo extends Blockly.blockRendering.RenderInfo {
  shouldStartNewRow_(currInput, prevInput) {
    if (prevInput instanceof CustomInput) return true;
    return super.shouldStartNewRow_(currInput, prevInput);
  }
}

(可选)为输入创建一个形状

最好将输入内容的形状存储在常量中,就像我们处理凹口和谜题标签页一样。这样可以让您的代码保持井然有序,并使以后的修改变得更轻松。

绘制输入源

最后,您需要修改抽屉以绘制形状。

自定义输入可以是以下任何一种:

  • 影响代码块的轮廓,例如语句输入

    大纲输入图像

  • 或者影响块的内部构件,例如内嵌值输入

    内部输入的图像

如果输入会影响代码块的轮廓,请替换 drawOutline_,否则,替换 drawInternals_

export class Drawer extends Blockly.blockRendering.Drawer {
  drawOutline_() {
    this.drawTop_();
    for (let r = 1; r < this.info_.rows.length - 1; r++) {
      const row = this.info_.rows[r];

      // Insert checks for your input here!
      if (row.getLastInput() instanceof CustomInputMeasurable) {
        this.drawCustomInput(row);
      } else if (row.hasJaggedEdge) {
        this.drawJaggedEdge_(row);
      } else if (row.hasStatement) {
        this.drawStatementInput_(row);
      } else if (row.hasExternalInput) {
        this.drawValueInput_(row);
      } else {
        this.drawRightSideRow_(row);
      }
    }
    this.drawBottom_();
    this.drawLeft_();
  }

  protected drawInternals_() {
    for (const row of rows) {
      for (const elem of row) {

        // Insert checks for your input here!
        if (elem instanceof CustomInputMeasurable) {
          this.drawCustomInput(elem);
        }

        if (Types.isInlineInput(elem)) {
          this.drawInlineInput_(elem as InlineInput);
        } else if (Types.isIcon(elem) || Types.isField(elem)) {
          this.layoutField_(elem as Field | Icon);
        }
      }
    }
  }
}