连接形状

您可以通过多种方式自定义连接的外观,每种方式 难度逐渐增加。所有这些操作都需要创建一个 自定义渲染程序

基本维度

具有不同维度的连接

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

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

对于基础渲染程序,您可以替换 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);
        }
      }
    }
  }
}