Scratch Blocks自定义组件之「下拉图标」

一、背景 

由于自带的下拉图标是给水平布局的block使用,放在垂直布局下显得别扭,而且下拉选择后回修改image字段的图片,这让我很不爽,所以在原来的基础上稍作修改,效果如下:

 二、使用说明

(1)引入field_icon_dropdown.js到core文件夹中,代码在下文

(2)将field_icon_dropdown注册到Blockly中,这样在任意地方都可以使用,如果不想注入,直接用script标签引入也行,代码如下

goog.require('Blockly.FieldIconDropDown');

(3)block定义代码如下,下面代码是直接集成到一个完整block中,以设置彩灯块为例:

// ZE3P LED
Blockly.Blocks['ZE3P_led'] = {
  init: function () {
    this.jsonInit({
      "message0": "%1",
      "args0": [
        {
          "type": "field_icon_dropdown",
          "name": "COLOR",
          "options": [
            {
              src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_coral.svg',
              width: 48,
              height: 48,
              value: 'Red'
            },
            {
              src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_green.svg',
              width: 48,
              height: 48,
              value: 'Green'
            },
            {
              src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_blue.svg',
              width: 48,
              height: 48,
              value: 'Blue'
            },
            {
              src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_orange.svg',
              width: 48,
              height: 48,
              value: 'Orange'
            },
            {
              src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_yellow.svg',
              width: 48,
              height: 48,
              value: 'Yellow'
            },
            {
              src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_white.svg',
              width: 48,
              height: 48,
              value: 'White'
            },
          ],
        }
      ],
      "outputShape": Blockly.OUTPUT_SHAPE_ROUND,
      "output": "String",
      "extensions": ["colours_looks"]
    });
  }
}
// ZE3P LED显示颜色
Blockly.Blocks['ZE3P_led_set_color'] = {
  init: function () {
    this.jsonInit({
      "message0": "%1%2",
      "args0": [
        {
          "type": "field_image",
          "src": Blockly.mainWorkspace.options.pathToMedia + "/extensions/ZE3P.png",
          "width": 24,
          "height": 24
        },
        {
          "type": "field_vertical_separator"
        }
      ],
      "message1": "设置彩灯 %1 显示 %2 ",
      "args1": [
        {
          "type": "field_pin_dropdown",
          "name": "INTERFACE",
          "options": Blockly.Blocks.ZE3PInterfaceOptions,
        },
        {
          "type": "input_value",
          "name": "COLOR",
        }
      ],
      "category": Blockly.Categories.looks,
      "extensions": ["colours_looks", "shape_statement"]
    });
  }
};

 (4)添加toolbox配置,代码如下:

<block type="ZE3P_led_set_color" id="ZE3P_led_set_color">
    <value name="COLOR">
        <shadow type="ZE3P_led">
            <field name="COLOR">Red</field>
        </shadow>
    </value>
</block>

(5)转码实现以python为例,代码如霞 

// LED颜色
Blockly.Python['ZE3P_led'] = function (block) {
  let color = block.getFieldValue('COLOR') || 0;
  const code = "LedColor." + color;
  return [code, Blockly.Python.ORDER_ATOMIC];
};
// LED显示颜色
Blockly.Python['ZE3P_led_set_color'] = function (block) {
  const pin = block.getFieldValue('INTERFACE') || "";
  const color = Blockly.Python.valueToCode(block, 'COLOR', Blockly.Python.ORDER_ATOMIC) || "";
  return `led.set_color(Interface.${pin}, ${color})\n`;
};

提示:如果采用注册方法,最好本地编译一下,使用javascript引入则不需要 

三、效果展示

扫描二维码关注公众号,回复: 16875447 查看本文章

 四、完整代码

完整field_icon_dropdown.js代码如下:

'use strict';

goog.provide('Blockly.FieldIconDropDown');
goog.require('Blockly.DropDownDiv');


