背景
- 为什么选择@antv/x6?
由于x6提供了一套强大友好的流程图API,并且支持使用 React、Vue 组件来渲染节点。这样可以使用组件开发的方式去快速完成卡片开发,并实现更加复杂的业务逻辑。 - 遇到了Safari的兼容问题
由于x6自定义节点基于SVG foreignObject节点去实现的,但是Safari对foreignObject的实现不够完整,这就出现了在safari上出现了无法设置节点的位置,有些内容展示不出来的问题。
以下是x6官网上Vue使用案例在Chrome和Safari上的展示对比:
Chrome 效果
Safari 效果
卡片跑到了左上角位置,设置的卡片位置完成无效了
参考:
Safari上SVG foreignObject中<video>元素的位置错误
解决Safari浏览器使用SVG foreignObject 不生效,错位的问题
解决方式
一开始想的是有没有对应的polyfill去解决foreignObject的兼容问题,但是没有找到。后面发现了x6-html-shape这个库,采用了html节点的方式,避开了foreignObject。
x6-html-shape原理解析
- 节点绘制
x6-html-shape通过创建一个html容器节点覆盖在svg画布的上层,使用绝对定位+transform将自定义节点绘制到这个html容器内,在每个节点的下层会对应相关的svg空节点。这样避免了使用Svg foreignObject的方式。
- 事件处理
对于画布上的事件通过添加pointer-events:none
样式,使事件透传。卡片上事件通过监听直接转发到下层的svg节点。核心代码如下:
ts
Dom.css(htmlContainer, {
position: "absolute",
width: "100%",
height: "100%",
"touch-action": "none",
"user-select": "none",
"pointer-events": "none",
'z-index': 0,
'transform-origin': 'left top',
});
htmlContainer.classList.add("x6-html-shape-container");
ts
Dom.css(container, {
"pointer-events": "auto",
"touch-action": "none",
"user-select": "none",
"transform-origin": "center",
position: "absolute",
})
container.classList.add("x6-html-shape-node");
// forward events
const events = "click,dblclick,contextmenu,mousedown,mousemove,mouseup,mouseover,mouseout,mouseenter,mouseleave".split(",");
events.forEach((eventType) =>
forwardEvent(eventType, container, this.container)
);
this.graph.htmlContainer.append(container);
container 就是自定义的卡片容器,卡片内容将放在里面, this.container能获取到对应的svg节点。这里container的pointer-events
设置成了auto
保证了卡片上的事件能正常触发。
ts
export function forwardEvent(eventType, fromElement, toElement) {
fromElement.addEventListener(eventType, function (event) {
toElement.dispatchEvent(new event.constructor(event.type, event));
event.preventDefault();
event.stopPropagation();
});
}
通过调用forwardEvent将自定义卡片上的事件转发到svg节点上。
使用中的问题
可能由于这个库,使用的人并不多,很多问题没有被发现,我在实际使用的时候也遇到了以下的问题:
- 缩放后节点错位,节点并没有合理缩放
- 卡片上不能滚动问题
- 节点删除后,下层对应的svg节点未删除
- 无法显示或隐藏节点问题
- 节点较多时缩放失效问题
- 卡片上点击功能按钮el-popover不展示问题(开发卡片时阻止按钮的事件冒泡行为即可)
由于原作者精力有限,忙于其他事情。我就自己主动提交了PR,参与到了这个项目的贡献。后续作者也给开通了github代码提交权限和npm包的发布权限,以上问题都得到了解决。
总结
总体来说,x6-html-shape很好的解决了x6使用SVG foreignObject绘制自定义节点在Safari上的兼容问题。这个库的解决思路非常不错,希望能帮助到更多的人。当然如果Safari能把foreignObject的功能给完善一下就更好了。