D3.js绘制飞线总结

使用D3 V3版本绘制

项目中多次遇到飞线的需求,故此抽个时间做个总结。本篇飞线demo效果:
在这里插入图片描述

主要思路:

  1. 先绘制一条由起点到终点的弧线;
  2. 绘制弧线由起始点增长至终点效果;
  3. 设置蒙版,位置跟随弧线起点移动,则飞线始终显示蒙版内大小,营造出飞线效果。

准备知识:

  1. 贝塞尔曲线的绘制;
  2. 二阶贝塞尔曲线控制点的计算;
  3. svg蒙版的使用。
1. 绘制贝塞尔曲线

此处由二阶贝塞尔绘制弧形曲线效果。二阶贝塞尔曲线命令:

Q x1 y1, x y (或 q dx1 dy1, dx dy)
其中:x1 y1 为控制点坐标,x y为终点坐标

绘制命令及效果如下:
在这里插入图片描述

  <path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
2. 二阶贝塞尔曲线控制点的计算

方法一
在这里插入图片描述

上面图形中求控制点坐标的方法有很多种,下面提供其中一种思路:

  1. 求出已知两点斜率K = y 2 − y 1 x 2 − x 1 \frac{y_2-y_1} {x_2-x_1} x2x1y2y1,则控制点与垂线的斜率为 - 1/K ;
  2. 求出AB中点O( x o , y o x_o,y_o xo,yo)的坐标( x 1 + x 2 2 , y 1 + y 2 2 \frac{x_1+x_2} {2},\frac{y_1+y_2} {2} 2x1+x2,2y1+y2),根据两点间距离公式OC = H,得 ( x c − x o ) 2 + ( y c − y o ) 2 2 \sqrt[2]{(x_c-x_o)^2+(y_c-y_o)^2} 2(xcxo)2+(ycyo)2 = H;
  3. 由1、2列出方程求出点C坐标( x c , y c x_c,y_c xc,yc)。

方法二
在这里插入图片描述
使用O点坐标加上△x,△y的方法计算C点坐标。

  function computeControlPoint(ps, pe, arc = 0.5) {
	  const deltaX = pe[0] - ps[0];
	  const deltaY = pe[1] - ps[1];
	  const theta = Math.atan(deltaY / deltaX);
	  const len = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)) / 2 * arc;   
	  const newTheta = theta - Math.PI / 2;    
	  return [
	    (ps[0] + pe[0]) / 2 + len * Math.cos(newTheta),    
	    (ps[1] + pe[1]) / 2 + len * Math.sin(newTheta),    
	  ];
	}

( θ − π 2 ) (θ- \frac{π} {2}) (θ2π)sinθ —> - cosθ cosθ —> sinθatan(K)的取值范围为 [ − π 2 , π 2 ] [-\frac{π} {2}, \frac{π} {2}] [2π,2π];
注1:由于svg坐标系原因,图中(1)中情况时,k为负值,此时 − π 2 < θ < 0 -\frac{π} {2}<θ<0 2π<θ<0
注2:因svg坐标原点在左上方,故当△y<0时控制点一直在上侧;
注3:θ平移 π 2 \frac{π} {2} 2π 也是为了保证控制点一直位于连线上方,此时弧形向下凹陷。

1.当 ( − π 2 < x < 0 ) (-\frac{π} {2}<x<0) (2π<x<0)时,cosθ<0;sinθ<0; 即△x<0,△y<0;
2.当 ( 0 < x < π 2 ) (0<x<\frac{π} {2}) (0<x<2π)时,cosθ>0;sinθ<0;即△x>0,△y<0;

故最终视图:
在这里插入图片描述

3. 使用蒙版

使用 svg 蒙板,在蒙板中定义一个透明度从内到外逐渐降低径向渐变的圆,渲染飞线“头粗尾巴细”的效果。

<svg>
    <defs>
	    <mask id="Mask">
	          <circle id="circle" r="150" fill="url(#grad)"  />
	    </mask>
        <radialGradient
	        id="grad"
	        cx="0.5"
	        cy="0.5"
	        r="0.5" >
	        <stop offset="0%" stop-color="#fff" stop-opacity='1'/>
	        <stop offset="100%" stop-color="#fff" stop-opacity='0' />
	    </radialGradient>
	</defs>
</svg>

绘制飞线:

1. 绘制地图

获取地图路径,根据投影函数绘制地图,代码如下:

// 定义地图的投影
const projection = d3.geo.mercator()
    .center(mapConfig.center)
    .scale(mapConfig.scale);
