前言:在現實生活中,看書的時候,在文本的下面畫個波浪線,畫個橫線,是很常見的行為。本篇文章使用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