柚子快報邀請碼778899分享:前端 Echarts-知識圖譜
柚子快報邀請碼778899分享:前端 Echarts-知識圖譜
Echarts-知識圖譜
demo地址
打開CodePen
效果
思路
1. 生成根節(jié)點
2. 根據(jù)子節(jié)點距離與根節(jié)點的角度關(guān)系,生成子節(jié)點坐標,進而生成子節(jié)點
3. 從子節(jié)點上按角度生成對應(yīng)的子節(jié)點
4. 遞歸將根節(jié)點與每一層級子節(jié)點連線
核心代碼
定義節(jié)點配置
function getNodeConfig() {
return {
/** 節(jié)點間距 */
nodeLine: 120,
/** 節(jié)點大小 */
nodeSize: 100,
/** 子節(jié)點間距 */
subNodeLine: 40,
/** 子節(jié)點大小 */
subNodeSize: 60
};
}
創(chuàng)建節(jié)點位置
function createNodePos({ index: i, len: iLen }) {
const { nodeLine } = getNodeConfig();
const radioDeg = (Math.PI * 2) / iLen;
const deg = i * radioDeg + Math.PI / 4;
const x = nodeLine * Math.cos(deg);
const y = nodeLine * Math.sin(deg);
const pos = { x, y };
return pos;
}
創(chuàng)建子節(jié)點位置
function createSubNodePos({ index: i, len: iLen }, { index: j, len: jLen }) {
const { nodeLine, subNodeLine } = getNodeConfig();
const radioDeg = (Math.PI * 2) / iLen;
const deg = i * radioDeg + Math.PI / 4;
const parentX = nodeLine * Math.cos(deg);
const parentY = nodeLine * Math.sin(deg);
const subRadioDeg = (Math.PI * 2) / (jLen + 1);
const subDeg = j * subRadioDeg + (Math.PI / 2) * 3 + deg;
const x = parentX + subNodeLine * Math.cos(subDeg);
const y = parentY + subNodeLine * Math.sin(subDeg);
const pos = { x, y };
return pos;
}
創(chuàng)建節(jié)點和鏈接
function initOption(root) {
root.categoryItem = categories?.[root?.category] || {};
const list = chartList || [];
const graph = {
...createNodesLinks(list, root),
categories
};
const chartOption = {
color: categories?.map((item) => item?.color),
legend: [
{
orient: 'vertical',
left: 0,
data: graph.categories.map(function (a) {
return a.name;
})
}
],
tooltip: {
formatter: (params) => {
return params?.data?.name;
}
},
animationDuration: 1500,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
force: {
repulsion: 100
},
data: graph.nodes,
links: graph.links,
categories: graph.categories,
roam: true,
label: {
show: true,
width: 36,
height: 36,
overflow: 'breakAll',
color: '#f2f2f2',
formatter: (params) => {
const { name = '', id } = params?.data || {};
const len = id === rootId ? 20 : 10;
return name?.length > len ? name?.slice(0, len) + '...' : name;
}
},
lineStyle: {
color: 'source',
curveness: 0.3
},
emphasis: {
focus: 'adjacency',
disabled: true,
lineStyle: {
width: 10
}
}
}
]
};
option = chartOption;
}
function createNodesLinks(list = [], root = {}) {
const nodes = [];
const links = [];
const { nodeSize, subNodeSize } = getNodeConfig();
nodes.push({
id: rootId,
category: 0,
name: '根節(jié)點',
...root,
symbolSize: nodeSize,
x: 0,
y: 0
});
for (let i = 0; i < list.length; i++) {
const iIndex = String(i);
const categoryItem = categories?.[i];
nodes.push({
id: iIndex,
category: i,
symbolSize: 1,
label: {
show: false
},
name: categoryItem?.name,
...createNodePos({ index: i, len: list.length })
});
links.push({
source: rootId,
target: iIndex
});
for (let j = 0; j < list[i].length; j++) {
const jIndex = `${i}.${j}`;
const jItem = _.get(list, jIndex, {});
nodes.push({
id: jIndex,
category: i,
symbolSize: subNodeSize,
...jItem,
...createSubNodePos({ index: i, len: list.length }, { index: j, len: list[i].length })
});
links.push({
source: iIndex,
target: jIndex
});
}
}
return { nodes, links };
};
初始化
function init() {
const { id, name, key } = { id: '1', name: '青霉素', key: 'drug-research' }
const category = categories?.findIndex((item) => item?.key === key);
const categoryItem = categories?.[category];
initOption({
category,
dataId: id,
name,
id: rootId
})
}
完整代碼
var dom = document.getElementById('chart-container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
const categories = [
{
name: '藥物',
color: 'rgba(0, 136, 184, 1)',
key: 'drug-research',
enumKey: 'Drug',
fieldKey: 'drug',
idKey: 'drug_uid',
nameKey: 'drug_name_cn',
nameEnKey: 'drug_name_en'
},
{
name: '靶點',
color: 'rgba(7, 214, 205, 1)',
key: 'target-spot',
enumKey: 'Target',
fieldKey: 'target',
idKey: 'target_uid',
nameKey: 'target_name'
},
{
name: '適應(yīng)癥',
color: 'rgba(236, 153, 41, 1)',
key: 'indications',
enumKey: 'Indication',
fieldKey: 'indication',
idKey: 'indication_uid',
nameKey: 'indication_name'
},
{
name: '企業(yè)',
color: 'rgba(210, 142, 200, 1)',
key: 'company',
enumKey: 'Entity',
fieldKey: 'entity',
idKey: 'entity_uid',
nameKey: 'entity_name'
},
{
name: '藥物設(shè)計技術(shù)',
color: 'rgba(255, 192, 185, 1)',
key: 'drug-tech',
enumKey: 'Tech',
fieldKey: 'tech',
idKey: 'tech_name',
nameKey: 'tech_name'
}
];
const rootId = 'root';
const serverMapData = {
"drug": [
{
"drug_uid": "1",
"drug_name_cn": "藥物1",
"drug_name_en": "藥物en"
},
{
"drug_uid": "2",
"drug_name_cn": "藥物2",
"drug_name_en": "藥物en"
},
{
"drug_uid": "3",
"drug_name_cn": "藥物3",
"drug_name_en": "藥物en"
},
{
"drug_uid": "4",
"drug_name_cn": "藥物4",
"drug_name_en": "藥物en"
},
{
"drug_uid": "5",
"drug_name_cn": "藥物5",
"drug_name_en": "藥物en"
},
],
"target": [
{
"target_uid": "1",
"target_name": "靶點1",
"target_code": [
"string"
]
},
{
"target_uid": "2",
"target_name": "靶點2",
"target_code": [
"string"
]
},
{
"target_uid": "3",
"target_name": "靶點3",
"target_code": [
"string"
]
},
{
"target_uid": "4",
"target_name": "靶點4",
"target_code": [
"string"
]
},
{
"target_uid": "5",
"target_name": "靶點5",
"target_code": [
"string"
]
},
],
"indication": [
{
"indication_uid": "1",
"indication_name": "適應(yīng)癥1",
"indication_code": [
"string"
]
},
{
"indication_uid": "2",
"indication_name": "適應(yīng)癥2",
"indication_code": [
"string"
]
},
{
"indication_uid": "3",
"indication_name": "適應(yīng)癥3",
"indication_code": [
"string"
]
},
{
"indication_uid": "4",
"indication_name": "適應(yīng)癥4",
"indication_code": [
"string"
]
},
{
"indication_uid": "5",
"indication_name": "適應(yīng)癥5",
"indication_code": [
"string"
]
},
],
"entity": [
{
"entity_uid": "1",
"entity_name": "企業(yè)1",
"entity_code": [
"string"
]
},
{
"entity_uid": "2",
"entity_name": "企業(yè)2",
"entity_code": [
"string"
]
},
{
"entity_uid": "3",
"entity_name": "企業(yè)3",
"entity_code": [
"string"
]
},
{
"entity_uid": "4",
"entity_name": "企業(yè)4",
"entity_code": [
"string"
]
},
{
"entity_uid": "5",
"entity_name": "企業(yè)5",
"entity_code": [
"string"
]
},
],
"tech": [
{
"tech_name": "技術(shù)1"
},
{
"tech_name": "技術(shù)2"
},
{
"tech_name": "技術(shù)3"
},
{
"tech_name": "技術(shù)4"
},
{
"tech_name": "技術(shù)5"
},
]
}
const chartList = categories?.map((categoryItem) => {
const dataList = serverMapData?.[categoryItem?.fieldKey] || [];
return dataList?.map((item) => {
return {
...item,
categoryItem,
dataId: item?.[categoryItem?.idKey],
name: item?.[categoryItem?.nameKey] || item?.[categoryItem?.nameEnKey]
};
});
});
init();
function init() {
const { id, name, key } = { id: '1', name: '青霉素', key: 'drug-research' }
const category = categories?.findIndex((item) => item?.key === key);
const categoryItem = categories?.[category];
initOption({
category,
dataId: id,
name,
id: rootId
})
}
function initOption(root) {
root.categoryItem = categories?.[root?.category] || {};
const list = chartList || [];
const graph = {
...createNodesLinks(list, root),
categories
};
const chartOption = {
color: categories?.map((item) => item?.color),
legend: [
{
orient: 'vertical',
left: 0,
data: graph.categories.map(function (a) {
return a.name;
})
}
],
tooltip: {
formatter: (params) => {
return params?.data?.name;
}
},
animationDuration: 1500,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
force: {
repulsion: 100
},
data: graph.nodes,
links: graph.links,
categories: graph.categories,
roam: true,
label: {
show: true,
width: 36,
height: 36,
overflow: 'breakAll',
color: '#f2f2f2',
formatter: (params) => {
const { name = '', id } = params?.data || {};
const len = id === rootId ? 20 : 10;
return name?.length > len ? name?.slice(0, len) + '...' : name;
}
},
lineStyle: {
color: 'source',
curveness: 0.3
},
emphasis: {
focus: 'adjacency',
disabled: true,
lineStyle: {
width: 10
}
}
}
]
};
console.log('chartOption', chartOption)
option = chartOption;
}
function createNodesLinks(list = [], root = {}) {
const nodes = [];
const links = [];
const { nodeSize, subNodeSize } = getNodeConfig();
nodes.push({
id: rootId,
category: 0,
name: '根節(jié)點',
...root,
symbolSize: nodeSize,
x: 0,
y: 0
});
for (let i = 0; i < list.length; i++) {
const iIndex = String(i);
const categoryItem = categories?.[i];
nodes.push({
id: iIndex,
category: i,
symbolSize: 1,
label: {
show: false
},
name: categoryItem?.name,
...createNodePos({ index: i, len: list.length })
});
links.push({
source: rootId,
target: iIndex
});
for (let j = 0; j < list[i].length; j++) {
const jIndex = `${i}.${j}`;
const jItem = _.get(list, jIndex, {});
nodes.push({
id: jIndex,
category: i,
symbolSize: subNodeSize,
...jItem,
...createSubNodePos({ index: i, len: list.length }, { index: j, len: list[i].length })
});
links.push({
source: iIndex,
target: jIndex
});
}
}
return { nodes, links };
};
function getNodeConfig() {
return {
nodeLine: 120,
nodeSize: 100,
subNodeLine: 40,
subNodeSize: 60
};
}
function createNodePos({ index: i, len: iLen }) {
const { nodeLine } = getNodeConfig();
const radioDeg = (Math.PI * 2) / iLen;
const deg = i * radioDeg + Math.PI / 4;
const x = nodeLine * Math.cos(deg);
const y = nodeLine * Math.sin(deg);
const pos = { x, y };
return pos;
}
function createSubNodePos({ index: i, len: iLen }, { index: j, len: jLen }) {
const { nodeLine, subNodeLine } = getNodeConfig();
const radioDeg = (Math.PI * 2) / iLen;
const deg = i * radioDeg + Math.PI / 4;
const parentX = nodeLine * Math.cos(deg);
const parentY = nodeLine * Math.sin(deg);
const subRadioDeg = (Math.PI * 2) / (jLen + 1);
const subDeg = j * subRadioDeg + (Math.PI / 2) * 3 + deg;
const x = parentX + subNodeLine * Math.cos(subDeg);
const y = parentY + subNodeLine * Math.sin(subDeg);
const pos = { x, y };
return pos;
}
if (option && typeof option === 'object') {
myChart.setOption(option);
}
window.addEventListener('resize', myChart.resize);
柚子快報邀請碼778899分享:前端 Echarts-知識圖譜
推薦文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。