blob: 24603c8f3da1128a5c6e2e3105fc0e313ecd8de5 [file] [log] [blame]
/*
title: Circle Packing with d3
category: custom
titleCN: Circle Packing with d3
difficulty: 11
*/
$.when(
$.get(ROOT_PATH + '/data/asset/data/option-view.json'),
$.getScript(
'https://fastly.jsdelivr.net/npm/d3-hierarchy@2.0.0/dist/d3-hierarchy.min.js'
)
).done(function (res) {
run(res[0]);
});
function run(rawData) {
const dataWrap = prepareData(rawData);
initChart(dataWrap.seriesData, dataWrap.maxDepth);
}
function prepareData(rawData) {
const seriesData = [];
let maxDepth = 0;
function convert(source, basePath, depth) {
if (source == null) {
return;
}
if (maxDepth > 5) {
return;
}
maxDepth = Math.max(depth, maxDepth);
seriesData.push({
id: basePath,
value: source.$count,
depth: depth,
index: seriesData.length
});
for (var key in source) {
if (source.hasOwnProperty(key) && !key.match(/^\$/)) {
var path = basePath + '.' + key;
convert(source[key], path, depth + 1);
}
}
}
convert(rawData, 'option', 0);
return {
seriesData: seriesData,
maxDepth: maxDepth
};
}
function initChart(seriesData, maxDepth) {
var displayRoot = stratify();
function stratify() {
return d3
.stratify()
.parentId(function (d) {
return d.id.substring(0, d.id.lastIndexOf('.'));
})(seriesData)
.sum(function (d) {
return d.value || 0;
})
.sort(function (a, b) {
return b.value - a.value;
});
}
function overallLayout(params, api) {
var context = params.context;
d3
.pack()
.size([api.getWidth() - 2, api.getHeight() - 2])
.padding(3)(displayRoot);
context.nodes = {};
displayRoot.descendants().forEach(function (node, index) {
context.nodes[node.id] = node;
});
}
function renderItem(params, api) {
var context = params.context;
// Only do that layout once in each time `setOption` called.
if (!context.layout) {
context.layout = true;
overallLayout(params, api);
}
var nodePath = api.value('id');
var node = context.nodes[nodePath];
if (!node) {
// Reder nothing.
return;
}
var isLeaf = !node.children || !node.children.length;
var focus = new Uint32Array(
node.descendants().map(function (node) {
return node.data.index;
})
);
var nodeName = isLeaf
? nodePath
.slice(nodePath.lastIndexOf('.') + 1)
.split(/(?=[A-Z][^A-Z])/g)
.join('\n')
: '';
var z2 = api.value('depth') * 2;
return {
type: 'circle',
focus: focus,
shape: {
cx: node.x,
cy: node.y,
r: node.r
},
transition: ['shape'],
z2: z2,
textContent: {
type: 'text',
style: {
// transition: isLeaf ? 'fontSize' : null,
text: nodeName,
fontFamily: 'Arial',
width: node.r * 1.3,
overflow: 'truncate',
fontSize: node.r / 3
},
emphasis: {
style: {
overflow: null,
fontSize: Math.max(node.r / 3, 12)
}
}
},
textConfig: {
position: 'inside'
},
style: {
fill: api.visual('color')
},
emphasis: {
style: {
fontFamily: 'Arial',
fontSize: 12,
shadowBlur: 20,
shadowOffsetX: 3,
shadowOffsetY: 5,
shadowColor: 'rgba(0,0,0,0.3)'
}
}
};
}
option = {
dataset: {
source: seriesData
},
tooltip: {},
visualMap: [
{
show: false,
min: 0,
max: maxDepth,
dimension: 'depth',
inRange: {
color: ['#006edd', '#e0ffff']
}
}
],
hoverLayerThreshold: Infinity,
series: {
type: 'custom',
renderItem: renderItem,
progressive: 0,
coordinateSystem: 'none',
encode: {
tooltip: 'value',
itemName: 'id'
}
}
};
myChart.setOption(option);
myChart.on('click', { seriesIndex: 0 }, function (params) {
drillDown(params.data.id);
});
function drillDown(targetNodeId) {
displayRoot = stratify();
if (targetNodeId != null) {
displayRoot = displayRoot.descendants().find(function (node) {
return node.data.id === targetNodeId;
});
}
// A trick to prevent d3-hierarchy from visiting parents in this algorithm.
displayRoot.parent = null;
myChart.setOption({
dataset: {
source: seriesData
}
});
}
// Reset: click on the blank area.
myChart.getZr().on('click', function (event) {
if (!event.target) {
drillDown();
}
});
}