refactor: menu creation; feat: legend menu, legend size, legend layout formatter, legend category

This commit is contained in:
Yanyan-Wang 2018-07-24 13:56:29 +08:00
parent 8fc2ba766f
commit 5a5c68a621
7 changed files with 214 additions and 116 deletions

View File

@ -3,102 +3,122 @@
"id": "1965",
"userview": 606,
"pageview": 1723,
"stayTime": 40
"stayTime": 40,
"class" : "a"
}, {
"id": "1966",
"userview": 627,
"pageview": 907,
"stayTime": 11
"stayTime": 11,
"class" : "a"
}, {
"id": "1967",
"userview": 56,
"pageview": 107,
"stayTime": 10
"stayTime": 10,
"class" : "a"
}, {
"id": "1968",
"userview": 31,
"pageview": 40,
"stayTime": 10
"stayTime": 10,
"class" : "a"
}, {
"id": "2003",
"userview": 31,
"pageview": 46,
"stayTime": 28
"stayTime": 28,
"class" : "a"
}, {
"id": "2060",
"userview": 5,
"pageview": 8,
"stayTime": 7
"stayTime": 7,
"class" : "a"
}, {
"id": "2303",
"userview": 117,
"pageview": 299,
"stayTime": 3
"stayTime": 3,
"class" : "a"
}, {
"id": "2304",
"userview": 35,
"pageview": 96,
"stayTime": 2
"stayTime": 2,
"class" : "a"
}, {
"id": "2316",
"userview": 1258,
"pageview": 2011,
"stayTime": 5
"stayTime": 5,
"class" : "a"
}, {
"id": "3041",
"userview": 759,
"pageview": 3337,
"stayTime": 5
"stayTime": 5,
"class" : "a"
}, {
"id": "3042",
"userview": 64,
"pageview": 246,
"stayTime": 7
"stayTime": 7,
"class" : "c"
}, {
"id": "3077",
"userview": 404,
"pageview": 1422,
"stayTime": 4
"stayTime": 4,
"class" : "c"
}, {
"id": "3078",
"userview": 8,
"pageview": 10,
"stayTime": 2
"stayTime": 2,
"class" : "c"
}, {
"id": "3081",
"userview": 134,
"pageview": 506,
"stayTime": 8
"stayTime": 8,
"class" : "c"
}, {
"id": "3083",
"userview": 24,
"pageview": 70,
"stayTime": 12
"stayTime": 12,
"class" : "c"
}, {
"id": "3084",
"userview": 5,
"pageview": 7,
"stayTime": 31
"stayTime": 31,
"class" : "b"
}, {
"id": "3328",
"userview": 31,
"pageview": 44,
"stayTime": 11
"stayTime": 11,
"class" : "b"
}, {
"id": "3413",
"userview": 130,
"pageview": 215,
"stayTime": 4
"stayTime": 4,
"class" : "b"
}, {
"id": "3589",
"userview": 35,
"pageview": 52,
"stayTime": 35
"stayTime": 35,
"class" : "b"
}, {
"id": "_activity_fd_75660",
"userview": 190,
"pageview": 279,
"stayTime": 30
"stayTime": 30,
"class" : "b"
}],
"edges": [{
"source": "1965",

View File

@ -9,52 +9,12 @@
<script src="../build/plugin.tool.mapper.js"></script>
<script src="../build/plugin.tool.minimap.js"></script>
<script src="./assets/jquery-3.2.1.min.js"></script>
<style>
ul,
li {
margin: 0;
padding: 0;
}
#myMenu {
color: #777;
list-style: none;
width: 150px;
border: 1px solid #ccc;
border-bottom: none;
position: absolute;
display: none;
background-color: #fff
}
#myMenu li {
padding: 5px 10px;
cursor: pointer;
}
#myMenu li:hover {
color: #6af
}
#menu_detail {
color: #6af;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
</style>
</head>
<body>
<div id='mountNode'></div>
<div id='legend'></div>
<ul id="myMenu">
<li id="menu_sources">来源</li>
<li id="menu_targets">去向</li>
<li id="menu_both">来源去向</li>
<li id='menu_detail'> 查看单页分析详情</li>
</ul>
<div id="minimap" style="border: 1px solid #999; position: absolute; top: 0px;"></div>
<script>
var graph = null;
@ -68,7 +28,7 @@
const maxSpanningForest = new MaxSpanningForestPlugin({
layoutCfg: {
max_iteration: 600,
kg: 10,
kg: 1,
prev_overlapping: true,
onLayoutComplete: function () {
const minimap = document.getElementById('minimap');
@ -81,6 +41,8 @@
const nodeSizeMapper = new Mapper('node', 'userview', 'size', [20, 50], {
legendCfg: {
containerId: 'legend',
legendTitle: 'UV',
legendLayout: 'vertical'
}
});
const edgeSizeMapper = new Mapper('edge', 'userview', 'size', [1, 16], {
@ -89,6 +51,8 @@
const nodeColorMapper = new Mapper('node', 'stayTime', 'color', ['#BAE7FF', '#1890FF', '#0050B3'], {
legendCfg: {
containerId: 'legend',
legendTitle: 'Stay Time',
legendLayout: 'vertical'
}
});
const minimapPlugin = new G6.Plugins['tool.minimap']({
@ -110,6 +74,7 @@
},
height: 600,
});
graph.createMenu();
graph.read(data);
const minimap = document.getElementById('minimap');
const legend = document.getElementById('legend');

View File

@ -18,9 +18,12 @@ parameter for this plugin:
- prev_overlapping: whether preventing the node overlapping
- onLayoutComplete: a listener for layout completement. When the layout is complete, the loading div and img disappear.
To navigate a node by id or item, if this item is not in the view, the whole graph will translate a shortest distance to make the node in the view:
To navigate an item (a node or edge) by id or item, if this item is not in the view, the whole graph will translate a shortest distance to make the node in the view:
graph.activeItem(item); // item or id
graph.navigateNode(item); // item or id
graph.navigate(item); // item or id
To create the menu which follows the mouse click:
graph.createMenu(func); //func is the onclick listener for the li '查看单页分析详情'
## use

View File

@ -56,6 +56,7 @@ class Plugin {
graph.set('layout', this.layout);
}
this.graph.activeItem = this.activeItem;
this.graph.createMenu = this.createMenu;
});
graph.on('beforerender', () => {
const data = graph.getSource();
@ -90,6 +91,44 @@ class Plugin {
this.setListener();
});
}
createMenu(detailListener) {
const hover_color = '#6af';
const custom_color = '#777';
const li_style = 'padding: 5px 10px; cursor: pointer;';
const menuHtml = `<ul id="menu" style = "
color: ` + custom_color + `;
list-style: none;
width: 150px;
border: 1px solid #ccc;
position: absolute;
display: none;
background-color: #fff">
<li id="menu_sources" class = "menu_li" style = "` + li_style + ` color: #777;">来源</li>
<li id="menu_targets" class = "menu_li" style = "` + li_style + ` color: #777;">去向</li>
<li id="menu_both" class = "menu_li" style = "` + li_style + ` color: #777;">来源去向</li>
<li id='menu_detail' class = "menu_li" style = "` + li_style + `
color: #6af;
border-top: 1px solid #ccc;">查看单页分析详情</li></ul>`;
const menu = Util.createDOM(menuHtml);
const body = document.getElementsByTagName('body')[0];
body.appendChild(menu);
const lis = document.getElementsByClassName('menu_li');
for (let i = 0; i < lis.length - 1; i += 1) {
lis[i].addEventListener('mouseover', function() {
this.style.setProperty('color', hover_color);
});
lis[i].addEventListener('mouseout', function() {
this.style.setProperty('color', custom_color);
});
}
if (detailListener !== undefined) {
const detail_menu = document.getElementById('menu_detail');
detail_menu.addEventListener('click', detailListener);
}
}
setStyle() {
const graph = this.graph;
const data = graph.getSource();
@ -127,7 +166,7 @@ class Plugin {
}
activeItem(item) {
if (Util.isString(item)) {
this.find(item);
item = this.find(item);
}
let style = {};
if (item.type === 'node') {
@ -137,13 +176,32 @@ class Plugin {
shadowColor: '#6a80aa',
shadowBlur: 20
};
} else if (item.type === 'edge') {
} else if (item.type === 'edge edge') {
style = {
endArrow: true,
stroke: '#000',
strokeOpacity: 0.65
};
} else return;
// // unactive the others
// const items = this.getItems();
// const common_nodestyle = {
// stroke: '#fff',
// lineWidth: 2
// };
// const common_edgestyle = {
// endArrow: true,
// stroke: '#4F7DAB',
// strokeOpacity: 0.65
// };
// Util.each(items, it => {
// let common_style;
// if (it.type === 'node') common_style = common_nodestyle;
// else if (it.type === 'edge') common_style = common_edgestyle;
// else return;
// this.update(it, { common_style });
// });
this.update(item, {
style
});
@ -153,7 +211,9 @@ class Plugin {
const graph = this.graph;
graph.on('mouseenter', item => {
if (item.item != null) {
graph.activeItem(item.item);
// graph.activeItem(item.item);
graph.activeItem('_activity_fd_75660');
graph.navigate('_activity_fd_75660');
}
});
graph.on('mouseleave', item => {
@ -173,7 +233,8 @@ class Plugin {
strokeOpacity: 0.65
};
break;
default: break;
default:
break;
}
}
graph.update(item.item, {
@ -181,20 +242,19 @@ class Plugin {
});
});
const menu = document.getElementById('menu');
graph.on('click', ({
shape,
item,
domEvent
}) => {
if (shape && item.isNode) {
const menu = document.getElementById('myMenu');
menu.style.display = 'block';
menu.style.left = domEvent.clientX + 'px';
menu.style.top = domEvent.clientY + 'px';
clickOnNode = item;
graph.draw();
} else {
const menu = document.getElementById('myMenu');
menu.style.display = 'none';
// restore the highlighted graph and hide the edges which are not tree edges.
graph.restoreGraph();
@ -209,7 +269,6 @@ class Plugin {
});
const menu = document.getElementById('myMenu');
menu.addEventListener('click', function(ev) {
let type = 'in';
switch (ev.target.id) {
@ -223,7 +282,7 @@ class Plugin {
type = 'bi';
break;
default:
break;
return;
}
const {
re_nodes,

View File

@ -1,16 +1,20 @@
## mapper
Associate the input range with the @antv/scale, @antv/attr and @antv/g2/src/component/legend
constructor parameters:
Associate the input range with the @antv/scale, @antv/attr and @antv/g2/src/component/legend.
Constructor parameters:
- itemType: 'node'/'edge. The type of the item being mapped.
- dim: the dimension of the edge. e.g. 'class'
- range: the range the of mapping result. e.g. [ 0, 1 ], ['#BAE7FF', '#1890FF', '#0050B3']
- channel: the visual channel. e.g.'size', 'color'
- dim: the dimension of the edge. e.g. 'class'.
- range: the range the of mapping result. e.g. [ 0, 1 ], ['#BAE7FF', '#1890FF', '#0050B3'].
- channel: the visual channel. e.g.'size', 'color'.
- othercfg:
- scaleCfg: the configuration of the scale
- legendCfg: the configuration of the legend
- scaleCfg: the configuration of the scale.
- legendCfg: the configuration of the legend.
null: no lengend.
scale: scaling the size of the legend.
formatter: a function for formatting the number label of the slider.
legendTitle: the title of the legend.
legendLayout: the layout way of the legend.'horizontal'/'vertical'
legendWdith and lengedHeight: the size of the legend. Defualt: 150*15 for horizontal layout, 15*150 for vertical layout.
## use

View File

@ -50,8 +50,8 @@ class Plugin {
* @type {object}
*/
legendCfg: {
legendTitle: '',
scale: 1
comtainerId: 'legend_container',
lengedLayout: 'horizontal' // horizontal or vertical
},
/**
@ -111,7 +111,7 @@ class Plugin {
if (Util.isNumber(data[0][dim])) {
scaleCfg.type = 'linear';
} else {
scaleCfg.type = 'ordinal';
scaleCfg.type = 'category';
}
}
return Util.upperFirst(scaleCfg.type);
@ -139,7 +139,7 @@ class Plugin {
let domain = scaleCfg.domain;
scale.range = range;
if (!domain) {
if (scaleType === 'Ordinal') {
if (scaleType === 'Category') {
domain = this._trainCategoryScale(itemType, data);
} else {
domain = this._trainNumberScale(itemType, data);
@ -178,7 +178,6 @@ class Plugin {
const graph = this.graph;
const containerId = this.legendCfg.containerId;
let legendContainer;
if (containerId === undefined) {
legendContainer = Util.createDOM('<div class="legend-container"></div>');
const container = graph.getGraphContainer();
@ -193,7 +192,7 @@ class Plugin {
height: 500
});
let legend;
if (scaleType === 'Ordinal') {
if (scaleType === 'Category') {
legend = this._createCatLegend(canvas);
} else {
if (channel === 'color') {
@ -278,6 +277,10 @@ class Plugin {
const itemType = this.itemType;
const legendCfg = this.legendCfg;
const items = [];
let lengendTitle = legendCfg.title;
if (lengendTitle === '' || lengendTitle === undefined) {
lengendTitle = this.dim;
}
const cfg = Util.mix({
items,
checkable: false
@ -288,12 +291,17 @@ class Plugin {
value: domain[i],
color: value,
type: itemType === 'node' ? 'circle' : 'line',
layout: 'vertical',
layout: legendCfg.lengedLayout,
marker: {
symbol: 'circle',
radius: 5,
fill: value
},
title: {
text: lengendTitle,
fill: '#333',
textBaseline: 'middle'
},
checked: true
});
});
@ -306,36 +314,52 @@ class Plugin {
const range = scale.range;
const domain = scale.values;
const legendCfg = this.legendCfg;
let lengendTitle = legendCfg.legendTitle;
if (lengendTitle === '') {
lengendTitle = this.dim;
let legendTitle = legendCfg.legendTitle;
if (legendTitle === '' || legendTitle === undefined) {
legendTitle = this.dim;
}
if (legendCfg.scale <= 0 || typeof legendCfg.scale === 'undefined') {
legendCfg.scale = 1;
let legendLayout = legendCfg.legendLayout;
if (legendLayout === '' || legendLayout === undefined) {
legendLayout = 'horizontal';
}
const items = [];
let legendWidth = legendCfg.lengedWidth;
let legendHeight = legendCfg.legendHeight;
if (legendWidth === null || legendWidth === undefined) {
if (legendLayout === 'horizontal') {
legendWidth = 150;
legendHeight = 15;
} else {
legendWidth = 15;
legendHeight = 150;
}
}
const items = [];
Util.each(range, (val, i) => {
const percent = (domain[i] - scale.min) / (scale.max - scale.min);
let item_text = domain[i];
if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) {
item_text = legendCfg.formatter(domain[i]);
}
items.push({
text: domain[i],
attrValue: val,
value: domain[i],
value: item_text, // the number label of the slider
scaleValue: percent
});
});
const cfg = Util.mix({
items,
layout: 'horizontal',
layout: legendLayout,
titleText: itemType,
title: {
text: lengendTitle,
text: legendTitle,
fill: '#333',
textBaseline: 'middle'
},
width: 150 * legendCfg.scale,
height: 15 // * legendCfg.scale
width: legendWidth,
height: legendHeight // * legendCfg.scale
}, legendCfg);
const legend = canvas.addGroup(Color, cfg);
@ -348,34 +372,52 @@ class Plugin {
const domain = scale.values;
const domainStep = (domain[domain.length - 1] - domain[0]) / (range.length - 1);
const legendCfg = this.legendCfg;
let lengendTitle = legendCfg.legendTitle;
if (lengendTitle === '') {
lengendTitle = this.dim;
let legendTitle = legendCfg.legendTitle;
if (legendTitle === '' || legendTitle === undefined) {
legendTitle = this.dim;
}
if (legendCfg.scale <= 0 || typeof legendCfg.scale === 'undefined') {
legendCfg.scale = 1;
let legendLayout = legendCfg.legendLayout;
if (legendLayout === '' || legendLayout === undefined) {
legendLayout = 'horizontal';
}
let legendWidth = legendCfg.lengedWidth;
let legendHeight = legendCfg.legendHeight;
if (legendWidth === null || legendWidth === undefined) {
if (legendLayout === 'horizontal') {
legendWidth = 150;
legendHeight = 15;
} else {
legendWidth = 15;
legendHeight = 150;
}
}
const items = [];
Util.each(range, (val, i) => {
const dom = domain[0] + domainStep * i;
let item_text = dom;
if (legendCfg.formatter !== undefined && legendCfg.formmater !== null) {
item_text = legendCfg.formatter(dom);
}
items.push({
text: dom,
attrValue: val * legendCfg.scale,
value: dom
attrValue: val,
value: item_text // the number label of the slider
});
});
const cfg = Util.mix({
items,
layout: 'horizontal',
layout: legendLayout,
attrType: 'size',
titleText: itemType,
title: {
text: lengendTitle,
text: legendTitle,
fill: '#333',
textBaseline: 'middle'
},
width: 150 * legendCfg.scale,
height: 15 * legendCfg.scale
width: legendWidth,
height: legendHeight
}, legendCfg);
const legend = canvas.addGroup(Size, cfg);
return legend;
@ -429,7 +471,7 @@ class Plugin {
return Scale.identity({
value: 'red'
});
case 'Ordinal':
case 'Category':
return Scale.cat({
values: domain
});
@ -437,6 +479,11 @@ class Plugin {
return Scale.linear(params);
}
}
destroy() {
this.legend.destroy();
this.legendCanvas.destroy();
this.scale.destroy();
}
}
G6.Plugins['tool.mapper'] = Plugin;

View File

@ -278,30 +278,30 @@ Mixin.AUGMENT = {
}
return this;
},
navigateNode(item) {
navigate(item) {
if (Util.isString(item)) {
item = this.find(item);
}
let dx = 0;
let dy = 0;
const rootGroup = this.get('_rootGroup');
const matrix = rootGroup.getMatrix();
if (item) {
const point = item.getCenter();
const rootGroup = this.get('_rootGroup');
const matrix = rootGroup.getMatrix();
const padding = 16;
const width = this.get('width') - padding;
const height = this.get('height') - padding;
const bbox = item.getBBox();
const node_hwidth = (bbox.maxX - bbox.minX) / 2;
const node_hheight = (bbox.maxX - bbox.minX) / 2;
if (matrix[0] * point.x < 0) {
if (matrix[0] * point.x + matrix[6] < 0) {
dx = -matrix[6] + matrix[0] * node_hwidth - matrix[0] * point.x;
} else if (matrix[0] * point.x > width) {
} else if (matrix[0] * point.x + matrix[6] > width) {
dx = -matrix[6] + width - matrix[0] * node_hwidth - matrix[0] * point.x;
}
if (matrix[0] * point.y < 0) {
if (matrix[0] * point.y + matrix[7] < 0) {
dy = -matrix[7] + matrix[0] * node_hheight - matrix[0] * point.y;
} else if (matrix[0] * point.y > height) {
} else if (matrix[0] * point.y + matrix[7] > height) {
dy = -matrix[7] + height - matrix[0] * node_hheight - matrix[0] * point.y;
}
}