0%

图元拾取提示方案

背景

针对在如何在图表编辑器场景下快速索引图元所对应的方案,我们得到了ECharts图元拾取方案。本文介绍两种方案去提示用户当前拾取到的图元。

image-20230704112302547

提示框方案

以研发的视角看,提示Dom信息自然的会联想到ChromeDevtool的方案。该方案参照ChromeDevtool的提示样式,提示了元素的名称、元素类型、图元大小等信息。

Untitled5

优势与弊端

优势:展示的信息更加丰富。

弊端:提示方式过于生硬,非研发用户接受度不高。

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 创建 DomVisualSelector 对象
this.domVisualSelector = new DomVisualSelector({
// 过滤 dom 元素 只有在包含图元信息时 触发Dom提示
filter: function (e) {
const elTarget = e.target.getAttribute("elTarget");
if (elTarget) {
return true;
}
return false;
},
// 点击Dom后触发的事件(filter 通过后的)
onSelect: (e) => {
// 获取图元信息
const elTarget = e.target.getAttribute("elTarget");
if (!elTarget) {
return
}
// 解析图元信息
const editorInfo = JSON.parse(elTarget)
const { component, element } = editorInfo
const { chartName, chartType } = this.getSelectedChartInfo()
// 触发图元选择事件
this.selectChartDom(chartName, chartType, component, element)
// 筛选到具体的元素后 停止事件传播
e.stopPropagation();
},
// 提示框配置属性
tooltipOption: {
formatter: (targetElementInfo) => {
delete targetElementInfo.elementInfo.style;
// 获取图元信息
const elTarget = targetElementInfo.targetEl.getAttribute("elTarget");
// 获取图表信息
const { chartName, chartType } = this.getSelectedChartInfo()
// 解析图元信息
const editorInfo = JSON.parse(elTarget)
// 翻译图元信息
const { component, element } = translateDevInfoToHumanReadable({ chartName, chartType }, editorInfo);
// 修改提示框内容
targetElementInfo.elementInfo.tagName = component;
targetElementInfo.elementInfo.idValue = element;
}
}
})

淡入淡出方案

Apr-23-2023 20-15-21

优势与弊端

优势:提示效果更加友好,所见即所得。

弊端:提示信息过少,且提示过程的淡入淡出效果侵入了图表原有的默认交互效果。

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// 在图表渲染之后 给图表的 Dom 新增交互事件
onChartMounted: (el: HTMLElement) => {
// 鼠标移动事件
el.onmousemove = getOnMouseMoveInChartFunction(el);
// 鼠标移出事件
el.onmouseleave = getOnMouseLeaveInChartFunction(el);
}

/**
* 鼠标在图表内部移动时的回调方法
*
* 1. 实时获取当前鼠标指向的图元
* 2. 遍历图表图元
* 3. 判断图表图元和当前鼠标指向的图元是否是同一类图元
* 4. 是同一类图元则执行 高亮 策略
* 5. 不是同一类图元则执行 模糊 策略
*
* @param {*} el
* @return {*} 鼠标在图表内部移动时的回调方法
*/
export const getOnMouseMoveInChartFunction = function (el) {
// 记录上一个 选中的 图元
let preSelectedDomTargetInfo = undefined;
return function (e) {
const selectedDom = e.target;
// 获取并解析图元信息
const chartInfo = JSON.parse(el.getAttribute("elTarget") || '{}');
let selectedDomTargetInfo = selectedDom.getAttribute("elTarget");
const selectedDomTargetInfoObj = {
tagName: selectedDom.tagName,
chartTag: chartInfo.chartTag,
chartType: chartInfo.chartType,
chartOption: chartInfo.chartOption,
...JSON.parse(selectedDomTargetInfo),
};
if (isUnNeedPickDom(selectedDomTargetInfoObj)) {
selectedDomTargetInfo = null;
selectedDom.style.pointerEvents = "none";
}
// 不再同一个图元上重复触发
if (selectedDomTargetInfo !== preSelectedDomTargetInfo) {
preSelectedDomTargetInfo = selectedDomTargetInfo;
if (selectedDomTargetInfo) {
blurUnselectedElementCategory(
el,
selectedDomTargetInfoObj
);
} else {
unBlurUnselectedElementCategory(el);
}
}
};
};

/**
* 鼠标移出图表时的回调方法
*
* 将图表所有图元恢复至默认状态
*
* @param {*} el
* @return {*} 鼠标移出图表时的回调方法
*/
export const getOnMouseLeaveInChartFunction = function (el) {
return function () {
unBlurUnselectedElementCategory(el);
};
};

/**
* 将非选中的图元类别 不透明度放低
*
* @param {*} chartContextDom
*/
export const blurUnselectedElementCategory = function (
chartContextDom,
selectedDomInfo
) {
const chartSVGs = chartContextDom.querySelectorAll("svg");
for (let i = 0; i < chartSVGs.length; i++) {
svgVisitor(chartSVGs[i], {
visitor: function (dom) {
if (dom.tagName === "svg" || dom.tagName === "g") return;
let elTargets = dom.getAttribute("elTarget");
elTargets = {
chartTag: selectedDomInfo.chartTag,
chartType: selectedDomInfo.chartType,
chartOption: selectedDomInfo.chartOption,
svgName: dom.tagName,
...JSON.parse(elTargets),
};
// 判断图元类型一致 把图元的不透明度改为 1
if (
elTargets === selectedDomInfo ||
isTargetCategoryEqualDomCategory(elTargets, selectedDomInfo)
) {
easeOutTransitionAnimation(dom);
deleteOpacityEffect(dom);
} else {
// 判断图元类型不一致 改变这类图元的透明度
easeOutTransitionAnimation(dom);
fillOpacityToBlurOpacity(dom);
}
},
});
}
};

/**
* 撤销将 将非选中的图元类别 不透明度放低 的操作
*
* @param {*} chartContextDom
*/
export const unBlurUnselectedElementCategory = function (chartContextDom) {
const chartSVGs = chartContextDom.querySelectorAll("svg");
for (let i = 0; i < chartSVGs.length; i++) {
svgVisitor(chartSVGs[i], {
visitor: function (dom) {
deleteOpacityEffect(dom);
},
});
}
};

参考 & 引用

ECharts图元拾取方案 | SZ 博客 (sz-p.cn)

图表元素拾取扩大热区范围方案 | SZ 博客 (sz-p.cn)

SVG后处理 | SZ 博客 (sz-p.cn)