可视化效果之飞线

706 阅读2分钟

在数据可视化中,飞线图是十分常见的,迁徙图、路线图、消息传递图等等数不胜数。是十分重要的可视化手段之一,下面就来介绍一下飞线的实现方式。

博主知道有三种实现方式:

1、粒子动效

所谓粒子动效,本质就是使用大量的点充当粒子,将这些粒子由大到小排列,沿着指定路线不断更新粒子的位置,这样就使线在视觉角度上动起来了,形成飞线效果。

粒子动效主要是依靠着色器来实现,

曲线:使用threejs根据坐标生成一条曲线,threejs中提供了许多曲线的插值结果,样条曲线、贝塞尔曲线等等,详细可以看官方文档,根据文档要求组织输入参数,生成曲线作为路径。

粒子:生成粒子,这步可以使用曲线对象的getPoints方法,根据曲线生成若干点作为粒子。

着色器:控制粒子颜色,从大到小排列以及粒子运动起来,这些操作主要靠着色器来实现,下面给出着色器代码:

顶点着色器:

  attribute float percent;
  uniform float time;
  uniform float number;
  uniform float speed;
  uniform float length;
  uniform float size;
  varying float opacity;

  void main() {
    float l = clamp(1.0 - length, 0.0, 1.0);
    gl_PointSize = clamp(fract(percent * number + l - time * number * speed) - l, 0.0, 1.0) * size * (1.0 / length);
    opacity = gl_PointSize / size;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }

片元着色器:

  varying float opacity;
  uniform vec3 color;
  void main(){
    if(opacity <= 0.3) {
      discard;
    } else {
      gl_FragColor = vec4(color, 1.0);
    }
  }

生成Mesh:使用着色器生成ShaderMaterial材质,将之前生成的粒子做成BufferGeometry,生成Mesh加到Scene中。

在render函数中修改material.uniform.time.value的值即可完成动效。

效果参考之前分享的地址。

2、TubeGeometry

第二种方式是使用three提供的管道缓冲集合体TubeGeometry。顾名思义,他是用来将三维曲线做成管道的几何对象。

首先,也需要生成曲线,与之前介绍的一样,使用threejs生成一条三维样条曲线。

  const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-10, 0, 10),
    new THREE.Vector3(-5, 5, 5),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(5, -5, 5),
    new THREE.Vector3(10, 0, 10),
  ]);

使用曲线生成TubeGeometry,并使用贴图生成材质。

  const geometry = new THREE.TubeGeometry( curve, 20, 1, 8, false );
  const material = new THREE.MeshBasicMaterial({
    color: new THREE.Color(0xffffff),
    map: texture,
    transparent: true,
  });

使用geometry与material生成Mesh,放入scene中。

在render函数中修改texture.offset.y属性,即可实现飞线动效。

3、meshline

meshline是一个扩展库,可以帮助我们绘制线,使用meshline生成飞线非常简单方便。

let curve = new THREE.CatmullRomCurve3(resPoints);
const vertices = curve.getPoints(50);
const geo = new THREE.BufferGeometry().setFromPoints(vertices);
const meshLine = new MeshLine();
meshLine.setGeometry(geo);
const material = new MeshLineMaterial({
  useMap: 1,
  color: new THREE.Color(0xff0000),
  lineWidth: 1,
  transparent: true,
  dashArray: 2,
  map: texture,
});

const mesh = new THREE.Mesh(meshLine, material);

在render函数中改变.material.uniforms.dashOffset.value的值即可实现飞线动效。