Github Repository 可视化 (D3.js & Three.js)

Github Repository 可视化 (D3.js & Three.js)

先上 Demo 链接 & 效果图
demo 链接
github 链接

效果图 2D:
demo 2d

效果图 3D:
demo 3d

为什么要做这样一个网站?

最初想法是因为 github 提供的页面无法一次看到用户的所有 repository, 也无法直观的看到每个 repository 的量级对比(如 commit 数, star 数),

所以希望做一个能直观展示用户所有 repository 的网站.

实现的功能有哪些?

用户 Github Repository 数据的2D3D展示, 点击用户 github 关注用户的头像, 可以查看他人的 Github Repository 展示效果.

2D 和 3D 版本均支持:

  • 展示用户的 Repository 可视化效果
  • 点击 following people 的头像查看他人的 Repository 可视化效果

其中 2D 视图支持页面缩放和拖拽 && 单个 Repository 的缩放和拖拽, 3D 视图仅支持页面的缩放和拖拽.

用到了哪些技术?

  • 数据来源为 Github 提供的 GraphQL API.
  • 2D 实现使用到了 D3.js
  • 3D 实现使用到了 Three.js
  • 页面搭建使用 Vue.js

实现细节?

2D 实现

2D 效果图中, 每一个 Repository 用一个圆形表示, 圆形的大小代表了 commit 数目 || start 数目 || fork 数目.

布局使用的是 d3-layout 中的 forceLayout, 达到模拟物理碰撞的效果. 拖拽用到了 d3-drag 模块, 大致逻辑为:

==> 检测鼠标拖拽事件

==> 更新 UI 元素坐标

==> 重新计算布局坐标

==> 更新 UI 来达到圆形可拖拽的效果.

让我们来看看具体代码:

2D 页面依赖 D3.js 的 force-layout 进行动态更新, 我们为 force-layout 添加了以下几种 force(作用力):

  • .force('charge', this.$d3.forceManyBody()) 添加节点之间的相互作用力
  • .force('collide',radius) 添加物理碰撞, 半径设置为圆形的半径
  • .force('forceX', this.$d3.forceX(this.width / 2).strength(0.05)) 添加横坐标居中的作用力
  • .force('forceY', this.$d3.forceY(this.height / 2).strength(0.05)) 添加纵坐标居中的作用力

主要代码如下:

this.simulation = this.$d3
  .forceSimulation(this.filteredRepositoryList)
  .force('charge', this.$d3.forceManyBody())
  .force(
    'collide',
    this.$d3.forceCollide().radius(d => this.areaScale(d.count) + 3)
  )
  .force('forceX', this.$d3.forceX(this.width / 2).strength(0.05))
  .force('forceY', this.$d3.forceY(this.height / 2).strength(0.05))
  .on('tick', tick)

最后一行 .on('tick', tick) 为 force-layout simulation 的回调方法, 该方法会在物理引擎更新的每个周期被调用, 我们可以在这个回调方法中更新页面, 以达到动画效果.

我们在这个 tick 回调中要完成的任务是: 刷新 svgcirclehtmlspan 的坐标. 具体代码如下.
如果用过 D3.js 的同学应该很熟悉这段代码了, 就是使用 d3-selection 对 DOM 元素 enter(), update(), exit() 三种状态进行的简单控制.

这里需要注意的一点是, 我们没有使用 svgtext 元素来实现文字而是使用了 htmlspan, 目的是更好的控制文字换行.

const tick = function() {
   
  const curTransform = self.$d3.zoomTransform(self.div)
  self.updateTextLocation()
  const texts = self.div.selectAll('span').data(self.filteredRepositoryList)
  texts
    .enter()
    .append('span')
    .merge(texts)
    .text(d => d.name)
    .style('font-size', d => self.textScale(d.count) + 'px')
    .style(
      'left',
      d =>
        d.x +
        self.width / 2 -
        ((self.areaScale(d.count) * 1.5) / 2.0) * curTransform.k +
        'px'
    )
    .style(
      'top',
      d => d.y - (self.textScale(d.count) / 2.0) * curTransform.k + 'px'
    )
    .style('width', d => self.areaScale(d.count) * 1.5 + 'px')
  texts.exit().remove()

  const repositoryCircles =
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值