前言:在现实生活中,看书的时候,在文本的下面画个波浪线,画个横线,是很常见的行为。本篇文章使用D3动画来实现一个给文本绘制下划线的效果,可以暂停绘制,继续绘制,重新绘制,还可以支持自定义的曲线,不过自定义曲线就需要你自己书写曲线函数,接下来就来看一下如何实现吧!
可惜文章里没办法上传视频,只能截图给大家看了。
我上传了视频资源效果演示
GitHub - d3/d3: Bring data to life with SVG, Canvas and HTML. 📊📈🎉
D3(或D3.js)是一个用于可视化数据的免费开源JavaScript库。D3不是传统意义上的图表库。它没有“图表”的概念。当您使用D3可视化数据时,您可以组合各种基元。
官网还推荐了一个高级姐妹库:
D3中的直方图可能需要50行代码,而Plot可以在一行中完成!Plot的简洁而富有表现力的API让您更专注于分析和可视化数据,而不是Web开发。
Observable Plot
D3名字含义为data-driven documents,数据驱动的文档,其中的选择和过渡模块会操作DOM,但是大部分模块是对数据进行操作。D3还可以与React、Vue和Svelte等Web框架搭配使用。
D3用于定制可视化。D3适合对于可视化效果要求比较高的场景,如果只是渲染普通的图表,建议使用Plot。
D3用于动态可视化。D3可以实现数据绑定,实现动态、交互式可视化。
下面的网站是D3示例网站:
Recent notebooks by D3
D3可以用来绘制图表和动画,功能非常的丰富,可玩性很强,今天先来分享一个动画的效果
D3是结合SVG一起使用的
d3-ease | D3 by Observable
D3 中的API都采用链式调用的方式,非常的方便进行连续的操作。
一、动画实现
(一)路径的绘制
d3.path() 用来绘制路径
d3.path
构建一个路径对象 path,通过路径对象上面的API来绘制路径
- path.moveTo(x, y) 移动画笔到特定点,不会进行绘制,用于波的起始位置的设定。
- path.lineTo(x, y) 从当前点到特定点绘制直线,用于执行绘制过程。
path.lineTo()
是绘制直线的,如果函数是一条直线,那么调用一次即可。如果要绘制波函数,则需要在无数个微小时间间隔中调用path.lineTo()
以组成具有弧度的曲线。以正弦波为例,
const yCenter = 0; // 波的y轴的坐标,相对于svg元素,控制下划线和文字的垂直距离
// 正弦波浪线
const waveAmplitude = 1.5; // 振幅
const waveLength = 8; // 波长,表示一个周期中波的传播距离
const pointsPerCycle = 10; // 每个周期点数 点数越多,曲线越平滑,但计算量也越大
const totalCycles = width / waveLength; // 总周期数
const steps = Math.floor(totalCycles * pointsPerCycle) // 总步数path.moveTo(0, yCenter);
for (let i = 0; i <= steps; i++) {const x = (i / steps) * width;const waveY = yCenter + Math.sin((x / waveLength) * Math.PI * 2) * waveAmplitude;path.lineTo(x, waveY);
}
(二)创建SVG元素
考虑到一段文字可能出现换行,所以需要动态创建SVG元素数组。通过遍历文字,计算当前文字和上一个文字的位置差异,如果两个文字离得比较远,则代表出现了换行。换行的时候就需要往下划线数组中增加一行。
然后需要给SVG元素添加波浪线
const svg = d3.select(svgEl); // 获取SVG元素
const wavePath = g.append("path").attr("class", `wavy-path-${index}`).attr("d", wavePathString) // 调用 wavePathString 生成 path.attr("stroke", props.wavyLineColor).attr("stroke-width", waveStyle.strokeWidth).attr("fill", waveStyle.fill).attr("stroke-linecap", waveStyle.strokeLinecap).attr("stroke-linejoin", waveStyle.strokeLinejoin);
(三)动画控制
由于SVG元素可能有多个,所以动画也需要维护一个数组。
const animationProgress = ref([]);
animationProgress.value.push({element: wavePath.node(), // 波浪线元素originalPathLength: pathLength, // 原始路径长度originalDuration: lineDuration, // 原始持续时间pausedOffset: pathLength, // 暂停偏移量lineIndex: index // 行索引
});
数组中存放所有的SVG元素的相关信息,通过下面的API,对SVG元素遍历进行操作
1、d3.select(selector)
选中元素目标元素,返回目标元素对象 element,链式进行动画。参数为选择器的字符串。
2、element.interrupt()
暂停动画。
3、element.transition()
这是D3动画系统的基础,用于创建一个过渡效果。它会返回一个过渡对象 transition,可以在该对象上链式调用其他过渡方法。
d3.selectAll("rect").transition() // 开始过渡
4、transition.duration()
设置动画持续时间(毫秒)。持续时间越长,动画越慢。
.duration(1000) // 1秒动画
延时时间是一个需要计算的点,因为在多行绘制的情况下,除了第一行,下面的行都需要等待上面的行的波浪线绘制完成之后再进行动画。
5、transition.delay()
.delay(function(d, i) {return i * 100; // 每个元素延迟100ms
})
6、transition.ease()
设置缓动函数,(如 <font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">d3.easeLinear</font>
、<font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">d3.easeBounce</font>
等),官网给出了不同速度下波的函数图像,斜率表示速度。
element.transition().duration(duration).ease(d3.easeLinear) // 匀速
7、transition.on()
监听函数
<font style="color:rgb(224, 70, 0);background-color:rgba(142, 150, 170, 0.14);">start</font>
- 动画开始<font style="color:rgb(224, 70, 0);background-color:rgba(142, 150, 170, 0.14);">end</font>
- 动画结束<font style="color:rgb(224, 70, 0);background-color:rgba(142, 150, 170, 0.14);">interrupt</font>
- 动画暂停<font style="color:rgb(224, 70, 0);background-color:rgba(142, 150, 170, 0.14);">cancel</font>
- 动画取消
二、组件介绍
写了一个Vue3组件,实现在文档中动态绘制波浪线的效果,可以动态控制波浪线绘制的动画效果的暂停和播放。接收参数:
- 文本 text 字符串
- 文本样式 style 对象
- 波浪线颜色 wavyLineColor 字符串
- 波浪线类型 waveType
sin | solid | zigzag | custom
- customFunction自定义曲线的函数 返回值是一个函数,比如 Math.sin(),会根据这个函数按照步长输入 x 得到 y,然后绘制曲线
方法:
- action 执行动画
- pause 暂停动画
- resume 继续播放暂停的动画
使用示例
<WavyText ref="wavyText1"text="D3.js 的反反复复凤飞飞反反复复方法方法反反复复反反复复凤飞飞发发发发发发发发发发发反反复复凤飞飞反反复复方法方法方法反反复复凤飞飞反反复复发发发发发发发发发发发发发发发都Vue 3 作为渐进式 JavaScript 框架的代表,凭借其出色的响应式系统和组合式 API":style="{ fontSize: '18px', color: '#2c3e50' }" wavyLineColor="#42b883" waveType="sin" />
三、开源代码
wavy-text-d3