// 定义地理路径生成器
const path = d3.geo.path()
    .projection(projection);
// 生成地图
const mapGroups = svg.append('g').attr('class', 'mapGroups');
mapGroups.selectAll('path')
    .data(mapJson.features)
    .enter()
    .append('path')
    .style('fill', mapColor)
    .attr('stroke', strokeColor)
    .attr('d', path);
2. 绘制弧线
const pointData = [];
for (let i = 0; i < dataset.length; i += 1) {
    // 计算飞线点坐标
    const startPoint = projection(mapConfig.start);
    const endPoint = projection(dataset[i].centroids);
    pointData.push({
       startPoint,
       endPoint,
       controlPoint: computeControlPoint(startPoint, endPoint, 0.5),
    });
const baseLineGroups = svg.append('g').attr('class', 'baseLineGroups');
baseLineGroups.append('path')
    .attr({
      stroke: 'none',
      fill: 'none',
      class: `line-path${i}`,
    })
    .attr('d', () => transPath(pointData[i]));
}

注:此处绘制弧线为基础弧线,做飞线动画时需以此弧线为基础获取长度,并计算各个点的坐标。

3. 飞线动画

使用 attrTween(),插入中间帧函数,不断变更 中的 d 属性,呈现出线条在“一点点绘制出来”的效果。

// 生成飞线并设置动画
lineGroups.append('path')
   .attr({
     stroke: '#FFCE00',
     'stroke-width': 3,
     fill: 'none',
     //mask: `url(#mask${i})`,
   })
   .transition()
   .duration(2500)
   .ease('linear')
   .delay(1200)
   .attrTween('d', () => {
     const $path = d3.select(`.line-path${i}`).node();
     const l = $path.getTotalLength();
     const coord = $path.getAttribute('d').replace(/(M|Q)/g, '').match(/((\d|\.)+)/g);
     const x1 = +coord[0]; const y1 = +coord[1]; // 起点
     const x2 = +coord[2]; const y2 = +coord[3]; // 控制点
     return function (t) {
       const p = $path.getPointAtLength(t * l);  // 新的终点
       const x = ((1 - t) * x1) + (t * x2); const y = ((1 - t) * y1) + (t * y2);
       // d3.select(`#circle${i}`).attr('cx', p.x).attr('cy', p.y);  // 蒙版坐标
       return `M${x1},${y1} Q${x},${y} ${p.x},${p.y}`;
     };
   })
   .transition()
   .duration(2400)
   .style('opacity', 0);
4. 飞线样式–添加蒙版
  • 圆心 cy,cx 为飞线终点;
  • 设置的半径即为可视区域;
  • 蒙板动态跟随飞线变化。

也可根据需求添加飞线终点样式。

参考文章 :
  1. svg之path详解
  2. 地图与飞线
  3. 一根飞线的故事-SVG篇
three.js飞线是一种在WebGL基础上构建的JavaScript库,用于创建和展示三维图形。它能够让你轻松地在浏览器中绘制复杂的3D模型、动画以及交互式内容。 在three.js中,“飞线”通常指的是线段或者是由多个点连接起来形成的线串。这种线条可以有各种属性,比如颜色、粗细等,并且可以根据需要调整其样式和位置。 在实际应用中,你可以利用three.js库创建出一系列动态的飞行轨迹、路径规划或者简单的物理模拟效果。例如,在游戏设计中,飞线可以用来描绘角色的移动路线;在可视化领域,它们可以用作数据追踪或者时间序列分析的辅助工具;在教育软件中,则可以作为示教机器人运动路径的演示。 要开始使用three.js制作飞线,你需要首先在HTML文件中引入three.js库,然后通过JavaScript编写脚本来初始化场景、加载几何体和材质,并最终渲染出来。基本步骤包括: 1. **设置场景** (`Scene`) - 创建包含所有可视元素的空间环境。 2. **添加相机** (`Camera`) - 观看场景的角度和位置。 3. **配置光源** (`Light`) - 确定光照情况,影响物体的颜色和阴影。 4. **创建网格或点数组** - 定义飞线的具体形状和位置。 5. **生成材质并赋予几何体** - 设定线条的颜色、透明度、粗细等视觉属性。 6. **将几何体加入场景** (`add` 方法)。 7. **更新渲染循环** (`render`) - 每帧都刷新显示的画面,让动画流畅。 如果你希望深入学习如何在three.js中操作飞线,这里有几个相关的问题可能会对你有所帮助:
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值