/**
 * 构造器
 * @param icons
 * @constructor
 */
Blockly.FieldIconDropDown = function (icons) {
  this.icons_ = icons;
  // Example:
  // [{src: '...', width: 20, height: 20, value: 'machine_value'}, ...]
  // 选择第一个为默认值
  const defaultValue = icons[0].value;
  Blockly.FieldIconDropDown.superClass_.constructor.call(this, defaultValue);
  this.addArgType('icon_dropdown');
};
goog.inherits(Blockly.FieldIconDropDown, Blockly.Field);

/**
 * Json配置
 */
Blockly.FieldIconDropDown.fromJson = function (element) {
  return new Blockly.FieldIconDropDown(element['options']);
};

/**
 * 下拉面板宽度(不需要修改,3个图标宽度)
 * @type {number}
 * @const
 */
Blockly.FieldIconDropDown.DROPDOWN_WIDTH = 168;

/**
 * 颜色记录
 */
Blockly.FieldIconDropDown.savedPrimary_ = null;

/**
 * 初始化
 */
Blockly.FieldIconDropDown.prototype.init = function (block) {
  if (this.fieldGroup_) {
    return;
  }
  // 下拉箭头大小
  const arrowSize = 12;

  // 重建dom
  this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
  this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);

  // 字段宽度
  this.size_.width = 44;

  // 图标
  this.imageElement_ = Blockly.utils.createSvgElement('image', {
    'height': 24 + 'px',
    'width': 24 + 'px',
    'x': 4 + "px",
    'y': 4 + "px",
  }, this.fieldGroup_);
  this.setParentFieldImage(this.getSrcForValue(this.value_));

  // 下拉箭头位置
  this.arrowX_ = 32;
  this.arrowY_ = 10;
  if (block.RTL) {
    this.arrowX_ = -this.arrowX_ - arrowSize;
  }

  // 下拉图标
  this.arrowIcon_ = Blockly.utils.createSvgElement('image', {
    'height': arrowSize + 'px',
    'width': arrowSize + 'px',
    'transform': 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')'
  }, this.fieldGroup_);
  this.arrowIcon_.setAttributeNS('http://www.w3.org/1999/xlink',
    'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow.svg');

  this.mouseDownWrapper_ = Blockly.bindEventWithChecks_(
    this.getClickTarget_(), 'mousedown', this, this.onMouseDown_);
};

/**
 * 鼠标放置样式
 */
Blockly.FieldIconDropDown.prototype.CURSOR = 'default';

/**
 * 设置值
 */
Blockly.FieldIconDropDown.prototype.setValue = function (newValue) {
  if (newValue === null || newValue === this.value_) {
    return;  // No change
  }
  if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
    Blockly.Events.fire(new Blockly.Events.Change(
      this.sourceBlock_, 'field', this.name, this.value_, newValue));
  }
  this.value_ = newValue;
  this.setParentFieldImage(this.getSrcForValue(this.value_));
};

/**
 * 设置当前选择图片
 */
Blockly.FieldIconDropDown.prototype.setParentFieldImage = function (src) {
  if (this.imageElement_ && src) {
    this.imageElement_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', src || '');
  }
};

/**
 * 获取值
 */
Blockly.FieldIconDropDown.prototype.getValue = function () {
  return this.value_;
};

/**
 * 根据src获取值
 * @param value
 * @returns {*}
 */
Blockly.FieldIconDropDown.prototype.getSrcForValue = function (value) {
  for (var i = 0, icon; icon = this.icons_[i]; i++) {
    if (icon.value === value) {
      return icon.src;
    }
  }
};

/**
 * 下拉选择
 */
