数据统计
这两天我都把知识图谱的界面一直打开着,用到什么东西如果有必要都加到知识图谱里面,现在看起来已经初具规模了。
但是我也不知道我加了几个节点。所以数据统计功能肯定必不可少啦。
直接一个 div 放到屏幕左下角,调整下边距和颜色:
<div class="absolute bottom-0 left-0 z-20 text-sm text-gray-500 pl-4 pb-2">
{{ nodes.length }} nodes / {{ edges.length }} edges
</div>
看看效果:
完美,我知道我现在有 101 个节点 和 120 个边了。
搜索节点和定位
随着日常的对知识图谱的维护,节点数量会越来越多,图谱变得越来越复杂,想找某个东西就变得越来越困难。就比如我的 101 个节点,我想从中找到我想要的节点就很困难:
搜索节点
一个一个点去找肯定不可能的,那就给知识图谱加上搜索功能。
搜索功能的设计也很简单,我决定搞一个下拉框,把所有节点的数据作为下拉选项,根据名称搜索。放到屏幕的左上角,然后把这这一排按钮往后放一放。
效果出来了:
搜索的时候可以根据关键词快速过滤,也可以直接滚动选择选择:
节点定位
搜索完之后,我发现搜索了个寂寞,图谱啥反应没有,这不成,就像军训教官点名一样,叫到谁谁立刻马上给我出列才对。
不过从交互上来说,让节点出列是不太好办,不过我们可以让 3d 场景的镜头对准选中的节点,并且给定位的节点一个高亮。
我们借用一下 force-3d 官网 Examaple 的代码:
const onNodeClick = (node, event = {}) => {
const distance = 200
const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z)
const newPos =
node.x || node.y || node.z
? { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }
: { x: 0, y: 0, z: distance }
g.value.cameraPosition(newPos, node, 3000)
g.value.nodeColor(g.value.nodeColor())
}
看下效果:
图谱样式优化之路
图谱用着用着我就发现还是有点儿不太尽如人意,于是便进行了一系列折腾。
只显示文本
有时候我想只看文本,圆点有点儿干扰我的视线了,于是调整 nodeThreeObjectExtend 参数:
Graph
.nodeThreeObject((node) => getSprite(node))
.nodeThreeObjectExtend(false)
把镜头拉进一点看看:
文本颜色加量
文本颜色有点儿浅,调亮一点:
Graph.nodeThreeObject((node) => {
const sprite = new SpriteText(node.name)
sprite.color = '#FFFFFF'
return sprite
})
文本换色
看了一会儿看腻了,又想换个颜色:
Graph.nodeThreeObject((node) => {
const sprite = new SpriteText(node.name)
sprite.color = '#0ca1c0'
return sprite
})
青色对比更强一点儿,青花瓷?
没几分钟又看腻了,觉得红色的更酷一点:
Graph.nodeThreeObject((node) => {
const sprite = new SpriteText(node.name)
sprite.color = '#e62c9b'
return sprite
})
红色果然酷一点,就是有点儿偏粉色怎么回事儿。
字体大小
颜色先不折腾了,用着用着觉得字太小了,调大一点:
Graph.nodeThreeObject((node) => {
const sprite = new SpriteText(node.name)
sprite.material.depthWrite = false
sprite.textHeight = 20
sprite.color = nodeCtrl.value.textColor
return sprite
})
这样不那么费眼睛了。
这篇文章正文字体我也悄悄调大了一丁点儿
不显示文字
又过了一会儿,我发现我只想看球球,可能截图更好看,于是乎:
Graph
.nodeThreeObject((node) => false)
.nodeThreeObjectExtend(true)
还不错,回到我最初的样子了。
球体发光
球球不够炫酷,灰不拉机的,给球球打个光吧:
const bloomPass = new UnrealBloomPass()
bloomPass.strength = 1
bloomPass.radius = 1
bloomPass.threshold = 0
Graph.postProcessingComposer().addPass(bloomPass)
bloomPass 效果我当然不会写,还是借官网 Examples 的用一用。
球体大小
看久了觉得球球太小了,把球球放大一点:
Graph.nodeVal((node: NodeValue) => {
return 50
})
这有点儿像二维 Chart 里面的气泡图了。
动态大小
每一个节点的权重不一样,有的关系多,有的关系少。每个球都一样的大小不合适,根据关系多少决定大小更好一点:
Graph.nodeVal((node: NodeValue) => {
const count = node.links ? node.links.length : 1
return 10 * count
})
效果如下:
可能是数据少,效果不是那么明显,不过也还不错。
动态调整显示属性
优化了这么多样式,不停的改代码,虽然不难,但是挺烦人的。
一劳永逸的解决这个问题,还是要在界面上让用户(也就是我自己)动态设置显示属性。
还记得我们之前安装过 dat.GUI[1],这个前端库么, 这是我之前在做互动艺术相关程序是经常用到的一个工具。如今可以派上用场了。
dat.GUI 提供了一个很棒的控制器 UI,可以方便的编辑颜色、文本、复选框、数字等属性。
把刚才改动的样式,都作为变量加到控制器里面。
const gui = new GUI({ name: '知识图谱控制器' })
const folder = gui.addFolder('节点')
folder.add(nodeCtrl.value, 'size', 0, 200)
folder.add(nodeCtrl.value, 'dynamicSize')
folder.add(nodeCtrl.value, 'showCircle')
folder.add(nodeCtrl.value, 'showText')
folder.addColor(nodeCtrl.value, 'textColor')
folder.add(nodeCtrl.value, 'textSize', 1, 50)
folder.add(nodeCtrl.value, 'textToFront')
folder.add(nodeCtrl.value, 'bloom')
folder.onChange(updateNodeStyle)
folder.open()
调整下位置放到搜索框下面,同时增加一个半透明效果:
最后看看控制器的效果:
其他交互提升
界面交互其实还有很多可以优化的地方,比如:
• 区分节点级别,对不同节点展示不同的效果;
• 增加过滤功能,按需要展示部分节点;
• 显示样式还是比较平淡,可以增加更好的3d 效果;
• 连线的颜色和粗细也可以动态调整;
• 可以对连线增加动画效果,让关系更加清楚;
• 字体现在在球球里面,可以在球球外面显示,看的更清楚;
• 对节点增加各种布局;
• 。。。
当然优化永无止尽的,很多时候我们主打一个够用就行。后期我也分享一些企业级知识图谱的特效和交互, 关于界面交互大家有新的想法可以留言或者私信跟我交流。
引用链接
[1] dat.GUI: https://github.com/dataarts/dat.gui
--- END ---

