canvas繪制飛線效果


在我們做的可視化大屏項目中,經常會遇到飛線的效果。 在我們的大屏編輯器中,可以通過拖拽+配置參數的方式很快就能夠實現。下面是我們使用大屏編輯器實現的一個項目效果:

效果

中間地圖就有飛線的效果。

拋開編輯器的快速實現不說,我們大致來說下canvas繪制飛線的大致原理。

貝塞爾曲線

飛線的路徑主要是一個貝塞爾曲線,canvas繪制貝塞爾曲線比較容易。canvas支持繪制二次和三次,在本次示例中,主要還是繪制二次貝塞爾曲線為主。canvas中指定二次貝塞爾曲線路徑的函數如下:

ctx.quadraticCurveTo(cpx, cpy, x, y);

有關貝塞爾曲線的基礎知識,讀者可以自行學習,此處不再贅述。

漸變實現

從圖中,可以看出飛線的效果是淡入的效果,顏色並不是一致的,起點處顏色很淡,終點處顏色就比較濃厚。
怎么樣能夠實現這種效果呢? 答案就是漸變,我們知道,canvas支持線性漸變和放射漸變。但是這兩種漸變似乎都不太適合曲線的路徑。

事實上,我們會考慮使用線性漸變。因為飛線效果中,曲線的彎曲程度都不太大,所以使用線性漸變,曲線造成的差異,人眼是感覺不出來的。

嗯嗯,圖形學就是欺騙的藝術。

只要在線的起點和終點創建一個線性漸變,起點的顏色非透明度是0,終點的非透明度是1即可達到目標。

示例代碼如下:

  function createGradient(ctx,p0,p1){
           var grd = ctx.createLinearGradient(p0.x,p0.y,p1.x,p1.y);
           grd.addColorStop(0,'rgba(255,0,255,0)');
           grd.addColorStop(1,'rgba(255,0,255,1)');
           return grd;
   }
  ctx.beginPath();
  ctx.moveTo(P0.x,P0.y);
  ctx.quadraticCurveTo(Q01.x,Q01.y,B1.x,B1.y);
  ctx.lineCap = 'round';
  ctx.lineWidth =3;
  ctx.strokeStyle = createGradient(ctx,P0,P2);
  ctx.shadowColor = 'rgba(255,0,255,1)';
  ctx.shadowBlur = 5;
  ctx.stroke();

漸變效果

流動效果

流動效果就是線條從起點開始,慢慢飛到終點的效果。 技術角度來說,就是繪制二次曲線百分之幾的一部分,百分比的數值從0增加到1,然后又回到0,周而復始。
代碼如下:

        let percent = 0.0;       
        function render(){
            ctx.save();
            //按百分比繪制
            ctx.restore();
            percent += 0.005;
            if(percent > 1){
                percent = 0.;
            }
            requestAnimationFrame(render);
        }

問題的關鍵在於如何繪制貝塞爾曲線的一部分。 一種思路是使用二次貝塞爾曲線的公式,把曲線分成很多片段來進行模擬,然而這種方式的效率並不高。 其實可以使用插值的方式來獲取一段貝塞爾曲線。代碼如下:

    // 參考https://xiaozhuanlan.com/topic/9506147283#section0t
           let P0 = startPoint, P1 = controlPoint,P2 = endPoint;
           let Q01 = interpolation(P0,P1,percent),
               Q11 = interpolation(P1,P2,percent),
               B1 = interpolation(Q01,Q11,percent);
function interpolation(P0,P1,t) {
           var Q = {
               x: P0.x * (1 - t) + P1.x * (t),
               y: P0.y * (1 - t) + P1.y * (t),
           };
           return Q;
       } 

有關上面插值的原理,可以參考下面的說明,摘取字文章:
https://xiaozhuanlan.com/topic/9506147283#section-5

二次貝塞爾曲線

我們知道二次貝塞爾曲線有三個點P0、P1、P2。二次貝塞爾曲線的表達方程如下:
B(t) = (1-t)2 * P0 + 2t(1-t) * P1 + t2 * P2
其中: $t \in $[0,1]

借助上面一次貝塞爾曲線的計算方法,可以通過以下步驟來確定二次貝塞爾曲線的B(t)點:

  • 選定 $t \in $[0,1]
  • 通過插值運算法則,在P0和P1所組成的線段上,計算出P0和P1點之間的插值點Q0,其中插值的比例值是t。根據插值規則有:length( P0, Q0 ) = length( P0, P1 ) * t
  • 通過插值運算法則,在P1和P2所組成的線段上,計算出P1和P2點之間的插值點Q1,其中插值的比例是t。
  • 通過插值運算法則,在Q1和Q2所組成的線段上,計算出P1和P2點之間的插值點B,其中插值的比例是t。
    上述過程中計算出來的點B就是在曲線上面點。上述過程如下圖所示:

二次貝塞爾曲線的計算方法

從圖中可以得出結論:

  • 直線(Q0,Q1)和曲線相切於B點。

另外還有隱藏的結論:

  • 曲線(P0,B)也是貝塞爾曲線,P0是曲線的起始點,B是曲線的終止點,而Q0是控制點
  • 曲線(B,P2)也是貝塞爾曲線,B是曲線的起始點,P2是曲線的終止點,而Q1是控制點

上面兩個結論會很有用,有了這個兩個結論,前面“迭代(分片)”繪制部分貝塞爾的方法,可以用更加簡單的方法替代,這在稍后詳細說明。

如果將t的值從0過渡到1,不斷計算點B,這些點的集合就可以組成一條二次貝塞爾曲線。下面圖形動畫復現了這個效果:
二次貝塞爾曲線的計算方法過程

通過上面的方式,就可以繪制流動的飛線效果了,如下圖所示:

流動效果

加上陰影

默認線條的樣式並不是很好看,如果加上陰影,可以讓效果更加豐滿。 加上陰影也很簡單,代碼如下:

 ctx.shadowColor = 'rgba(255,0,255,1)';
 ctx.shadowBlur = 5;

最終的飛線效果參考下圖:
效果

結語

如果對可視化感興趣,可以和我交流,微信541002349. 如果你對我們的大屏編輯器產品或者3d組態編輯器感興趣,可以加我微信交流,或者發郵件也可以,terry.tan@servasoft.com。 另外關注公眾號“ITMan彪叔” 可以及時收到更多有價值的文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM