如何在 Node-Red 中使用 D3
How to use D3 in Node-Red
我正在使用仪表板节点,但还想显示动画。 D3 非常适合动画,因此我想知道是否可以用 D3 填充 Node-Red 中的一组仪表板选项卡?
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
<script src="https://rawgit.com/gka/d3-jetpack/master/d3-jetpack.js"></script>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", '800px')
.attr("height", '800px');
var data = [{x: 100, y: 100, h: 10, w: 50, c: "red"}, {x: 200, y: 200, h: 20, w: 15, c: "yellow"}, {x: 300, y: 300, h: 60, w: 80, c: "green"}]
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.w; })
.attr("height", function(d) { return d.h; })
.style("fill", function(d) { return d.c; })
//# sourceURL=userscript.js
</script>
您几乎可以在模板节点中放置任何您想要的东西。
值得注意的是,仪表板-ui 使用 Angular,因此您需要使用它才能使用传入的消息和数据。
为了从您的 ui_template
代码中使用外部 js 库,最好将 src 链接和 html/css/svg 代码放入单独的模板中。在第一个中,选择“Add to site section”选项,并包括您的图书馆参考:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
<script src="https://rawgit.com/gka/d3-jetpack/master/d3-jetpack.js"></script>
然后,将另一个 ui_template
节点添加到您的仪表板 tab/group,并包含此代码:
<style>
#myBoxes {
height: 800px;
width: 800px;
}
</style>
<svg id="myBoxes">
<!-- svg content goes here -->
</svg>
<script>
(function(scope) {
// watch for msgs from node-red
scope.$watch('msg.payload', function(payload) {
drawBoxes("myBoxes", payload);
})
})(scope);
function drawBoxes(id, data) {
var svg = d3.select("#" + id);
var rect = svg.selectAll("rect")
.data(data);
rect.enter()
.append("rect");
rect.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.w; })
.attr("height", function(d) { return d.h; })
.style("fill", function(d) { return d.c; });
rect.exit()
.remove();
}
</script>
我对你的示例代码进行了多次修改--
- 对固定的 DOM 元素使用 CSS 样式
- 创建一个空的
<svg>
元素,具有唯一的 id
- 包含 angular
scope.$watch(...)
以接收来自 node-red 的新消息
- 将 D3 逻辑放在一个新函数中,从范围观察器调用
- 使用 D3 enter/update/exit 选项支持 object constancy
这是一个包含几个不同输入的示例流程:
[{"id":"7b4017ce.0802d8","type":"inject","z":"76a6ae8d.8a35","name":"red/white/blue","topic":"","payload":"[{\"x\":100,\"y\":100,\"h\":40,\"w\":50,\"c\":\"red\"},{\"x\":200,\"y\":200,\"h\":20,\"w\":75,\"c\":\"white\"},{\"x\":300,\"y\":300,\"h\":20,\"w\":75,\"c\":\"blue\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":540,"wires":[["3adb8b4a.dbf3c4"]]},{"id":"3adb8b4a.dbf3c4","type":"ui_template","z":"76a6ae8d.8a35","group":"f2c8d56c.4d75b8","name":"D3 template","order":1,"width":0,"height":0,"format":"<style>\n #myBoxes {\n height: 800px;\n width: 800px;\n }\n</style>\n\n<svg id=\"myBoxes\">\n <!-- svg content goes here -->\n</svg>\n\n<script>\n(function(scope) { \n // watch for msgs from node-red\n scope.$watch('msg.payload', function(payload) {\n drawBoxes(\"myBoxes\", payload);\n })\n})(scope);\n\nfunction drawBoxes(id, data) {\n var svg = d3.select(\"#\" + id);\n var rect = svg.selectAll(\"rect\")\n .data(data);\n\n rect.enter()\n .append(\"rect\");\n \n rect.attr(\"x\", function(d) { return d.x; })\n .attr(\"y\", function(d) { return d.y; }) \n .attr(\"width\", function(d) { return d.w; })\n .attr(\"height\", function(d) { return d.h; })\n .style(\"fill\", function(d) { return d.c; });\n \n rect.exit()\n .remove();\n}\n</script>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":510,"y":540,"wires":[[]]},{"id":"e1ab2277.a8f19","type":"ui_template","z":"76a6ae8d.8a35","group":"d215108d.8b51b","name":"D3 libraries","order":0,"width":0,"height":0,"format":"<script src=\"https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js\"></script>\n<script src=\"https://rawgit.com/gka/d3-jetpack/master/d3-jetpack.js\"></script>\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"global","x":510,"y":500,"wires":[[]]},{"id":"a4d0e22c.4689","type":"inject","z":"76a6ae8d.8a35","name":"black/white","topic":"","payload":"[{\"x\":100,\"y\":100,\"h\":40,\"w\":40,\"c\":\"black\"},{\"x\":200,\"y\":200,\"h\":40,\"w\":40,\"c\":\"white\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":200,"y":580,"wires":[["3adb8b4a.dbf3c4"]]},{"id":"e7af594f.9862d8","type":"inject","z":"76a6ae8d.8a35","name":"red/yellow/green","topic":"","payload":"[{\"x\":100,\"y\":100,\"h\":10,\"w\":50,\"c\":\"red\"},{\"x\":200,\"y\":200,\"h\":20,\"w\":15,\"c\":\"yellow\"},{\"x\":300,\"y\":300,\"h\":60,\"w\":80,\"c\":\"green\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":500,"wires":[["3adb8b4a.dbf3c4"]]},{"id":"f2c8d56c.4d75b8","type":"ui_group","z":"","name":"D3","tab":"e3e3a35c.7a77f","order":2,"disp":true,"width":"12","collapse":false},{"id":"d215108d.8b51b","type":"ui_group","z":"","name":"Recalls","tab":"3c000bae.098b94","order":2,"disp":true,"width":"18"},{"id":"e3e3a35c.7a77f","type":"ui_tab","z":"","name":"Libraries","icon":"fa-bank","order":6},{"id":"3c000bae.098b94","type":"ui_tab","z":"","name":"Vehicles","icon":"fa-car"}]
我正在使用仪表板节点,但还想显示动画。 D3 非常适合动画,因此我想知道是否可以用 D3 填充 Node-Red 中的一组仪表板选项卡?
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
<script src="https://rawgit.com/gka/d3-jetpack/master/d3-jetpack.js"></script>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", '800px')
.attr("height", '800px');
var data = [{x: 100, y: 100, h: 10, w: 50, c: "red"}, {x: 200, y: 200, h: 20, w: 15, c: "yellow"}, {x: 300, y: 300, h: 60, w: 80, c: "green"}]
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.w; })
.attr("height", function(d) { return d.h; })
.style("fill", function(d) { return d.c; })
//# sourceURL=userscript.js
</script>
您几乎可以在模板节点中放置任何您想要的东西。
值得注意的是,仪表板-ui 使用 Angular,因此您需要使用它才能使用传入的消息和数据。
为了从您的 ui_template
代码中使用外部 js 库,最好将 src 链接和 html/css/svg 代码放入单独的模板中。在第一个中,选择“Add to site section”选项,并包括您的图书馆参考:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
<script src="https://rawgit.com/gka/d3-jetpack/master/d3-jetpack.js"></script>
然后,将另一个 ui_template
节点添加到您的仪表板 tab/group,并包含此代码:
<style>
#myBoxes {
height: 800px;
width: 800px;
}
</style>
<svg id="myBoxes">
<!-- svg content goes here -->
</svg>
<script>
(function(scope) {
// watch for msgs from node-red
scope.$watch('msg.payload', function(payload) {
drawBoxes("myBoxes", payload);
})
})(scope);
function drawBoxes(id, data) {
var svg = d3.select("#" + id);
var rect = svg.selectAll("rect")
.data(data);
rect.enter()
.append("rect");
rect.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.w; })
.attr("height", function(d) { return d.h; })
.style("fill", function(d) { return d.c; });
rect.exit()
.remove();
}
</script>
我对你的示例代码进行了多次修改--
- 对固定的 DOM 元素使用 CSS 样式
- 创建一个空的
<svg>
元素,具有唯一的 id - 包含 angular
scope.$watch(...)
以接收来自 node-red 的新消息
- 将 D3 逻辑放在一个新函数中,从范围观察器调用
- 使用 D3 enter/update/exit 选项支持 object constancy
这是一个包含几个不同输入的示例流程:
[{"id":"7b4017ce.0802d8","type":"inject","z":"76a6ae8d.8a35","name":"red/white/blue","topic":"","payload":"[{\"x\":100,\"y\":100,\"h\":40,\"w\":50,\"c\":\"red\"},{\"x\":200,\"y\":200,\"h\":20,\"w\":75,\"c\":\"white\"},{\"x\":300,\"y\":300,\"h\":20,\"w\":75,\"c\":\"blue\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":540,"wires":[["3adb8b4a.dbf3c4"]]},{"id":"3adb8b4a.dbf3c4","type":"ui_template","z":"76a6ae8d.8a35","group":"f2c8d56c.4d75b8","name":"D3 template","order":1,"width":0,"height":0,"format":"<style>\n #myBoxes {\n height: 800px;\n width: 800px;\n }\n</style>\n\n<svg id=\"myBoxes\">\n <!-- svg content goes here -->\n</svg>\n\n<script>\n(function(scope) { \n // watch for msgs from node-red\n scope.$watch('msg.payload', function(payload) {\n drawBoxes(\"myBoxes\", payload);\n })\n})(scope);\n\nfunction drawBoxes(id, data) {\n var svg = d3.select(\"#\" + id);\n var rect = svg.selectAll(\"rect\")\n .data(data);\n\n rect.enter()\n .append(\"rect\");\n \n rect.attr(\"x\", function(d) { return d.x; })\n .attr(\"y\", function(d) { return d.y; }) \n .attr(\"width\", function(d) { return d.w; })\n .attr(\"height\", function(d) { return d.h; })\n .style(\"fill\", function(d) { return d.c; });\n \n rect.exit()\n .remove();\n}\n</script>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":510,"y":540,"wires":[[]]},{"id":"e1ab2277.a8f19","type":"ui_template","z":"76a6ae8d.8a35","group":"d215108d.8b51b","name":"D3 libraries","order":0,"width":0,"height":0,"format":"<script src=\"https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js\"></script>\n<script src=\"https://rawgit.com/gka/d3-jetpack/master/d3-jetpack.js\"></script>\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"global","x":510,"y":500,"wires":[[]]},{"id":"a4d0e22c.4689","type":"inject","z":"76a6ae8d.8a35","name":"black/white","topic":"","payload":"[{\"x\":100,\"y\":100,\"h\":40,\"w\":40,\"c\":\"black\"},{\"x\":200,\"y\":200,\"h\":40,\"w\":40,\"c\":\"white\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":200,"y":580,"wires":[["3adb8b4a.dbf3c4"]]},{"id":"e7af594f.9862d8","type":"inject","z":"76a6ae8d.8a35","name":"red/yellow/green","topic":"","payload":"[{\"x\":100,\"y\":100,\"h\":10,\"w\":50,\"c\":\"red\"},{\"x\":200,\"y\":200,\"h\":20,\"w\":15,\"c\":\"yellow\"},{\"x\":300,\"y\":300,\"h\":60,\"w\":80,\"c\":\"green\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":500,"wires":[["3adb8b4a.dbf3c4"]]},{"id":"f2c8d56c.4d75b8","type":"ui_group","z":"","name":"D3","tab":"e3e3a35c.7a77f","order":2,"disp":true,"width":"12","collapse":false},{"id":"d215108d.8b51b","type":"ui_group","z":"","name":"Recalls","tab":"3c000bae.098b94","order":2,"disp":true,"width":"18"},{"id":"e3e3a35c.7a77f","type":"ui_tab","z":"","name":"Libraries","icon":"fa-bank","order":6},{"id":"3c000bae.098b94","type":"ui_tab","z":"","name":"Vehicles","icon":"fa-car"}]