使用node-canvas在服務(wù)端渲染echarts圖表解析
踩了很長時(shí)間的坑,總算是能跑起來了
但是如果要我給echarts的SSR一個(gè)評價(jià),那就是不好用……可能是我太菜了。而且,因?yàn)槲沂荳indows用戶,這個(gè)過程對Windows極其不友好。
友情提示:入坑請慎重
在服務(wù)端渲染圖表,繞不開的一個(gè)問題就是,沒有DOM怎么繪圖?這個(gè)主要有兩種解決方案,一個(gè)是用那些headless的瀏覽器去渲染,然后進(jìn)行截圖;另一個(gè)就是在Node環(huán)境下模擬DOM元素,比如我在這里想用canvas,就得裝個(gè)node-canvas;如果想用SVG,就得用JSDOM一類的庫。我這里主要是用的canvas,所以就用node-canvas了。
首先,需要安裝node-canvas和echarts。echarts不用多說,但是有一點(diǎn),不建議使用官方推薦的node-echarts,版本陳舊,而且依賴的庫新版本的Node(12.x)不支持。
node-canvas安裝請參考官方文檔,因?yàn)榘惭b流程比較復(fù)雜,尤其是Windows用戶:https://github.com/Automattic/node-canvas/wiki/Installation:-Windows
當(dāng)然,因?yàn)楸娝苤脑?,最后一步用npm install安裝node-canvas是個(gè)問題,因?yàn)樗A(yù)編譯的安裝包好像是在AWS上,大概率會卡在這里:
node-pre-gyp WARN Using request for node-pre-gyp https download
或者,還有可能卡在這里:
node-pre-gyp info install unpacking Release/ node-pre-gyp info install unpacking Release/canvas.exp node-pre-gyp info install unpacking Release/canvas.ilk
然后還有可能直接報(bào)錯(cuò)。解決方案就是,加個(gè)–build-from-source,不用他編譯好的,而是在我們本機(jī)進(jìn)行編譯:
npm install canvas --verbose --build-from-source
在這個(gè)過程中,還有可能報(bào)錯(cuò),比較常見的有這些
1、找不到node-gyp。
解決辦法:npm install -g node-gyp,裝一個(gè)就是了。不過一般來說會自帶才對……
2、fatal error C1083: 無法打開包括文件: “cairo.h”: No such file or directory。
解決方法:參考官方文檔安裝GTK 2,并放在合適的路徑。
3、fatal error C1083: 無法打開包括文件: “jpeglib.h”: No such file or directory
解決方法:參考官方文檔安裝libjpeg-turbo,并放在合適的路徑。
注意,對于Windows用戶,一定要安裝for VC++的版本,不要裝成for gcc的版本。
4、Error: Cannot find module '../build/Release/canvas.node'
解決方法:進(jìn)入node_modules/canvas目錄,然后使用node-gyp configure build手動編譯。
5、gyp: binding.gyp not found
解決方法:同4。還有一種可能是缺少windows-build-tools,這個(gè)在第7點(diǎn)中說。
6、node.lib已損壞。
解決方法:升級Node版本,或者嘗試Node安裝包的repair功能。大概率是Node沒裝好,或者你使用了canvas-prebuilt這個(gè)已經(jīng)廢棄的庫。實(shí)測升級到最新版本12.16.1可以修復(fù)。
7、與windows-build-tools相關(guān)的一系列錯(cuò)誤。
解決方法:npm install --global --production windows-build-tools
但是在此過程中可能會出現(xiàn)一系列問題??赡軙ㄔ谶@里:
Python 2.7.16 is already installed, not installing again.
也有可能卡在這里:
Successfully installed Python 2.7
或者提示安裝完了,但一直沒有退出,請往下看。
第一次卡住的時(shí)候,嘗試重復(fù)一遍install,如果解決了那自然最好,如果沒解決,甚至報(bào)錯(cuò):
Error: EBUSY: resource busy or locked
那么我們可能是遇到了同樣的問題。
網(wǎng)上有兩種方案,我放在這里做個(gè)參考,雖然對我來說都沒用就是了:
1、先安裝一個(gè)舊版本npm install --global --production windows-build-tools@4.0.0,然后重新npm install -g --production windows-build-tools,就可以了。
2、針對resource busy or locked的報(bào)錯(cuò),先在任務(wù)管理器里kill掉BuildTolls_Full.exe這個(gè)進(jìn)程,然后去C:\Users\<你的用戶名>\.windows-build-tools里找到build-tools-log.txt,在這個(gè)文件的最后增加一行:
Variable: IsInstalled = 1
保存后重新install。
但是這兩種對我來說都沒用。后來,我無意中看到一個(gè)方法,死馬當(dāng)活馬醫(yī),居然成了……說起來也很迷幻,加個(gè)–verbose就好了:
npm install --global --production --verbose windows-build-tools
到了這里,基本上就可以開始了。其實(shí)方法并不神秘,主要就是這么一個(gè)方法:
const path = require('path')
const { createCanvas } = require('canvas')
const echarts = require('echarts')
function generateImage(options) {
const canvas = createCanvas(600, 600) // 600 * 600的canvas
const ctx = canvas.getContext('2d')
ctx.font = '12px'
echarts.setCanvasCreator(() => canvas) // 使用node-canvas
const chart = echarts.init(canvas)
options.animation = false
options.textStyle = {
fontSize: 12
}
chart.setOption(options) // 就是echarts的options
return chart.getDom().toBuffer() // 返回buffer
}
這里也可以用fs.writeFileSync來進(jìn)行文件讀寫,不過我更傾向于返回buffer流,個(gè)人愛好而已。
不過,返回buffer流的話,前端需要一些處理,以axios為例,需要設(shè)置responseType為blob,然后用createObjectURL來處理blob,然后把url放到img里去:
const { data } = await axios.get('/api/chart', {
responseType: 'blob'
})
URL.createObjectURL(data) // 放進(jìn)img.src的url最終效果差不多是這樣:

