【摘要】 用原生canvasAPI實現百度echartswebpack
示例代碼託管在:http://www.github.com/dashnowords/blogsgit
使用原生canvasAPI
繪製水球圖,這將是一個很是有意思的挑戰任務。水球圖是一種常見的加載動畫,屬於擴展圖形,在echarts
中使用時須要下載擴展庫(同爲擴展庫的還包括文字雲插件和地圖插件,項目地址爲https://github.com/ecomfe/echarts-liquidfill)。github
水球圖的繪製有如下幾個難點:web
水波的繪製canvas
水波的繪製其實是運用簡諧振動公式來模擬的,也就是x = A*(wt +φ)
,其中振幅A
決定了水波的波紋高低,角頻率w
決定了水波的快慢,相位φ
決定了初始位移差,再加上一些y軸方向的位移誤差和顏色的差別,就能夠模擬出不一樣的水波,接着只須要在幀動畫中不斷改變φ
並重繪曲線,就能夠模擬出水波效果了。瀏覽器
球形剪裁區域echarts
水波的範圍是不能流出球形的外輪廓的,此處的作法是在繪製水波以前,先使用context.clip( )
方法將水波的可見繪圖區域控制在水球以內便可,若是還有水球外的圖形須要繪製,記得在每一幀繪製完水波後調用context.restore( )
取消掉以前的剪裁。dom
文字的繪製動畫
若是隻是繪製漂浮於水球圖之上的文字,是比較容易實現的,可是若是想要實現一些細節更豐富的效果,並不那麼容易。咱們指望實現的效果是,當文字未被水波浸入時,顯示水紋的藍色,而被水浸潤的部分顯示爲白色,這樣看起來更加生動。可是繪製起來卻並不容易,若是將文字繪製成藍色,那麼被水淹沒的部分就會消失在水紋中,若是繪製成白色,那麼水紋高度較小時,會徹底看不到文字。那麼這樣的渲染文字要如何實現呢?ui
let options = { value:0, a:20,//振幅 pos:[300,300],//水球圖位置 r:160,//水球圖半徑 color:['#2E5199','#1567c8','#1593E7','#42B8F9']//水紋顏色 }; start(options); /** * 繪製水球圖 */ function start(options) { //移動繪圖座標至水球圖左邊界點 context.translate(options.pos[0],options.pos[1]); context.font = 'bold 60px Arial'; context.textAlign='center'; context.textBaseLine = 'baseline'; //計算水球圖繪圖數據 createParams(options); //開啓幀動畫 requestAnimationFrame(startAnim); } //生成水波動畫參數,位置座標公式爲 y = A * (wt + φ) function createParams(options) { options.w = [];//存儲水波的角速度 options.theta = [];//存儲每條水波的位移 for(let i = 0; i < 4; i++){ options.w.push(Math.PI /(100 + 20*Math.random())); options.theta.push(20*Math.random()); } } //繪製水波線 function drawWaterLines(options) { let offset; let A = options.a;//正弦曲線振幅 let y,x,w,theta; let r = options.r; //遍歷每一條水紋理 for(let line = 0; line < 4; line++){ context.save(); //每次繪製時水波的偏移距離 theta = Math.random(); offset = r + A / 2 - (r*19/8 + A) * (options.value / 100 ) + line * r/12; //獲取正弦曲線計算參數 w = options.w[line]; theta = options.theta[line]; context.fillStyle = options.color[line]; context.moveTo(0,0); context.beginPath(); //以0.1爲步長繪製正弦曲線 for(x = 0; x <= 2*r; x+=0.1){ y = A * Math.sin(w * x + theta) + offset; //繪製點 context.lineTo(x,y); } //繪製爲超出水球範圍的封閉圖形 context.lineTo(x,r); context.lineTo(x - 2 * r,r); context.lineTo(0, A * Math.sin(theta) - options.height); context.closePath(); //填充封閉圖形獲得一條水波 context.fill(); //截取水波範圍,繪製文字(此處將在後文解釋) context.clip(); context.fillStyle = 'white'; context.fillText(parseInt(options.value,10) + '%',options.r + 10,10); context.restore(); } } //繪製最底層文字 function drawText1(options) { context.fillStyle = options.color[0]; context.fillText(parseInt(options.value,10) + '%',options.r + 10,10); } //幀動畫循環 function startAnim() { //用位移變化模擬水波 options.theta = options.theta.map(item=>item-0.03); //用百分比進度計算水波的高度 options.value += options.value > 100 ? 0:0.1; context.save(); resetClip(options);//剪切繪圖區 drawText1(options);//繪製藍色文字 drawWaterLines(options);//繪製水波線 context.restore(); requestAnimationFrame(startAnim); } /**設置水球範圍爲剪裁區域 *(本例中並無水球之外的部分須要繪製,實際上這裏不須要加入幀動畫循環中,只須要在開頭設置一次便可。) */ function resetClip(options) { let r = options.r; context.strokeStyle = '#2E5199'; context.fillStyle = 'white'; context.lineWidth = 10; context.beginPath(); context.arc(r, 0, r + 10, 0, 2*Math.PI, false); context.closePath(); context.fill(); context.stroke(); context.beginPath(); context.arc(r, 0, r, 0, 2*Math.PI, true); context.clip(); }
瀏覽器中可查看效果:
文字淹水效果的繪製其實是按照以下思路來進行的:
首先繪製與最上層水紋顏色一致的文字,這樣在被水淹沒以前,文字均可以以可見的顏色顯示。
在繪製水波的過程當中,連線完成後調用context.clip( )
方法將繪圖區域剪裁爲全部浸水部分,此時再將填充色設置爲白色,接着在同一個位置渲染文字,這樣渲染出的白色文字不會超出水紋的範圍,那麼水紋以外的文字的藍色部分也就被保存在畫布上了。
爲了不文字中白色的部分被下一層水紋繪製時截斷,咱們須要在每一層水紋繪製後,都重複步驟2,將該層水紋到水球底部的全部範圍設置爲剪裁區域,而後繪製該層水紋之內的白色文字部分,這樣當幾層水紋都繪製完畢後,文字淹水的部分就都會被染成白色。
在這樣的繪製方法中,文字的最終效果至關因而逐層繪製出來的片斷拼接起來的,每次繪製中能被保存到最後的部分,都只有和當前層的水紋相交的部分。
若是咱們將每一層文字的繪製顏色修改一下,就比較容易理解繪製過程:
若是仔細查看上面的水球外圓,會發現水球圖的外側不是很平整,看起來會有不少鋸齒。查到的方法大可能是將畫布畫布尺寸(canvas.height
,canvas.width
)調整爲元素尺寸(CSS中設置的canvas
元素的尺寸)的3-4倍,但願利用縮放來達到抗鋸齒的做用,但實測的結果卻並無明顯改進,利用畫布尺寸來縮放在解決圖像和填充模糊的時候效果較好,但在抗鋸齒方面的做用彷佛與線條自己的尺寸仍有關係,不是一種絕對有效的方案。另外一種較爲有效的方案,是在繪製外圓時增長2px-4px的深色陰影,在視覺上能夠很好地弱化鋸齒感。
//在繪製外圓以前添加以下代碼 context.shadowColor = '#2E5199'; context.shadowBlur = 2; context.shadowOffsetX = 0; context.shadowOffsetY = 2;
至此,咱們在這個系列中完成了全部基本圖表的原生API繪製,一些相對高級的圖表,其繪製過程並不必定很複雜,好比矩形樹圖,繪製起來實際上都是矩形方塊,但卻有助於咱們以某種更直觀更具備表現力的方式來觀察數據,例如可視化呈現webpack
的打包結果。數據可視化的基本任務就是讓數據變得可視,這須要咱們爲想觀察的數據選出恰當的表現方式,這不是純粹靠技術可以達到的,也須要一些藝術細胞和想象力。但不管如何,這都是一個值得研究的有趣的方向。
做者:大史不說話