D3阳光爆发:通过数据确定叶子的颜色,并让其父代继承该颜色
D3 Sunburst: Determine Leaf Color by Data and have its Parents Inherit that Color
所以我对D3可视化还很陌生,在学习中遇到了障碍。我有一个非常基本的日光浴设置,运行数据来确定测试是"失败"还是"成功"。此失败或成功表示为作为数据结果的太阳爆发外环。
我正试图根据这个成功或失败来改变这个外圈的颜色。例如,如果测试结果是成功,颜色将是绿色,如果失败,颜色将变为红色。我觉得有一种方法可以做到这一点,但我完全忽略了。
同样,一旦确定了颜色,我希望它的父圆弧,一直到最里面的环,"继承"这个颜色,红色将具有更高的优先级。所以基本上,如果一组测试中有一个失败,那么代表该组的弧也会是红色的。
PRTG的sunburst以这种方式工作,我找不到两者之间的联系来实现这一功能。
我使用的是D3示例中的基本sunburst代码:
define(function(require, exports, module) {
var _ = require('underscore');
var SimpleSplunkView = require("splunkjs/mvc/simplesplunkview");
var nester = require("../underscore-nest/underscore-nest");
var d3 = require("../d3/d3");
require("css!./sunburst.css");
window.nester = nester;
var ANIMATION_DURATION = 750; // milliseconds
var Sunburst = SimpleSplunkView.extend({
moduleId: module.id,
className: "splunk-toolkit-sunburst",
options: {
managerid: null,
data: 'preview',
chartTitle: null,
valueField: null,
categoryFields: null,
truncateValue: 0,
formatLabel: _.identity,
formatTooltip: function(d) {
return (d.name || "Total") + ": " + d.value;
}
},
output_mode: "json_rows",
initialize: function() {
SimpleSplunkView.prototype.initialize.apply(this, arguments);
this.settings.on("change:valueField", this.render, this);
this.settings.on("change:categoryFields", this.render, this);
this.settings.on("change:formatLabel change:formatTooltip change:chartTitle", this.render, this);
// Set up resize callback.
$(window).resize(_.debounce(_.bind(this._handleResize, this), 20));
},
_handleResize: function() {
this.render();
},
createView: function() {
// Here we wet up the initial view layout
var margin = {top: 30, right: 30, bottom: 30, left: 30};
var availableWidth = parseInt(this.settings.get("width") || this.$el.width());
var availableHeight = parseInt(this.settings.get("height") || this.$el.height());
this.$el.html("");
var svg = d3.select(this.el)
.append("svg")
.attr("width", availableWidth)
.attr("height", availableHeight)
.attr("pointer-events", "all");
// The returned object gets passed to updateView as viz
return { container: this.$el, svg: svg, margin: margin};
},
// making the data look how we want it to for updateView to do its job
formatData: function(data) {
var valueField = this.settings.get('valueField');
var rawFields = this.resultsModel.data().fields;
var fieldList = this.settings.get("categoryFields");
if(fieldList){
fieldList = fieldList.split(/[ ,]+/);
}
else{
fieldList = this.resultsModel.data().fields;
}
var objects = _.map(data, function(row) {
return _.object(rawFields, row);
});
var dataResults = nester.nest(objects, fieldList, function(children) {
var total = 0;
_.each(children, function(child){
var size = child[valueField] || 1;
total += size;
});
return total;
});
dataResults['name'] = this.settings.get("chartTitle") || "";
data = {
'results': dataResults,
'fields': fieldList
};
return data;
},
updateView: function(viz, data) {
var that = this;
var formatLabel = this.settings.get("formatLabel") || _.identity;
var formatTooltip = this.settings.get("formatTooltip") || function(d) { return d.name; };
var truncateValue = this.settings.get("truncateValue");
var containerHeight = this.$el.height();
var containerWidth = this.$el.width();
// Clear svg
var svg = $(viz.svg[0]);
svg.empty();
svg.height(containerHeight);
svg.width(containerWidth);
// Add the graph group as a child of the main svg
var graphWidth = containerWidth - viz.margin.left - viz.margin.right;
var graphHeight = containerHeight - viz.margin.top - viz.margin.bottom;
var graph = viz.svg
.append("g")
.attr("width", graphWidth)
.attr("height", graphHeight)
.attr("transform", "translate("
+ ((graphWidth/2) + viz.margin.left ) + ","
+ ((graphHeight/2) + viz.margin.top ) + ")");
var radius = Math.min(graphWidth, graphHeight) / 2;
var color = d3.scale.category20().range(["#98df8a"]);
var colorA = d3.scale.category20().range(["#98df8a"]); //green
var colorB = d3.scale.category20().range(["#d62728"]); //red
var colorC = d3.scale.category20().range(["#bcbd22"]); //yellow
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.range([0, radius]);
var partition = d3.layout.partition()
.value(function(d) { return d['value']; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
var root = data.results;
var g = graph.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var leaves = d3.selectAll("rect").filter(function(d) {
return d.children === null; });
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {return color((d.children ? d : d.parent).name); })
.on("click", click);
path.append("title")
.text(formatTooltip);
var textAnchorPos = function(depthMarker) {
return function(d) {
return (d.depth === depthMarker) ? 'middle' : ((x(d.x + d.dx / 2) > Math.PI) ? "end" : "start");
};
};
var textTransform = function(depthMarker) {
return function(d) {
// Objects at the origin don't need to be rotated
var angle = x(d.x + d.dx / 2) * 180 / Math.PI + (d.depth === depthMarker ? 0 : -90);
// Objects at the origin don't need to be moved.
// "5" pads the text off the drawn circle.
var translation = d.depth === depthMarker ? 0 : (y(d.y) + 5);
var rotate = angle;
return "rotate(" + rotate + ")translate(" + (translation) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
};
};
var text = g.append("text")
.attr("text-anchor", textAnchorPos(0))
.attr("transform", textTransform(0))
.attr("dy", ".2em")
.attr("x", 0)
.text(function(d) {
var sliceWidth = Math.abs(Math.max(0, y(d.y)) - Math.max(0, y(d.y + d.dy)));
var formatted = formatLabel(d.name);
// Trunctate the title
return formatted.substring(0, sliceWidth / truncateValue);
})
.on("click", click);
text.append("title")
.text(formatTooltip);
function click(d) {
// The "at depth" object is treated differently;
// centered and not rotated.
var depthMarker = d.depth;
// fade out all text elements
text.transition().attr("opacity", 0);
path.transition()
.duration(ANIMATION_DURATION)
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies
// within the visible angle span given in d and
// the element is d or a possible child of d
if ((e.x >= d.x && e.x < (d.x + d.dx)) && (e.depth >= d.depth)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(ANIMATION_DURATION)
.attr("opacity", 1)
.attr("text-anchor", textAnchorPos(depthMarker))
.attr("transform", textTransform(depthMarker))
.attr("dy", ".2em")
.attr("x", 0);
}
});
}
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i
? function(t) { return arc(d); }
: function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
};
}
}
});
return Sunburst;
});
着色发生在这里:
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {return color((d.children ? d : d.parent).name); })
.on("click", click);
因此,您可能能够检查get传递给匿名函数的元素的测试状态(在样式调用中),并返回"green"或"red"。这对叶子来说可能很直接,但对父母来说有点棘手。因为您必须通过查看父元素来发现它是否有测试失败的子元素,所以返回"红色",否则返回"绿色"。
相关文章:
- 使用JS如何动态更改显示的html文件中的文本背景颜色
- 高亮显示时编辑文本大小和颜色
- 用与线条相同的颜色填充多折线图上的点
- 更改使用Chart.js创建的图表中的轴线颜色
- 如何使用jquery更改html中的背景颜色
- 动态更改高图中的系列颜色
- 从控制器继承了隔离的作用域以生成可重用的指令
- jsf中两个字符串的颜色代码差异
- 两个指令创建新的继承的和隔离的作用域-元素得到哪个
- 我们如何使用css或JavaScript在i/j上更改句点(点)的颜色
- d3.hexbin插件-动态定义颜色域以适应数据
- 我如何制作一个JS函数,它可以从相似的原始颜色双向更改为某个颜色
- 每个选择器的Jquery css颜色都在变化,但字体大小却没有变化
- Javascript对象类在单击时打开窗口进行颜色选择,并在更改时替换对象背景颜色
- jQuery动画-边框宽度和颜色
- JS幻灯片与CSS背景颜色变化
- 使用CSS/JavaScript更改剑道图工具提示文本颜色
- 当使用控制器作为语法时,如何从父指令继承属性
- 级联非继承属性(例如:(文本颜色)到网页中的子元素,而不显式地告诉子元素继承父元素
- D3阳光爆发:通过数据确定叶子的颜色,并让其父代继承该颜色