如何繪制好看的動態排序圖,今天手把手教學
本文轉載自微信公眾號「小張Python」,作者zeroingi 。轉載本文請聯系小張Python公眾號。
0. 前言
大家好啊,已經好久沒更原創文了,失蹤人口回歸 ~,近期要準備秋招了在學一些 Java 相關知識,更文什么的也就耽擱了
之前一直是在與 Python 打交道,但這不馬上要找工作了么,Python 雖然全能但目前不是企業所需的主流語言,跟C++、Java 相比還挺弱勢的,因此想借此好好學一下 Java,希望能趕上秋招的末班車
學了 Java之后才知道自己是有多菜,,,以前并沒有刻意的去了解語言背后的基礎知識,例如進程、內存管理,比如程序啟動后沒報錯但長時間沒有反應、通過任務管理器發現資源一直在消耗著;是一直等著或者重新再啟動程序,還是去定位問題并解決、優化代碼,如果要定位問題從哪里下手?
網絡TCP/IP 協議:猜測現在看這篇文章的很多小伙伴們是為了學爬蟲而學 Python,現在也有兩個問題問大家:1,Response Headers 中常見的Cookie 、Session指的是什么, 針對反爬機制帶 Cookie去訪問服務器的原理又是什么?2,當請求目標站點獲取數據時,返回的狀態碼中2xx,3xx,4xx分別代表什么?以上列舉的這兩個問題都與計算機網絡相關,了解它們之后做爬蟲的話才更能游刃有余,而類似于正則、bs4文本解析只是工具而已,學習的話上手很快
垃圾回收、緩存一致性等相關知識:有時候為了縮短任務執行周期,會用到線程,使用時 Python 中的 GIL(全局解釋器鎖)能夠保證程序運行時的數據一致性問題,但里面的原理機制是什么?
后來接觸了這些知識才發現,操作系統、語言特性、計算機網絡、數據結構與算法這些基礎知識才是計算機入門的開始(語言只能說是一種工具),只有掌握了基礎知識,以后的學習之路才會更扎實、更順利;也就應證了一句老話 基礎不牢、地動山搖
平常的我只知道為了一些功能學習一下相關庫函數,把腳本實現出來即可俗稱 調包俠 吧。也希望大家在以后的學習路上引以為戒,重視基礎知識。
1. anichart 介紹
啰嗦了這么多,正式開始本期技術案例分享,關于動態排序圖制作之前分享過一篇文章,用的是 matplolib 的 animation 函數,感興趣的可以看下用python制作超燃動態排序圖!
基本功能是實現了,但最終效果并不那么友好,這種方法制圖的基本原理就是先把數據借助 Matplotlib 可視化為每一幀圖片,再將每一幀拼接為視頻,缺點很明顯:步驟繁瑣、代碼量多,可視化效果差
今天分享另外一種方法,制作此類狀態圖,用到的是一個 Github 項目 anichart,眾所周知目前如果想做一些絲滑流暢的可視化交互效果,javascripts 是必不可缺少的,而這個項目主要是用 typescript 實現,項目是由一個 B 站 Up主【Jannchie見齊】維護
typescript 語言是基于 js 開發的一門編程語言,為了彌補后者可維護性差、類型混亂等缺點;anichart 的主要功能是來制作動態排序圖,可視化效果要好得多,下面是我根據針對項目中提供的測試數據最終繪制的排序圖,
2. 環境介紹
先交代下本次用到的項目環境,anichart 整體算是一個前端項目,因此本次用到的工具都是一些與前端相關的,本期教程只是介紹給大家這個項目怎么用,關于具體細節不需要去考慮,所以無需擔心自己是否具備前端知識;
需要用到的軟件如下:
- node.js;
- ffmpeg;
測試系統為 Win10;
Node.js 在這里主要有兩個作用:1,管理下載項目所需要的一些依賴項,2,啟動 js 腳本;
ffmpeg 主要是將序列圖片轉化為視頻
關于 node.js 與 ffmpeg 在 windows下載安裝方式比較簡單,這里不多做介紹啦,這里給大家推薦兩個鏈接可以參考下:
Node.js 安裝:https://www.jianshu.com/p/2958fc051bfb
ffmpeg 安裝:https://zhuanlan.zhihu.com/p/118362010
兩個軟件安裝好之后,不要忘記配置環境變量~
3. anichart 庫使用
3.1 anichart安裝
anichart 作者已經把上傳至 npm 官網中,打開命令行,借助 npm i anichart 命令即可安裝,不需要我們再從 Github 上克隆,
npm是什么東東?npm其實是Node.js的包管理工具(package manager),也就是說你配置好 Node.js 命令之后,npm 就可以正常使用了
3.2 新建一個 js文件
安裝完之后,接下來就能使用了,在anichart 安裝路徑下 新建一個 js 文件,文件名可任意命名,然后把下面代碼添加到剛剛建立的 js 文件
- const ani = require('anichart')
- const stage = new ani.Stage()
- stage.options.fps = 24
- stage.options.sec = 30
- stage.output = false
- const bgAni = new ani.RectAni()
- bgAni.component.shape = {
- width: stage.canvas.width,
- height: stage.canvas.height,
- }
- bgAni.component.fillStyle = '#1e1e1e'
- const textLinesAni = new ani.TextLinesAni()
- textLinesAni.component.fillStyle = '#eee'
- textLinesAni.component.textAlign = 'center'
- textLinesAni.component.textBaseline = 'middle'
- textLinesAni.component.position = {
- x: stage.canvas.width / 2,
- y: stage.canvas.height / 2,
- }
- const textAnichart = new ani.TextAni()
- textAnichart.component.fontSize = 48
- textAnichart.component.font = 'Sarasa Mono Slab SC'
- textAnichart.component.text = 'Anichart'
- textAnichart.component.fontWeight = 'bolder'
- textAnichart.type = 'blur'
- textLinesAni.children.push(textAnichart)
- ani.recourse.loadImage('H:/Data/data/ANI.png', 'logo')
- ani.recourse.loadImage(
- 'https://avatars3.githubusercontent.com/u/29743310?s=460&u=8e0d49b98c35738afadc04e70c7f3918d6ad8cdb&v=4',
- 'jannchie'
- )
- ani.recourse.loadCSV('H:/Data/data/test.csv', 'data')
- const rectAni = ani.createAni(
- [
- new ani.Rect({
- position: { x: 100, y: 0 },
- shape: { width: 100, height: 0 },
- fillStyle: '#d23',
- }),
- new ani.Rect({
- shape: { width: 100, height: 200 },
- fillStyle: '#2a3',
- alpha: 1,
- }),
- new ani.Rect({
- shape: { width: 100, height: 0 },
- fillStyle: '#569',
- alpha: 0,
- }),
- ],
- [0, 1, 2],
- ani.ease.easeElastic
- )
- const logoCenter = new ani.Image({
- path: 'H:/Data/data/ANI.png',
- position: {
- x: stage.canvas.width / 2,
- y: stage.canvas.height / 2,
- },
- alpha: 0.25,
- center: { x: 128, y: 128 },
- shape: { width: 256, height: 256 },
- })
- const logoAni = ani.createAni(
- [
- new ani.Image({
- path: 'H:/Data/data/ANI.png',
- position: {
- x: 0,
- y: stage.canvas.height - 108,
- },
- shape: { width: 128, height: 128 },
- }),
- new ani.Image({
- path: 'H:/Data/data/ANI.png',
- position: {
- x: stage.canvas.width - 128,
- y: stage.canvas.height - 108,
- },
- shape: { width: 128, height: 128 },
- alpha: 1.0,
- }),
- new ani.Image({
- path: 'H:/Data/data/ANI.png',
- position: {
- x: stage.canvas.width - 128,
- y: stage.canvas.height - 108,
- },
- shape: { width: 128, height: 128 },
- alpha: 0,
- }),
- ],
- [0, 1, 2],
- ani.ease.easeBounce
- )
- const barChart = new ani.BarChart({
- shape: { width: stage.canvas.width, height: stage.canvas.height },
- labelFormat(id) {
- return id
- // return meta.get(id).name;
- },
- aniTime: [0, 30],
- })
- const lineChart = new ani.LineChart({
- aniTime: [0, 30],
- shape: { width: stage.canvas.width, height: stage.canvas.height / 2 },
- position: { x: 0, y: stage.canvas.height / 2 },
- })
- stage.addChild(bgAni)
- // stage.addChild(a)
- stage.addChild(logoCenter)
- stage.addChild(textLinesAni)
- stage.addChild(rectAni)
- stage.addChild(logoAni)
- stage.addChild(barChart)
- stage.addChild(lineChart)
- const pie = new ani.PieChart({
- aniTime: [0, 30],
- radius: [80, 120],
- position: { x: stage.canvas.width / 2, y: stage.canvas.height / 2 },
- })
- stage.addChild(pie)
- stage.play()
運行之前,需要改幾個參數,第一個更改數據路徑
- ani.recourse.loadCSV('H:/Data/data/test.csv', 'data')
數據形式需以下面形式存放,后面 data 代表數據源中更改的列名,比如這里是以日期作為變量進行序列化
- name,date,value,channel,other
- Jannchie,2020-01-01,1,科技,other
- Jannchie,2020-01-03,6,科技,other
- Jannchie,2020-01-05,3,科技,other
- Jannchie,2020-01-07,-,科技,other
- Jannchie,2020-01-09,7,科技,other
- Jannchie,2020-01-12,12,科技,other
- Cake47,2020-01-03,10,生活,other
- Cake47,2020-01-02,5,生活,other
- Cake47,2020-01-06,2,生活,other
- Cake47,2020-01-09,3,生活,other
- Cake47,2020-01-11,4,生活,other
第二個需要更改一下所有 png 路徑,改成你自己的,可以隨意替換為你的 圖片路徑,影響不大
- ani.recourse.loadImage('H:/Data/data/ANI.png', 'logo')
3.2 運行 js 腳本
以上修改完之后,接下來就是啟動腳本,在 js 同文件目錄下打開一個命令行,輸入 node XXX.js ,回車即可,XXX.js 代表你的 js 文件,效果如下
之后會有一個out 文件夾生成,里面存放的就是 anichart 繪制好的圖片
3.3 借助ffmpeg圖片生成視頻
最后,進入out 文件夾,借助ffmpeg 命令將圖片合成視頻,
- ffmpeg -f image2 -framerate 12 -i output-%d.png foo.avi
-framerate 后面參數 12 代表生成視頻的fps,可 根據自己情況設定,這里我設置的是 24;
最終一個動態排序視頻就生成了,隨后自己也可以加一些 bgm 給視頻加一些 feel
4. js中一些參數介紹
關于 anichart 的使用,原作者并沒有介紹太多,Github 主頁上只介紹了最簡單的使用方法
因此為了大家生成一些比較不錯的可視化圖,對上面 js 代碼中的部分參數做一些簡單介紹,增加一些對這個 anichart 庫的理解
4.1 fps、sec
- stage.options.fps = 24
- stage.options.sec = 30
fps 顧名思義就是一秒多少幀,sec 代表生成切片持續多長時間(單位:秒);用上面參數來設定的話,會生成 24x30 = 720 張圖片;這兩個參數決定最終視頻的流暢度,后面用 ffmpeg 生成視頻時,建議 framerate 參數 fps保持 一致
4.2 ani.recourse.loadImage
- ani.recourse.loadImage(
- 'https://avatars3.githubusercontent.com/u/29743310?s=460&u=8e0d49b98c35738afadc04e70c7f3918d6ad8cdb&v=4',
- 'jannchie'
- )
loadImage用來給柱狀圖中每個數據條上加一個 圖片 logo,方法需要加入兩個參數,前者表示圖片路徑或網頁鏈接,后者表示需要加logo 的數據條名字,例如這里選擇的數據條名字為 jannchie,可視化效果如下
4.3 BarChart、LineChart、PieChart
BarChart、LineChart、PieChart 分別表示柱狀圖、曲線圖、餅圖;anichart 除了這三類圖形外還有 ItemChart、BaseChart、MapChart 等,
anichart 在所有圖形對象中,都需要加入兩個參數,shape 和 aniTime,前者代表形狀大小、后者表示該圖形持續時間,單位 s
4.4 stage.addChild(xx)
anichart 通過 stage.addChild() 函數使得創建的對象生效 ,stage 表示全局 畫布,通過以下命令生成
- const ani = require('anichart')
- const stage = new ani.Stage()
5. 源碼數據獲取
為了方便,本文中涉及到的源碼、測試數據已經被我打包在一起了,獲取方式:在公眾號 小張Python 后臺,回復關鍵字:210605 即可獲取
6. 小結
關于動態排序圖制作除了這兩種方法之外,再向大家推薦一個網站名叫 flourish,網址:https://flourish.studio/examples/
flourish 最終生成效果也非常不錯,但缺點是需要微調大量參數
好了,關于排序圖的制作就介紹到這里了,如果內容對你有幫助的話不妨點個贊來鼓勵一下我~