Preface
React 和 G2Plot 都不了解的同学请先移步官网,如果你熟悉其一,可以往下看了,这篇文章主要介绍怎么将两者结合起来,在 G2Plot 提供的图表上进行富操作。
Annotations
G2Plot 提供 Annotation 作为图表的辅助元素,主要用于在图表上标识额外的标记注解,目前包括 line、text、image、html等10种类型。类型虽多,但每种类型的配置项都有一定的限制,在复杂业务场景显得很鸡肋,毕竟是 canvas 不是 html。
其它几个类型比较简单也没有太多操作空间,以 type: 'html' 为例,一个简单的辅助标记如下:
annotations: [
{
type: "html",
position: ["1995", 4.9],
html: '<p>辅助标记</p>'
}
],
从示例可以看出,html 类型是支持 html 字符串的,在 React Vue 横行的时代,我相信没有人愿意去拼接 html 字符串了,除非迫不得已。
ReactDOM
Render
既然 type: 'html' 模式支持 html 字符串,不知你是否想到 ReactDOM,配合 ReactDOM 几乎可以完美实现了,简单改造之后效果如下。
annotations: [
{
type: "html",
position: ["1995", 4.9],
html: () => {
const ele = document.createElement("div");
ReactDom.render(<Annotation />, ele);
return ele;
}
}
],
看上去已经很完美,但业务实际要复杂的多,很简单的一种情况,如果图表容器有 overflow: 'hidden' 的配置,会看到如下效果。
Annotation 被截断了,增加容器高度或是 Annotation 组件添加滚动条,往往都不是最佳解法。
CreatePortal
为了解决上述问题,让 Annotation 不受限于父容器,我们可以借助 CreatePortal 将 Annotation 渲染到任意我们期望的 DOM 树上,以 body 为例。
const getAnnotationHtml = () => {
const ele = document.createElement("div");
ele.id = "annotation-box";
ReactDOM.render(
<>
{ReactDOM.createPortal(
<Annotation />,
document.getElementsByTagName("body")[0]
)}
</>,
ele
);
return ele;
};
Annotation 正确渲染在 body 里面了,但并不是我们期望的效果,因为 Annotaion 没有渲染在 HTMLElement id['annotation-box'] 里面, 所以位置偏离了。
其实正常情况下,如果 Annotation 的内容过多,也不宜直接展示,因为太太太遮挡内容了,我们简单的添加个交互(onmousemove)。
// 全量代码请查看示例代码
annotations: [
{
type: "html",
position: ["1995", 4.9],
html: getAnnotationHtml()
}
]
const getPosition = (targetElement) => {
const { top, left, right } = targetElement.getBoundingClientRect();
// 需要考虑有滚动条时的情况
const boxTop =
top -
document.documentElement.clientTop +
document.documentElement.scrollTop;
const boxLeft =
left -
document.documentElement.clientLeft +
document.documentElement.scrollLeft;
const body = document.getElementsByTagName("body")[0];
const { width } = body.getBoundingClientRect();
const boxWdith = 230; // 容器宽度
const offsetX = width - right < boxWdith ? boxWdith - (width - right) : 0; // 考虑超出右侧的情况
return {
left: boxLeft - offsetX,
top: boxTop
};
};
const showAnnotationComponent = (e) => {
const exist = document.querySelector("#annotaion-component");
if (!exist) {
const targetElement = e.currentTarget.parentNode.getElementsByClassName(
"annotation-box"
)[0];
const { top, left } = getPosition(targetElement);
ReactDOM.render(
<>
{ReactDOM.createPortal(
<div
id="annotaion-component"
style={{ position: "absolute", left, top }}
>
<Annotation />
</div>,
document.getElementsByTagName("body")[0]
)}
</>,
targetElement
);
} else {
// todo set display
}
};
const getAnnotationHtml = () => {
const ele = document.createElement("div");
ele.innerHTML = '<span>查看详情</span><div class="annotation-box"></div>';
ele.onmousemove = (e) => {
showAnnotationComponent(e);
};
return ele;
};
当鼠标移动到查看详情上时,即使父容器设置了 overFlow: 'hidden' 也不影响,效果如下:
onmouseleave 等事件,根据业务需求处理就行了。
What's more?
其实类似问题不少,有时候可以绕过,有时则不能,当遇到时可以考虑下 CreatePortal,例如典型的 tooltip ,我们可结合 customContent 迎刃而解。
示例代码地址: codesandbox.io/s/annotatio…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!