928b9177 by guyan

finish start-code for simple js block definition

0 parents
1 <!doctype html>
2 <html>
3 <head>
4 <meta charset="utf-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
8 <title>Blockly for the Web Codelab</title>
9
10 <link
11 rel="stylesheet"
12 href="https://code.getmdl.io/1.2.1/material.indigo-pink.min.css" />
13 <link rel="stylesheet" href="styles/index.css" />
14 </head>
15
16 <body mode="maker">
17 <header class="mdl-color--cyan-500">
18 <h1 class="mode-maker">Music Maker</h1>
19 <h1 class="mode-edit mode-blockly">Music Maker Configuration</h1>
20 </header>
21
22 <main>
23 <button class="mode-maker mdl-button" id="edit">Edit</button>
24 <button class="mode-edit mdl-button mdl-js-button" id="done">Done</button>
25 <button class="mode-blockly mdl-button mdl-js-button" id="save">
26 Save
27 </button>
28 <p class="hint mode-edit">
29 Tap any button to edit its code. <br />When complete, press Done.
30 </p>
31
32 <div class="maker">
33 <div>
34 <div class="button mdl-color--amber-500">1</div>
35 <div class="button mdl-color--yellow-500">2</div>
36 <div class="button mdl-color--lime-500">3</div>
37 </div>
38 <div>
39 <div class="button mdl-color--pink-500">4</div>
40 <div class="button mdl-color--red-500">5</div>
41 <div class="button mdl-color--light-green-500">6</div>
42 </div>
43 <div>
44 <div class="button mdl-color--cyan-500">7</div>
45 <div class="button mdl-color--teal-500">8</div>
46 <div class="button mdl-color--green-500">9</div>
47 </div>
48 </div>
49
50 <div class="blockly-editor">
51 <div id="blocklyDiv" style="height: 480px; width: 400px"></div>
52 </div>
53 </main>
54
55
56 <script src="https://unpkg.com/blockly/blockly_compressed.js"></script>
57 <script src="https://unpkg.com/blockly/blocks_compressed.js"></script>
58 <script src="https://unpkg.com/blockly/javascript_compressed.js"></script>
59 <script src="https://unpkg.com/blockly/msg/en.js"></script>
60 <!-- 先加载blocky的几个核心模块 -->
61 <script src="scripts/sound_blocks.js"></script>
62
63 <script src="scripts/music_maker.js"></script>
64 <script src="scripts/main.js"></script>
65 </body>
66 </html>
1 'use strict';
2
3 goog.provide('Blockly.FieldIconDropDown');
4 goog.require('Blockly.DropDownDiv');
5
6
7 /**
8 * 构造器
9 * @param icons
10 * @constructor
11 */
12 Blockly.FieldIconDropDown = function (icons) {
13 this.icons_ = icons;
14 // Example:
15 // [{src: '...', width: 20, height: 20, value: 'machine_value'}, ...]
16 // 选择第一个为默认值
17 const defaultValue = icons[0].value;
18 Blockly.FieldIconDropDown.superClass_.constructor.call(this, defaultValue);
19 this.addArgType('icon_dropdown');
20 };
21 goog.inherits(Blockly.FieldIconDropDown, Blockly.Field);
22
23 /**
24 * Json配置
25 */
26 Blockly.FieldIconDropDown.fromJson = function (element) {
27 return new Blockly.FieldIconDropDown(element['options']);
28 };
29
30 /**
31 * 下拉面板宽度(不需要修改,3个图标宽度)
32 * @type {number}
33 * @const
34 */
35 Blockly.FieldIconDropDown.DROPDOWN_WIDTH = 168;
36
37 /**
38 * 颜色记录
39 */
40 Blockly.FieldIconDropDown.savedPrimary_ = null;
41
42 /**
43 * 初始化
44 */
45 Blockly.FieldIconDropDown.prototype.init = function (block) {
46 if (this.fieldGroup_) {
47 return;
48 }
49 // 下拉箭头大小
50 const arrowSize = 12;
51
52 // 重建dom
53 this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
54 this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
55
56 // 字段宽度
57 this.size_.width = 44;
58
59 // 图标
60 this.imageElement_ = Blockly.utils.createSvgElement('image', {
61 'height': 24 + 'px',
62 'width': 24 + 'px',
63 'x': 4 + "px",
64 'y': 4 + "px",
65 }, this.fieldGroup_);
66 this.setParentFieldImage(this.getSrcForValue(this.value_));
67
68 // 下拉箭头位置
69 this.arrowX_ = 32;
70 this.arrowY_ = 10;
71 if (block.RTL) {
72 this.arrowX_ = -this.arrowX_ - arrowSize;
73 }
74
75 // 下拉图标
76 this.arrowIcon_ = Blockly.utils.createSvgElement('image', {
77 'height': arrowSize + 'px',
78 'width': arrowSize + 'px',
79 'transform': 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')'
80 }, this.fieldGroup_);
81 this.arrowIcon_.setAttributeNS('http://www.w3.org/1999/xlink',
82 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow.svg');
83
84 this.mouseDownWrapper_ = Blockly.bindEventWithChecks_(
85 this.getClickTarget_(), 'mousedown', this, this.onMouseDown_);
86 };
87
88 /**
89 * 鼠标放置样式
90 */
91 Blockly.FieldIconDropDown.prototype.CURSOR = 'default';
92
93 /**
94 * 设置值
95 */
96 Blockly.FieldIconDropDown.prototype.setValue = function (newValue) {
97 if (newValue === null || newValue === this.value_) {
98 return; // No change
99 }
100 if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
101 Blockly.Events.fire(new Blockly.Events.Change(
102 this.sourceBlock_, 'field', this.name, this.value_, newValue));
103 }
104 this.value_ = newValue;
105 this.setParentFieldImage(this.getSrcForValue(this.value_));
106 };
107
108 /**
109 * 设置当前选择图片
110 */
111 Blockly.FieldIconDropDown.prototype.setParentFieldImage = function (src) {
112 if (this.imageElement_ && src) {
113 this.imageElement_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', src || '');
114 }
115 };
116
117 /**
118 * 获取值
119 */
120 Blockly.FieldIconDropDown.prototype.getValue = function () {
121 return this.value_;
122 };
123
124 /**
125 * 根据src获取值
126 * @param value
127 * @returns {*}
128 */
129 Blockly.FieldIconDropDown.prototype.getSrcForValue = function (value) {
130 for (var i = 0, icon; icon = this.icons_[i]; i++) {
131 if (icon.value === value) {
132 return icon.src;
133 }
134 }
135 };
136
137 /**
138 * 下拉选择
139 */
140 Blockly.FieldIconDropDown.prototype.showEditor_ = function () {
141 if (Blockly.DropDownDiv.hideIfOwner(this)) {
142 return;
143 }
144 Blockly.DropDownDiv.hideWithoutAnimation();
145 Blockly.DropDownDiv.clearContent();
146 // 构建下拉内容
147 const contentDiv = Blockly.DropDownDiv.getContentDiv();
148 // Accessibility properties
149 contentDiv.setAttribute('role', 'menu');
150 contentDiv.setAttribute('aria-haspopup', 'true');
151 for (let i = 0, icon; icon = this.icons_[i]; i++) {
152
153 // 按钮
154 const button = document.createElement('button');
155 button.setAttribute('id', ':' + i);
156 button.setAttribute('role', 'menuitem');
157 button.setAttribute('class', 'blocklyDropDownButton');
158 button.title = icon.alt;
159 button.style.width = icon.width + 'px';
160 button.style.height = icon.height + 'px';
161 let backgroundColor = this.sourceBlock_.getColour();
162 if (icon.value === this.getValue()) {
163 backgroundColor = this.sourceBlock_.getColourTertiary();
164 button.setAttribute('aria-selected', 'true');
165 }
166 button.style.backgroundColor = backgroundColor;
167 button.style.borderColor = this.sourceBlock_.getColourTertiary();
168
169 // 事件
170 Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
171 Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
172 Blockly.bindEvent_(button, 'mousedown', button, function (e) {
173 this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
174 e.preventDefault();
175 });
176 Blockly.bindEvent_(button, 'mouseover', button, function () {
177 this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
178 contentDiv.setAttribute('aria-activedescendant', this.id);
179 });
180 Blockly.bindEvent_(button, 'mouseout', button, function () {
181 this.setAttribute('class', 'blocklyDropDownButton');
182 contentDiv.removeAttribute('aria-activedescendant');
183 });
184
185 // 图标
186 const buttonImg = document.createElement('img');
187 buttonImg.src = icon.src;
188 button.setAttribute('data-value', icon.value);
189 buttonImg.setAttribute('data-value', icon.value);
190 button.appendChild(buttonImg);
191 contentDiv.appendChild(button);
192 }
193 contentDiv.style.width = Blockly.FieldIconDropDown.DROPDOWN_WIDTH + 'px';
194
195 // 设置颜色
196 Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
197 Blockly.DropDownDiv.setCategory(this.sourceBlock_.parentBlock_.getCategory());
198 this.savedPrimary_ = this.sourceBlock_.getColour();
199 this.sourceBlock_.setColour(this.sourceBlock_.getColourSecondary(),
200 this.sourceBlock_.getColourSecondary(),
201 this.sourceBlock_.getColourTertiary());
202
203 const scale = this.sourceBlock_.workspace.scale;
204 const secondaryYOffset = (
205 -(Blockly.BlockSvg.MIN_BLOCK_Y * scale) - (Blockly.BlockSvg.FIELD_Y_OFFSET * scale)
206 );
207 const renderedPrimary = Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_, this.onHide_.bind(this), secondaryYOffset);
208 if (!renderedPrimary) {
209 const arrowX = this.arrowX_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5 + 1;
210 const arrowY = this.arrowY_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5;
211 this.arrowIcon_.setAttribute('transform', 'translate(' + arrowX + ',' + arrowY + ') rotate(180)');
212 }
213 };
214
215 /**
216 * 点击按钮
217 */
218 Blockly.FieldIconDropDown.prototype.buttonClick_ = function (e) {
219 const value = e.target.getAttribute('data-value');
220 this.setValue(value);
221 Blockly.DropDownDiv.hide();
222 };
223
224 /**
225 * 关闭下拉面板时回掉
226 */
227 Blockly.FieldIconDropDown.prototype.onHide_ = function () {
228 if (this.sourceBlock_) {
229 this.sourceBlock_.setColour(this.savedPrimary_,
230 this.sourceBlock_.getColourSecondary(),
231 this.sourceBlock_.getColourTertiary());
232 }
233 Blockly.DropDownDiv.content_.removeAttribute('role');
234 Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
235 Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
236 this.arrowIcon_.setAttribute('transform', 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')');
237 };
238
239 Blockly.Field.register('field_icon_dropdown', Blockly.FieldIconDropDown);
...\ No newline at end of file ...\ No newline at end of file
1 /**
2 * @license
3 * Copyright 2017 Google LLC
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 goog.require('Blockly.FieldIconDropDown');
8
9 (function () {
10 let currentButton;
11
12 function handlePlay(event) {
13 loadWorkspace(event.target); // 加载被点击的按钮的工作区
14 let code = javascript.javascriptGenerator.workspaceToCode(Blockly.getMainWorkspace());
15 code += 'MusicMaker.play();';// 先加载生成音乐的代码,再添加播放音乐的代码
16 try {
17 eval(code); // 执行生成的代码
18 } catch (error) {
19 console.log(error);
20 }
21 }
22
23 function save(button) {
24 // Add code for saving the behavior of a button.
25 button.blocklySave = Blockly.serialization.workspaces.save(Blockly.getMainWorkspace());//保存特定按钮的工作区的状态
26 }
27
28 function handleSave() {
29 document.body.setAttribute('mode', 'edit');
30 save(currentButton);
31 }
32
33 function loadWorkspace(button) {
34 const workspace = Blockly.getMainWorkspace(); // 获取主工作区
35 if (button.blocklySave) {
36 Blockly.serialization.workspaces.load(button.blocklySave, workspace); // 加载特定按钮的工作区的状态
37 } else {
38 workspace.clear(); // 清空工作区
39 }
40 }
41
42 function enableEditMode() {
43 document.body.setAttribute('mode', 'edit');
44 document.querySelectorAll('.button').forEach((btn) => {
45 btn.removeEventListener('click', handlePlay);
46 btn.addEventListener('click', enableBlocklyMode);
47 });
48 }
49
50 function enableMakerMode() {
51 document.body.setAttribute('mode', 'maker');
52 document.querySelectorAll('.button').forEach((btn) => {
53 btn.addEventListener('click', handlePlay);
54 btn.removeEventListener('click', enableBlocklyMode);
55 });
56 }
57
58 function enableBlocklyMode(e) {
59 document.body.setAttribute('mode', 'blockly');
60 currentButton = e.target;
61 loadWorkspace(currentButton);
62 }
63
64 document.querySelector('#edit').addEventListener('click', enableEditMode);
65 document.querySelector('#done').addEventListener('click', enableMakerMode);
66 document.querySelector('#save').addEventListener('click', handleSave);
67
68 enableMakerMode();
69 const toolbox = { // 工具箱
70 'kind': 'flyoutToolbox',
71 'contents': [
72 {
73 'kind': 'block',
74 'type': 'controls_repeat_ext', // 循环块
75 'inputs': {
76 'TIMES': {
77 'shadow': {
78 'type': 'math_number',
79 'fields': {
80 'NUM': 5
81 }
82 }
83 }
84 }
85 },
86 {
87 'kind': 'block',
88 'type': 'play_sound' // 播放声音块
89 },
90 {
91 'kind': 'block',
92 'type': 'ZE3P_led_set_color', // 块类型
93 'inputs': {
94 'COLOR': {
95 'shadow': {
96 'type': 'ZE3P_led', // 影子块类型
97 'fields': {
98 'COLOR': 'Red' // 影子块中的字段值
99 }
100 }
101 }
102 }
103 }
104 ]
105 };
106
107 Blockly.inject('blocklyDiv', { // 注入到blocklyDiv中
108
109 toolbox: toolbox,
110 scrollbars: false,
111 horizontalLayout: true,
112 toolboxPosition: "end",
113 });
114 })();
1 /**
2 * @license
3 * Copyright 2017 Google LLC
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 const MusicMaker = {
7 queue_: [],
8 player_: new Audio(),
9 queueSound: function (soundUrl) {
10 this.queue_.push(soundUrl);
11 },
12 play: function () {
13 const next = this.queue_.shift();
14 if (next) {
15 this.player_.src = next;
16 this.player_.play();
17 }
18 },
19 };
20
21 MusicMaker.player_.addEventListener('ended', MusicMaker.play.bind(MusicMaker));
1 Blockly.common.defineBlocksWithJsonArray([
2 {
3 "type": "play_sound",
4 "message0": "Play %1",
5 "args0": [
6 {
7 "type": "field_dropdown", // 下拉框
8 "name": "VALUE",
9 "options": [ // 声音选项
10 ["C4", "sounds/c4.m4a"],
11 ["D4", "sounds/d4.m4a"],
12 ["E4", "sounds/e4.m4a"],
13 ["F4", "sounds/f4.m4a"],
14 ["G4", "sounds/g4.m4a"]
15 ]
16 }
17 ],
18 "previousStatement": null,
19 "nextStatement": null,
20 "colour": 355
21 }
22 ]);
23
24 javascript.javascriptGenerator.forBlock['play_sound'] = function(block) {
25 let value = '\'' + block.getFieldValue('VALUE') + '\'';
26 return 'MusicMaker.queueSound(' + value + ');\n'; // 调用MusicMaker.queueSound函数
27
28 };
29
30
31 Blockly.Blocks['ZE3P_led'] = {
32 init: function () {
33 this.jsonInit({
34 "message0": "%1",
35 "args0": [
36 {
37 "type": "field_icon_dropdown",
38 "name": "COLOR",
39 "options": [
40 {
41 src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_coral.svg',
42 width: 48,
43 height: 48,
44 value: 'Red'
45 },
46 {
47 src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_green.svg',
48 width: 48,
49 height: 48,
50 value: 'Green'
51 },
52 {
53 src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_blue.svg',
54 width: 48,
55 height: 48,
56 value: 'Blue'
57 },
58 {
59 src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_orange.svg',
60 width: 48,
61 height: 48,
62 value: 'Orange'
63 },
64 {
65 src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_yellow.svg',
66 width: 48,
67 height: 48,
68 value: 'Yellow'
69 },
70 {
71 src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_white.svg',
72 width: 48,
73 height: 48,
74 value: 'White'
75 },
76 ],
77 }
78 ],
79 "outputShape": Blockly.OUTPUT_SHAPE_ROUND,
80 "output": "String",
81 "extensions": ["colours_looks"]
82 });
83 }
84 }
85
86 // ZE3P LED显示颜色
87 Blockly.Blocks['ZE3P_led_set_color'] = {
88 init: function () {
89 this.jsonInit({
90 "message0": "%1%2",
91 "args0": [
92 {
93 "type": "field_image",
94 "src": Blockly.mainWorkspace.options.pathToMedia + "/extensions/ZE3P.png",
95 "width": 24,
96 "height": 24
97 },
98 {
99 "type": "field_vertical_separator"
100 }
101 ],
102 "message1": "设置彩灯 %1 显示 %2 ",
103 "args1": [
104 {
105 "type": "field_pin_dropdown",
106 "name": "INTERFACE",
107 "options": Blockly.Blocks.ZE3PInterfaceOptions,
108 },
109 {
110 "type": "input_value",
111 "name": "COLOR",
112 }
113 ],
114 "category": Blockly.Categories.looks,
115 "extensions": ["colours_looks", "shape_statement"]
116 });
117 }
118 };
119
120 // LED颜色
121 Blockly.Python['ZE3P_led'] = function (block) {
122 let color = block.getFieldValue('COLOR') || 0;
123 const code = "LedColor." + color;
124 return [code, Blockly.Python.ORDER_ATOMIC];
125 };
126
127 // LED显示颜色
128 Blockly.Python['ZE3P_led_set_color'] = function (block) {
129 const pin = block.getFieldValue('INTERFACE') || "";
130 const color = Blockly.Python.valueToCode(block, 'COLOR', Blockly.Python.ORDER_ATOMIC) || "";
131 return `led.set_color(Interface.${pin}, ${color})\n`;
132 };
...\ No newline at end of file ...\ No newline at end of file
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
1 main {
2 width: 400px;
3 position: relative;
4 margin: 0 auto;
5 overflow: hidden;
6 height: 600px;
7 }
8
9 header {
10 background-color: green;
11 width: 100%;
12 }
13
14 h1 {
15 width: 400px;
16 position: relative;
17 margin: 0 auto;
18 color: #fff;
19 font-size: 1.8em;
20 line-height: 2.4em;
21 }
22
23 .mode-edit,
24 .mode-maker,
25 .mode-blockly {
26 display: none;
27 }
28
29 [mode='maker'] .mode-maker,
30 [mode='edit'] .mode-edit,
31 [mode='blockly'] .mode-blockly {
32 display: block;
33 }
34
35 .blockly-editor {
36 position: absolute;
37 top: 64px;
38 left: -400px;
39 transition: left 0.4s;
40 height: 460px;
41 width: 400px;
42 background-color: #eee;
43 }
44
45 [mode='blockly'] .blockly-editor {
46 left: 0;
47 }
48
49 .maker {
50 display: flex;
51 flex-flow: column;
52 justify-content: space-between;
53 height: 460px;
54 width: 400px;
55 }
56
57 .maker > div {
58 display: flex;
59 justify-content: space-between;
60 }
61
62 .button {
63 width: 120px;
64 height: 140px;
65 color: #fff;
66 font-size: 3em;
67 text-align: center;
68 vertical-align: middle;
69 line-height: 140px;
70 }
71
72 .mdl-button {
73 margin: 1em 0;
74 float: right;
75 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!