同時(shí),服務(wù)端渲染還面臨著服務(wù)端不支持中文導(dǎo)致亂碼,圖片不清晰等問題,這些我還沒有特別好的處理辦法,只能看情況進(jìn)行處理。
我只說說我試過有用的辦法
1、針對亂碼問題,node-canvas 2.x提供了一個(gè)導(dǎo)入字體的方法registerFont(),可以指定中文字體。但是我并不喜歡這個(gè)方法,平白無故增加靜態(tài)資源的數(shù)量。
據(jù)說在服務(wù)器上裝好中文字體可以解決,但在我這里沒用。
2、圖片不清晰,可以在init的時(shí)候增大像素比:
echarts.init(canvas, null, {devicePixelRatio: 2});但是這又有一個(gè)問題,這么弄出來的圖片大小會翻倍。本來我用SSR就是希望提高性能,為了清晰度還得犧牲性能,就有點(diǎn)本末倒置了。
總的來說,目前我還沒有發(fā)現(xiàn)對echarts進(jìn)行SSR的好處,可能用那些更輕量的圖表庫配合SVG效果會比較好,比如D3??赡苡杏玫膱鼍熬褪秋@示那些對清晰度要求不太高的圖表,比如圖表的動態(tài)縮略圖,因?yàn)橛袝r(shí)候可能會有這樣的需求,雖然是縮略圖或者是示意圖,也希望能夠動態(tài)更新,因?yàn)榍昂髷?shù)據(jù)變化可能比較大。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
參考資料
https://github.com/Automattic/node-canvas/issues/1468#issuecomment-522961098
https://github.com/mapbox/node-pre-gyp/issues/477#issuecomment-534231739
相關(guān)文章
node.js使用stream模塊實(shí)現(xiàn)自定義流示例
這篇文章主要介紹了node.js使用stream模塊實(shí)現(xiàn)自定義流,結(jié)合實(shí)例形式詳細(xì)分析了node.js基于stream模塊實(shí)現(xiàn)自定義的可讀流、可寫流、可讀寫流等相關(guān)操作技巧,需要的朋友可以參考下2020-02-02
node koa2實(shí)現(xiàn)上傳圖片并且同步上傳到七牛云存儲
這篇文章主要介紹了node koa2實(shí)現(xiàn)上傳圖片并且同步上傳到七牛云存儲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
NodeJS使用Range請求實(shí)現(xiàn)下載功能的方法示例
本篇使用 NodeJS 的 HTTP 服務(wù)創(chuàng)建客戶端,使用 Range 請求實(shí)現(xiàn)下載功能,本篇使用 NodeJS 的 HTTP 服務(wù)創(chuàng)建客戶端,使用 Range 請求實(shí)現(xiàn)下載功能2018-10-10
node.js使用zlib模塊進(jìn)行數(shù)據(jù)壓縮和解壓操作示例
這篇文章主要介紹了node.js使用zlib模塊進(jìn)行數(shù)據(jù)壓縮和解壓操作,結(jié)合實(shí)例形式詳細(xì)分析了node.js基于zlib模塊創(chuàng)建數(shù)據(jù)流以及壓縮和解壓縮等相關(guān)操作技巧,需要的朋友可以參考下2020-02-02
Node.js數(shù)據(jù)流Stream之Readable流和Writable流用法
這篇文章介紹了Node.js數(shù)據(jù)流Stream之Readable流和Writable流的用法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07

