Canvas畫圖-一個比想象中更騷氣的圓(漸變圓環)

Bob-Chen發表於2019-03-01

之前介紹了Canvas畫圖基礎,這篇介紹一下畫一個帶漸變效果的圓。

一個漸變的圓環

漸變色應用廣泛,和圓環結合做進度條非常酷,今天我們就來畫一個這樣的圓環:

Canvas畫圖-一個比想象中更騷氣的圓(漸變圓環)

Canvas漸變

Canvas畫圖基礎中我們知道給canvas上色主要是ctx.fillStylectx.strokeStyle方法,這裡是圓環,所以主要講strokeStyle方法,fillStyle方法也同樣適用。

看一下上面那個圓,像把一個線性的漸變給『掰彎』成一個圓。Canvas中有線性漸變的支援,我們可以試一下:

    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext(`2d`);
    ctx.lineWidth = 7;

    ctx.beginPath(); // 開一條新路
    ctx.moveTo(170, 120);

    var grd=ctx.createLinearGradient(0,0,170,0);
    grd.addColorStop("0","black");
    grd.addColorStop("0.3","magenta");
    grd.addColorStop("0.5","blue");
    grd.addColorStop("0.6","green");
    grd.addColorStop("0.8","yellow");
    grd.addColorStop(1,"red");

    ctx.strokeStyle = grd;
    ctx.arc(120, 120, 50, 0 ,Math.PI*2);
    ctx.stroke(); // 畫圓複製程式碼

畫出來的效果:

Canvas畫圖-一個比想象中更騷氣的圓(漸變圓環)

  • ctx.createLinearGradient就是建立一個線性漸變的物件,其中前兩個引數是起始點的x,y座標,後兩個引數是結束點的x,y座標,這裡是一個水平的線性漸變。
  • grd.addColorStop就是設定漸變點,類似css3漸變中的color-stop
  • 設定完漸變的物件,把漸變物件用於strokeStyle就可以實現漸變效果了。

再加點動畫上去,方便後面做進度條:

    var canvas = document.getElementById("canvas1");
    var ctx1 = canvas.getContext(`2d`);
    //
    // 中間設定漸變引數的程式碼一樣
    //
    function draw(ctx, x) {
        ctx.clearRect(0, 0, 300, 300);
        ctx.beginPath();
        ctx.strokeStyle = grd;//`#ff4444`;
        if (x < Math.PI*2) {
            x += Math.PI/100;
        } else {
            x = 0;
        }
        ctx.arc(80, 80, 50, 0, x, false); // 畫圓
        ctx.stroke();
        requestAnimationFrame(function () {
            draw(ctx, x);
        });
    }

    requestAnimationFrame(function () {
        draw(ctx1, 0);
    });複製程式碼

現在動畫有了,漸變也有了,但是一個最大的問題是,這個畫出來的是一個從左到右的漸變,上下顏色是對稱的。而我們想要的效果是上下非對稱的。

非對稱的漸變圓環

Canvas提供了線性漸變和徑向漸變(就是從圓心往外漸變,一個圓周上的顏色相同),二者都無法滿足我們設計師畫出的這個騷氣的圓。

於是,這裡要藉助另一個東東,ctx.createPattern

關於createPattern方法的定義如下:

createPattern() 方法在指定的方向內重複指定的元素。元素可以是圖片、視訊,或者其他 canvas 元素。被重複的元素可用於繪製/填充矩形、圓形或線條等等。

上面說可以指定用圖片來繪製圓形,斯國一。

來上程式碼:

    var canvas = document.getElementById("canvas2");
    var ctx2 = canvas.getContext(`2d`);
    ctx2.lineWidth = 8;
    ctx2.lineCap="round";

    var img;
    img = new Image();
    img.src = "./bg.png";

    if (img.complete) {
       setImageFill();
    } else {
       img.onload = setImageFill;
    }

    var newFill = null;
    function setImageFill() {
        newFill = ctx2.createPattern(img, `no-repeat`);
        drawNew(ctx2, 0);
    }

    function drawNew(ctx, x) {
        ctx.clearRect(0, 0, 300, 300);
        ctx.beginPath();
        ctx.strokeStyle = newFill;
        if (x < Math.PI*2) {
            x += Math.PI/100;
        } else {
            x = 0;
        }
        ctx.arc(50, 50, 46, 0, x, false);
        ctx.stroke();
        requestAnimationFrame(function () {
            drawNew(ctx, x);
        });
    }複製程式碼

畫出來的效果:

Canvas畫圖-一個比想象中更騷氣的圓(漸變圓環)

程式碼很多有幾點需要注意:

  1. 首先是載入圖片,要等圖片載入完之後再去進行繪製,也可以考慮將圖片base64進去;
  2. ctx2.createPattern(img, `no-repeat`)建立Pattern的時候不對圖片做repeat;
  3. ctx.arc(50, 50, 46, 0, x, false);畫圓的時候需要注意 如果想畫出來的半徑為50的話,需要用50減去描邊寬度的一半,也就是50-8/2 這個就是這裡半徑設定46的由來。因為描邊是把線放在中間然後往兩邊擴充套件來描,所以設定lineWidth為8的時候,實際我們看到的描邊是4;
  4. ctx2.lineCap="round";設定描邊的兩頭是圓形。

另外我們用的圖片是這樣的:

Canvas畫圖-一個比想象中更騷氣的圓(漸變圓環)

如果考慮到想支援各種Size可以現把圖片畫到另一個canvas上,做個resize。

    var bg = $(`<canvas>`)[0];
    var size = 100;
    bg.width = size;
    bg.height = size;
    bg.getContext(`2d`).drawImage(img, 0, 0, size, size);複製程式碼

至此,一個如此騷氣的圓環就畫完了。

完整程式碼:github.com/bob-chen/ca…

tips:移動端解決Canvas鋸齒問題

Canvas畫出來的實際上是點陣圖,在移動端高清屏橫行,我們需要根據window.devicePixelRatio來畫一個更大的Canvas,然後再縮小,原理類似於移動端使用雙倍圖,這樣可以很大程度上解決鋸齒問題,白色背景下基本看不出來。不過在黑色背景下細看還是很發現有一點鋸齒。

    //Variables global to the chart
    var width = context.canvas.width;
    var height = context.canvas.height;

    //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
    if (window.devicePixelRatio) {
        context.canvas.style.width = width + "px";
        context.canvas.style.height = height + "px";
        context.canvas.height = height * window.devicePixelRatio;
        context.canvas.width = width * window.devicePixelRatio;
        context.scale(window.devicePixelRatio, window.devicePixelRatio);
    }複製程式碼

參考

www.w3school.com.cn/tags/canvas…

www.w3school.com.cn/tags/canvas…

P.S 已新增svg的實現:一個比想象中更騷氣的圓 – svg 實現

相關文章