Blockly.FieldIconDropDown.prototype.showEditor_ = function () {
  if (Blockly.DropDownDiv.hideIfOwner(this)) {
    return;
  }
  Blockly.DropDownDiv.hideWithoutAnimation();
  Blockly.DropDownDiv.clearContent();
  // 构建下拉内容
  const contentDiv = Blockly.DropDownDiv.getContentDiv();
  // Accessibility properties
  contentDiv.setAttribute('role', 'menu');
  contentDiv.setAttribute('aria-haspopup', 'true');
  for (let i = 0, icon; icon = this.icons_[i]; i++) {

    // 按钮
    const button = document.createElement('button');
    button.setAttribute('id', ':' + i);
    button.setAttribute('role', 'menuitem');
    button.setAttribute('class', 'blocklyDropDownButton');
    button.title = icon.alt;
    button.style.width = icon.width + 'px';
    button.style.height = icon.height + 'px';
    let backgroundColor = this.sourceBlock_.getColour();
    if (icon.value === this.getValue()) {
      backgroundColor = this.sourceBlock_.getColourTertiary();
      button.setAttribute('aria-selected', 'true');
    }
    button.style.backgroundColor = backgroundColor;
    button.style.borderColor = this.sourceBlock_.getColourTertiary();

    // 事件
    Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
    Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
    Blockly.bindEvent_(button, 'mousedown', button, function (e) {
      this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
      e.preventDefault();
    });
    Blockly.bindEvent_(button, 'mouseover', button, function () {
      this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
      contentDiv.setAttribute('aria-activedescendant', this.id);
    });
    Blockly.bindEvent_(button, 'mouseout', button, function () {
      this.setAttribute('class', 'blocklyDropDownButton');
      contentDiv.removeAttribute('aria-activedescendant');
    });

    // 图标
    const buttonImg = document.createElement('img');
    buttonImg.src = icon.src;
    button.setAttribute('data-value', icon.value);
    buttonImg.setAttribute('data-value', icon.value);
    button.appendChild(buttonImg);
    contentDiv.appendChild(button);
  }
  contentDiv.style.width = Blockly.FieldIconDropDown.DROPDOWN_WIDTH + 'px';

  // 设置颜色
  Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
  Blockly.DropDownDiv.setCategory(this.sourceBlock_.parentBlock_.getCategory());
  this.savedPrimary_ = this.sourceBlock_.getColour();
  this.sourceBlock_.setColour(this.sourceBlock_.getColourSecondary(),
    this.sourceBlock_.getColourSecondary(),
    this.sourceBlock_.getColourTertiary());

  const scale = this.sourceBlock_.workspace.scale;
  const secondaryYOffset = (
    -(Blockly.BlockSvg.MIN_BLOCK_Y * scale) - (Blockly.BlockSvg.FIELD_Y_OFFSET * scale)
  );
  const renderedPrimary = Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_, this.onHide_.bind(this), secondaryYOffset);
  if (!renderedPrimary) {
    const arrowX = this.arrowX_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5 + 1;
    const arrowY = this.arrowY_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5;
    this.arrowIcon_.setAttribute('transform', 'translate(' + arrowX + ',' + arrowY + ') rotate(180)');
  }
};

/**
 * 点击按钮
 */
Blockly.FieldIconDropDown.prototype.buttonClick_ = function (e) {
  const value = e.target.getAttribute('data-value');
  this.setValue(value);
  Blockly.DropDownDiv.hide();
};

/**
 * 关闭下拉面板时回掉
 */
Blockly.FieldIconDropDown.prototype.onHide_ = function () {
  if (this.sourceBlock_) {
    this.sourceBlock_.setColour(this.savedPrimary_,
      this.sourceBlock_.getColourSecondary(),
      this.sourceBlock_.getColourTertiary());
  }
  Blockly.DropDownDiv.content_.removeAttribute('role');
  Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
  Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
  this.arrowIcon_.setAttribute('transform', 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')');
};

Blockly.Field.register('field_icon_dropdown', Blockly.FieldIconDropDown);

五、关于我

作者:陆志敏

联系:[email protected]

猜你喜欢

转载自blog.csdn.net/weixin_43532890/article/details/132057887
今日推荐