一個少女心滿滿的例子帶你入門 Canvas

canvas入門
canvas入門

本文首發於個人我的博客:cherryblog.site/
github項目地址:github.com/sunshine940…
項目演示地址:sunshine940326.github.io/canvasStar/css

以前看到了一個很好看的canvas效果,而後拿來作個人博客背景,很多童鞋留言說求教程,而且反應說太耗內存,因而前一段我就重寫了一遍,而且使用離屏渲染進行優化,效果仍是挺顯著的。可是由於畢竟是canvas,須要一直進行重繪,因此仍是比較耗內存的,可是比優化以前已經好不少了。而且最近準備本身寫插件,因而就拿這個練手了,html

github地址:github.com/sunshine940… git

代碼還有不少的不足,求大神 review (づ。◕‿‿◕。)づ~github

canvas 基本知識

什麼是 canvas

canvas 是 HTML5 新定義的標籤,經過使用腳本(一般是 JavaScript)繪製圖形。
<canvas> 標籤只是圖形容器,至關於一個畫布,canvas 元素自己是沒有繪圖能力的。全部的繪製工做必須在 JavaScript 內部完成,至關於使用畫筆在畫布上畫畫。canvas

默認狀況下,<canvas> 沒有邊框和內容。默認是一個 300*150 的畫布,因此咱們建立了 <canvas> 以後要對其設置寬高。api

咱們能夠經過html屬性‘width’,‘height’來設置canvas的寬高,不能夠經過 css 屬性來設置寬高。由於經過 css 屬性設置的寬高會使 canvas 內的圖像按照 300*150 時的比例放大或縮小緩存

getContext()

context 是一個封裝了不少繪圖功能的對象,咱們在頁面中建立一個 canvas 標籤以後,首先要使用 getContext() 獲取 canvas 的上下文環境,目前 getContext() 的參數只有 2d,暫時還不支持 3d bash

getContext("2d") 對象是內建的 HTML5 對象,擁有多種繪製路徑、矩形、圓形、字符以及添加圖像的方法。網絡

canvas 元素繪製圖像

canvas 建立圖形有兩種方式dom

context.fill()

fill() 方法填充當前的圖像(路徑)。默認顏色是黑色。在填充前要先使用 fillStyle 設置填充的顏色或者漸變,而且若是路徑未關閉,那麼 fill() 方法會從路徑結束點到開始點之間添加一條線,以關閉該路徑(正如 closePath() 同樣),而後填充該路徑。

context.stroke()

stroke() 方法會實際地繪製出經過 moveTo()lineTo() 方法定義的路徑。默認顏色是黑色。在進行圖形繪製前,要設置好繪圖的樣式

fillStyle()//填充的樣式
strokeStyle()//邊框樣式
context.lineWidth()//圖形邊框寬度複製代碼

繪製矩形

用 canvas 繪製一個矩形很簡單

fillRect(x,y,width,height)  // 實心矩形 
strokeRect(x,y,width,height)        // 空心矩形複製代碼
  • x :起始點的 x 座標
  • y :起始點的 y 座標
  • width : 矩形的寬
  • height : 矩形的高
//html代碼
<canvas id="canvas"></canvas>
//script代碼
   var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');
    context.fillRect(0, 0, 100, 100);
    context.strokeRect(120, 0, 100, 100);複製代碼

顯示以下:

canvas繪製矩形有填充顏色
canvas繪製矩形有填充顏色

咱們能夠看出,在沒有設置顏色的狀況下,默認是黑色的。

咱們還能夠經過設置 fillStyle 或者 fillStyle 改變其填充顏色。

context.fillStyle = "pink";
context.strokeStyle = "darkred";
context.fillRect(0, 0, 100, 100);
context.strokeRect(120, 0, 100, 100);複製代碼

效果以下

canvas繪製矩形有填充顏色
canvas繪製矩形有填充顏色

清除矩形區域

clearRect(x,y,width,height)複製代碼
  • x :清除矩形起始點的 x 座標
  • y :清除矩形起始點的 y 座標
  • width : 清除矩形矩形的寬
  • height : 清除矩形矩形的高
    var canvas = document.getElementById('canvas');
    var context = canvas.getContext("2d");
    context.fillRect(0, 0, 100, 100);
    context.strokeRect(120, 0, 100, 100);
    context.fillStyle = "pink";
    context.strokeStyle = "darkred";
    context.fillRect(0, 120, 100, 100);
    context.strokeRect(120, 120, 100, 100);
    context.clearRect( 50,50,120,120)複製代碼
    效果以下:
    清除矩形
    清除矩形

    實心圓

    context.arc(x, y, radius, starAngle,endAngle, anticlockwise)
  • x : 圓心的 x 座標
  • y:圓心的 y 座標
  • radius : 半徑
  • starAngle :開始角度
  • endAngle:結束角度
  • anticlockwise :是否逆時針(true)爲逆時針,(false)爲順時針
    context.beginPath();
    context.arc(300, 350, 100, 0, Math.PI * 2, true);
    //不關閉路徑路徑會一直保留下去
    context.closePath();
    context.fillStyle = 'rgba(0,255,0,0.25)';
    context.fill();複製代碼
    效果以下:
    canvas繪製圓弧
    canvas繪製圓弧

圓弧

若是不填充顏色,實心圓就是圓弧

context.beginPath();
    context.arc(600, 350, 100, 0, Math.PI , true);
    context.strokeStyle = 'pink';
    context.closePath();
    context.stroke();

    context.beginPath();
    context.arc(300, 350, 100, 0, Math.PI , true);
    context.strokeStyle = 'red';
    //沒有closePath
    context.stroke();複製代碼

效果如圖:

canvas繪製圓弧
canvas繪製圓弧

  • 系統默認在繪製第一個路徑的開始點爲beginPath
  • 若是畫完前面的路徑沒有從新指定beginPath,那麼畫第其餘路徑的時候會將前面最近指定的beginPath後的所有路徑從新繪製
  • 每次調用context.fill()的時候會自動把當次繪製的路徑的開始點和結束點相連,接着填充封閉的部分

因此說,若是第一個圓弧沒有 closePath() 而且第二個圓弧沒有 beginPath() 的話就是這樣的效果:

canvas繪製矩形
canvas繪製矩形

繪製線段

  • moveTo(x,y):把路徑移動到畫布中的指定點,不建立線條
  • lineTo(x,y):添加一個新點,而後在畫布中建立從該點到最後指定點的線條
  • 每次畫線都從 moveTo 的點到 lineTo 的點,
    context.strokeStyle = 'pink';
     context.moveTo(0, 0);
     context.lineTo(100, 100);
     context.stroke();*/複製代碼
    效果以下:
    canvas繪製片斷
    canvas繪製片斷

    若是沒有 moveTo 那麼第一次 lineTo 的效果和 moveTo 同樣,
    例如:
context.strokeStyle = 'pink';
    context.lineTo(100, 100);
    context.lineTo(200, 200);
    context.stroke();*/複製代碼

效果以下:

canvas繪製線段
canvas繪製線段

每次lineTo後若是沒有moveTo,那麼下次lineTo的開始點爲前一次lineTo的結束點
例如:

// 繪製片斷
    context.strokeStyle = 'pink';
    context.lineTo(200, 200);
    context.lineTo(200, 100);
    context.lineTo(100,50);
    context.stroke();複製代碼

效果以下:

canvas繪製線段
canvas繪製線段

咱們可使用 canvas 的線段繪製各類各樣的圖形,好比繪製一個六邊形

var n = 0;
    var dx = 150;
    var dy = 150;
    var s = 100;
    context.beginPath();
    context.fillStyle = 'pink';
    context.strokeStyle = 'rgb(0,0,100)';
    var x = Math.sin(0);
    var y = Math.cos(0);
    var dig = Math.PI / 15 * 5;
    for (var i = 0; i < 6; i++) {
        var x = Math.sin(i * dig);
        var y = Math.cos(i * dig);
        context.lineTo(dx + x * s, dy + y * s);
        console.log( x ,y )
    }
    context.closePath();
    context.fill();
    context.stroke();複製代碼

使用canvas繪製六邊形
使用canvas繪製六邊形

繪製 30 邊形:

var n = 0;
    var dx = 150;
    var dy = 150;
    var s = 100;
    context.beginPath();
    context.fillStyle = 'pink';
    context.strokeStyle = 'rgb(0,0,100)';
    var x = Math.sin(0);
    var y = Math.cos(0);
    var dig = Math.PI / 15 * 7;
    for (var i = 0; i < 30; i++) {
        var x = Math.sin(i * dig);
        var y = Math.cos(i * dig);
        context.lineTo(dx + x * s, dy + y * s);
        console.log( x ,y )
    }
    context.closePath();
    context.fill();
    context.stroke();複製代碼

效果以下:

canvas繪製 30 邊形
canvas繪製 30 邊形

線性漸變

var lg= context.createLinearGradient(xStart,yStart,xEnd,yEnd)
lg.addColorStop(offset,color)

  • xstart:漸變開始點x座標
  • ystart:漸變開始點y座標
  • xEnd:漸變結束點x座標
  • yEnd:漸變結束點y座標
  • offset:設定的顏色離漸變結束點的偏移量(0~1)
  • color:繪製時要使用的顏色

例如:

var g1 = context.createLinearGradient(0, 0, 0, 300);
    g1.addColorStop(0, '#E55D87'); 
    g1.addColorStop(1, '#5FC3E4');
    context.fillStyle = g1;
    context.fillRect(0, 0, 400, 300);複製代碼

效果以下:

canvas繪製漸變
canvas繪製漸變

徑向漸變

var rg=context.createRadialGradient(xStart,yStart,radiusStart,xEnd,yEnd,radiusEnd)
rg.addColorStop(offset,color)

  • xStart:發散開始圓心x座標
  • yStart:發散開始圓心y座標
  • radiusStart:發散開始圓的半徑
  • xEnd:發散結束圓心的x座標
  • yEnd:發散結束圓心的y座標
  • radiusEnd:發散結束圓的半徑
  • offset:設定的顏色離漸變結束點的偏移量(0~1)
  • color:繪製時要使用的顏色

徑向漸變原理
徑向漸變原理

例如:

// 同心圓徑向漸變
    var g1 = context.createRadialGradient(200, 150, 0, 200, 150, 200);
    g1.addColorStop(0.1, '#F09819');
    g1.addColorStop(1, '#EDDE5D');
    context.fillStyle = g1;
    context.beginPath();
    context.arc(200, 150, 100, 0, Math.PI * 2, true);
    context.closePath();
    context.fill();複製代碼

canvas繪製同心圓徑向漸變
canvas繪製同心圓徑向漸變

//不一樣圓心的徑向漸變模型
    var g1 = context.createRadialGradient(100, 150, 10, 300, 150, 80);
    g1.addColorStop(0.1, '#F09819');
    g1.addColorStop(0.8, 'red');
    g1.addColorStop(1, '#EDDE5D');

    context.fillStyle = g1;
    context.fillRect(0, 0, 300, 500);複製代碼

效果圖:

不一樣圓心徑向漸變
不一樣圓心徑向漸變

圖形變形

縮放

scale(x,y)

  • x :x座標軸按 x 比例縮放
  • y :x座標軸按 y 比例縮放

    旋轉

    rotate(angle)
  • angle :座標軸旋轉x角度(角度變化模型和畫圓的模型同樣)

平移

translate(x,y)

  • x :座標原點向x軸方向平移x
  • y :座標原點向y軸方向平移y

平移,縮放,旋轉前後順序不一樣,座標軸的變化圖,圖片來源於網絡:

平移縮放旋轉前後順序不一樣座標軸的變化圖
平移縮放旋轉前後順序不一樣座標軸的變化圖

圖形組合

globalCompositeOperation=type
設置或返回新圖像如何繪製到已有的圖像上。最後的效果取決於 type 的值
type:

  • source-over(默認值):在原有圖形上繪製新圖形
  • destination-over:在原有圖形下繪製新圖形
  • source-in:顯示原有圖形和新圖形的交集,新圖形在上,因此顏色爲新圖形的顏色
  • destination-in:顯示原有圖形和新圖形的交集,原有圖形在上,因此顏色爲原有圖形的顏色
  • source-out:只顯示新圖形非交集部分
  • destination-out:只顯示原有圖形非交集部分
  • source-atop:顯示原有圖形和交集部分,新圖形在上,因此交集部分的顏色爲新圖形的顏色
  • destination-atop:顯示新圖形和交集部分,新圖形在下,因此交集部分的顏色爲原有圖形的顏色
  • lighter:原有圖形和新圖形都顯示,交集部分作顏色疊加
  • xor:重疊飛部分不現實
  • copy:只顯示新圖形
    效果圖以下,圖片來源於網絡
    效果圖
    效果圖

陰影

shadowOffsetX:設置或返回陰影距形狀的水平距離(默認值爲 0)
shadowOffsetY:設置或返回陰影距形狀的垂直距離(默認值爲 0)
shadowColor:設置或返回用於陰影的顏色
shadowBlur:設置或返回用於陰影的模糊級別(值越大越模糊)複製代碼

例如:

context.fillStyle = 'white';
    context.beginPath();
    context.arc(100,100,10,0,2 * Math.PI);
    context.shadowColor = 'white';
    context.shadowBlur = 10;
    context.fill();
    context.closePath();複製代碼

咱們看到的效果就是咱們在開頭提起的例子中的 star 粒子的效果,由於其有白色陰影的效果,因此看起來像是發光同樣,效果以下圖:

帶陰影效果的圓形
帶陰影效果的圓形

圖像繪製

drawImage()
向畫布上繪製圖像、畫布或視頻

  • 在畫布上定位圖像:context.drawImage(img,x,y);
  • 在畫布上定位圖像,並規定圖像的寬度和高度:context.drawImage(img,x,y,width,height);
  • 剪切圖像,並在畫布上定位被剪切的部分:context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
  • img:規定要使用的圖像、畫布或視頻。
  • sx:可選。開始剪切的 x 座標位置。
  • sy:可選。開始剪切的 y 座標位置。
  • swidth:可選。被剪切圖像的寬度。
  • sheight:可選。被剪切圖像的高度。
  • x:在畫布上放置圖像的 x 座標位置。
  • y:在畫布上放置圖像的 y 座標位置。
  • width:可選。要使用的圖像的寬度。(伸展或縮小圖像)
  • height:可選。要使用的圖像的高度。(伸展或縮小圖像)

canvas繪製圖形例子
canvas繪製圖形例子

圖像平鋪

createPattern(image,type)
type:

  • no-repeat:不平鋪
  • repeat-x:橫方向平鋪
  • repeat-y:縱方向平鋪
  • repeat:全方向平鋪

圖像裁剪

clip()從原始畫布剪切任意形狀和尺寸的區域,須要先建立裁剪區域,再繪製圖像;一旦剪切了某個區域,則全部以後的繪圖都會被限制在被剪切的區域內(不能訪問畫布上的其餘區域)。您也能夠在使用 clip() 方法前經過使用 save() 方法對當前畫布區域進行保存,並在之後的任意時間對其進行恢復(經過 restore() 方法)。
例如:

// 設置剪切區域(粉色矩形)
    context.rect(0,0,500,400);
    context.fillStyle = "pink";
    context.fill();
    context.clip();

    // 在剪切區域中繪製圖形(白色矩形)
    context.fillStyle = "white";
    context.fillRect(10,10,100,100);

    // 以後繪製的圖形只能顯示在剪切區域以內(紅色矩形)
    context.fillStyle = "red";
    context.fillRect(100,100,600,600)複製代碼

效果以下:能夠看到咱們設置的紅色矩形是一個 600600 的矩形,可是顯然是沒有顯示完的,*一旦剪切了某個區域,則全部以後的繪圖都會被限制在被剪切的區域內(不能訪問畫布上的其餘區域)。

canvas進行圖像剪切
canvas進行圖像剪切

因此說咱們能夠在使用 clip() 方法前經過使用 save() 方法對當前畫布區域進行保存,並在之後的任意時間對其進行恢復(經過 restore() 方法)。
代碼以下:

context.save();
    // 設置剪切區域
    context.rect(0,0,500,400);
    context.fillStyle = "pink";
    context.fill();
    context.clip();

    // 在剪切區域中繪製圖形
    context.fillStyle = "white";
    context.fillRect(10,10,100,100);

    context.restore();
    // 以後繪製的圖形只能顯示在剪切區域以內
    context.fillStyle = "red";
    context.fillRect(100,100,600,600)複製代碼

這樣就能夠正常顯示了:

canvas進行圖像裁剪
canvas進行圖像裁剪

繪製文字

fillText(text,x,y):繪製實心文字
strokeText():繪製文字描邊(空心)
textAlign:設置或返回文本內容的當前對齊方式
textBaseline:設置或返回在繪製文本時使用的當前文本基線
font:設置或返回文本內容的當前字體屬性複製代碼

例如:

context.font="40px Arial";
    context.fillText("Hello world",200,200);
    context.strokeText("Hello world",200,300)複製代碼

效果以下:

canvas繪製文字
canvas繪製文字

準備工做

好的開始是成功的一半

簡單介紹了下 canvas 的經常使用 api,你們發現是否是也沒有那麼難呢~( ̄▽ ̄)~*,那麼讓咱們回到標題,一塊兒來看一下這個少女心滿滿的例子是怎樣實現的~

canvas 其實寫一個炫酷的特效在技術上並不難,難的是你的創意,由於 canvas 實現粒子的效果仍是比較驚豔的,但其實代碼都是比較簡單的,無非就是隨機的建立圖形或者路徑,固然圖形也是閉合的路徑。在加上必定的位移就能夠了。可是你要設計出一個好的特效是很是不容易的。

因此咱們就先來分析一下這個效果由那幾部分構成,將其拆分開來。

特效pc端演示地址:sunshine940326.github.io/canvasStar/ (固然,能夠直接查看個人博客,背景暫時就是這個,不知道何時會變,捂臉ing:cherryblog.site/)

分析 star 的表現和行爲

咱們能夠將其一直位移向上的粒子稱爲 star,咱們觀察 star 的特色:

  • 開始建立時位置隨機(座標隨機)
  • 透明度隨機
  • 建立時的大小在必定範圍內(半徑在必定範圍內)
  • 勻速上升
  • 總數不變

因此咱們就能夠總結出 star 的特色就是總數固定,建立時座標和半徑還有透明度隨機,勻速上升。是否是很簡單了呢~[]~( ̄▽ ̄)~*

分析 dot 的表現和行爲

再讓咱們來看一下隨着鼠標移入產生的粒子,咱們稱爲 dot,同理,咱們觀察獲得 dot 的特色

  • 列表內容
  • 鼠標移動時產生
  • 新產生的 dot 和以前的 3 個 dot 產生連線
  • 向四周移動
  • 達到必定條件消失

這樣,咱們就完成了一半了呢~將事件屢清楚以後咱們就能夠開始着手擼代碼了!

背景的 HTML 和 CSS

其實須要的 HTML 代碼和 CSS 代碼很簡答的,HTML 只須要一行就能夠了呢,設置一個漸變的背景蒙層和一個 canvas 標籤。

<div class="filter"></div>
<canvas id="canvas"></canvas>複製代碼

CSS 以下:

html, body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background: black;
            background: linear-gradient(to bottom, #dcdcdc 0%, palevioletred 100%);
        }

        #main-canvas {
            width: 100%;
            height: 100%;
        }

        .filter {
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            background: #fe5757;
            animation: colorChange 30s ease-in-out infinite;
            animation-fill-mode: both;
            mix-blend-mode: overlay;

        }

        @keyframes colorChange {
            0%, 100% {
                opacity: 0;
            }
            50% {
                opacity: .7;
            }
        }複製代碼

是的,我使用的是一個漸變的背景,不只是從上到下的漸變,而且顏色也是會漸變的,效果以下:

漸變背景
漸變背景

設置參數以及獲取 dom 對象

/*
     * @var star_r:star半徑係數,係數越大,半徑越大
     * @var star_alpha:生成star的透明度,star_alpha越大,透明度越低
     * @var initStarsPopulation:初始化stars的個數
     * @var move_distance:star位移的距離,數值越大,位移越大
     * @var dot_r : dot半徑係數,係數越大,半徑越大
     * @var dot_speeds : dots運動的速度
     * @var dot_alpha : dots的透明度
     * @var aReduction:dot消失條件,透明度小於aReduction時消失
     * @var dotsMinDist:dot最小距離
     * @var maxDistFromCursor:dot最大距離
     * */
    var config = {
        star_r : 3,
        star_alpha : 5,
        initStarsPopulation : 150,
        move_distance : 0.25,
        dot_r : 5,
        dot_speeds : 0.5,
        dot_alpha : 0.5,
        dot_aReduction : 0.01,
        dotsMinDist : 5,
        maxDistFromCursor : 50,
    };
    var stars = [],
        dots = [],
        canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d'),
        WIDTH,
        HEIGHT,
        mouseMoving = false,
        mouseMoveChecker,
        mouseX,
        mouseY;複製代碼

繪製單個 star

/* 設置單個 star
     * @param id:id
     * @param x:x座標
     * @param y:y座標
     * @param useCache:是否使用緩存
     * */
    function Star(id, x, y) {
        this.id = id;
        this.x = x;
        this.y = y;
        this.cacheCanvas = document.createElement("canvas");
        this.cacheCtx = this.cacheCanvas.getContext("2d");
        this.r = Math.floor(Math.random() * star_r) + 1;
        this.cacheCtx.width = 6 * this.r;
        this.cacheCtx.height = 6 * this.r;
        var alpha = ( Math.floor(Math.random() * 10) + 1) / star_alpha;
        this.color = "rgba(255,255,255," + alpha + ")";
        if (useCache) {
            this.cache()
        }
    }複製代碼

讓每個 star 動起來

這裏我使用的是原型的方式,將 drawcachemovedie 方法都設置在 Star 的原型上,這樣在使用 new 建立對象的時候,每個 star 均可以繼承這些方法。

Star.prototype = {
        draw : function () {
            if (!this.useCacha) {
                ctx.save();
                ctx.fillStyle = this.color;
                ctx.shadowBlur = this.r * 2;
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
                ctx.closePath();
                ctx.fill();
                ctx.restore();
            } else {
                ctx.drawImage(this.cacheCanvas, this.x - this.r, this.y - this.r);
            }
        },

        cache : function () {
            this.cacheCtx.save();
            this.cacheCtx.fillStyle = this.color;
            this.cacheCtx.shadowColor = "white";
            this.cacheCtx.shadowBlur = this.r * 2;
            this.cacheCtx.beginPath();
            this.cacheCtx.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI);
            this.cacheCtx.closePath();
            this.cacheCtx.fill();
            this.cacheCtx.restore();
        },

        move : function () {
            this.y -= move_distance;
            if (this.y <= -10) {
                this.y += HEIGHT + 10;
            }
            this.draw();
        },

        die : function () {
            stars[this.id] = null;
            delete stars[this.id]
        }
    };複製代碼

繪製 dot

function Dot(id, x, y, useCache) {
        this.id = id;
        this.x = x;
        this.y = y;
        this.r = Math.floor(Math.random() * dot_r)+1;
        this.speed = dot_speeds;
        this.a = dot_alpha;
        this.aReduction = dot_aReduction;
        this.useCache = useCache;
        this.dotCanvas = document.createElement("canvas");
        this.dotCtx = this.dotCanvas.getContext("2d");
        this.dotCtx.width = 6 * this.r;
        this.dotCtx.height = 6 * this.r;
        this.dotCtx.a = 0.5;
        this.color = "rgba(255,255,255," + this.a +")";
        this.dotCtx.color = "rgba(255,255,255," + this.dotCtx.a + ")";
        this.linkColor = "rgba(255,255,255," + this.a/4 + ")";
        this.dir = Math.floor(Math.random()*140)+200;

        if( useCache){
            this.cache()
        }
    }複製代碼

讓每個 dot 動起來

Dot.prototype = {
        draw : function () {
            if( !this.useCache){
                ctx.save();
                ctx.fillStyle = this.color;
                ctx.shadowColor = "white";
                ctx.shadowBlur = this.r * 2;
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
                ctx.closePath();
                ctx.fill();
                ctx.restore();
            }else{
                ctx.drawImage(this.dotCanvas, this.x - this.r * 3, this.y - this.r *3);

            }
        },

        cache : function () {
            this.dotCtx.save();
            this.dotCtx.a  -= this.aReduction;
            this.dotCtx.color = "rgba(255,255,255," + this.dotCtx.a + ")";
            this.dotCtx.fillStyle = this.dotCtx.color;
            this.dotCtx.shadowColor = "white";
            this.dotCtx.shadowBlur = this.r * 2;
            this.dotCtx.beginPath();
            this.dotCtx.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI, false);
            this.dotCtx.closePath();
            this.dotCtx.fill();
            this.dotCtx.restore();
        },
        link : function () {
            if (this.id == 0) return;
            var previousDot1 = getPreviousDot(this.id, 1);
            var previousDot2 = getPreviousDot(this.id, 2);
            var previousDot3 = getPreviousDot(this.id, 3);
            var previousDot4 = getPreviousDot(this.id, 4);


            if (!previousDot1) return;
            ctx.strokeStyle = this.linkColor;
            ctx.moveTo(previousDot1.x, previousDot1.y);
            ctx.beginPath();
            ctx.lineTo(this.x, this.y);
            if (previousDot2 != false) ctx.lineTo(previousDot2.x, previousDot2.y);
            if (previousDot3 != false) ctx.lineTo(previousDot3.x, previousDot3.y);
            if (previousDot4 != false) ctx.lineTo(previousDot4.x, previousDot4.y);

            ctx.stroke();
            ctx.closePath();
        },

        move : function () {


            this.a -= this.aReduction;
            if(this.a <= 0 ){
                this.die();
                return
            }
            this.dotCtx.a  -= this.aReduction;
            this.dotCtx.color = "rgba(255,255,255," + this.dotCtx.a + ")";
            this.color = "rgba(255,255,255," + this.a + ")";
            this.linkColor = "rgba(255,255,255," + this.a/4 + ")";
            this.x = this.x + Math.cos(degToRad(this.dir)) * this.speed;
            this.y = this.y + Math.sin(degToRad(this.dir)) * this.speed;

            this.draw();
            this.link();

        },

        die : function () {
            dots[this.id] = null;
            delete dots[this.id];
        }
    };複製代碼

鼠標移入事件監聽

此外,咱們還須要設置一些其餘的函數和對鼠標移入事件的監聽,這裏就再也不贅述了,感興趣的同窗能夠直接到 github 下載源碼。

canvas 離屏渲染優化

我所使用的離屏優化是基於此文,原文寫的很好,你們感興趣的話能夠去看一下:www.cnblogs.com/axes/p/3567…
由於這個效果以前我也在博客用當作背景過,很多同窗都反應很卡,因此我就找了下優化的教程作了下優化,我發現對性能影響最大的可能就是 canvas 的離屏渲染優化了,這也是 canvas 的最多見優化之一。

名字聽起來很複雜,什麼離屏渲染,其實就是設置緩存,繪製圖像的時候在屏幕以外的地方繪製好,而後再直接拿過來用,這不就是緩存的概念嗎?!︿( ̄︶ ̄)︿.

創建兩個 canvas 標籤,大小一致,一個正常顯示,一個隱藏(緩存用的,不插入dom中),先將結果draw緩存用的canvas上下文中,由於遊離canvas不會形成ui的渲染,因此它不會展示出來,再把緩存的內容整個裁剪再 draw 到正常顯示用的 canvas 上,這樣能優化很多。

其實已經體如今上述的代碼中的,好比,建立 star 的代碼中:

/* 設置單個star
     * @param id:id
     * @param x:x座標
     * @param y:y座標
     * @param useCache:是否使用緩存
     * */
    function Star(id, x, y, useCache) {
        this.id = id;
        this.x = x;
        this.y = y;
        this.useCacha = useCache;
        this.cacheCanvas = document.createElement("canvas");
        this.cacheCtx = this.cacheCanvas.getContext("2d");
        this.r = Math.floor(Math.random() * star_r) + 1;
        this.cacheCtx.width = 6 * this.r;
        this.cacheCtx.height = 6 * this.r;
        var alpha = ( Math.floor(Math.random() * 10) + 1) / star_alpha;
        this.color = "rgba(255,255,255," + alpha + ")";
        if (useCache) {
            this.cache()
        }
    }複製代碼

細心的同窗可能就會發現

this.cacheCanvas = document.createElement("canvas");
        this.cacheCtx = this.cacheCanvas.getContext("2d");複製代碼

這段代碼就是又建立了一個 canvas 標籤,而後再 star 的原型中有一個 cache 方法,這個 cache 方法就是在剛剛建立的 canvas 中繪製 star,而不是直接在原來的 canvas 畫布中繪製的。

cache : function () {
            this.cacheCtx.save();
            this.cacheCtx.fillStyle = this.color;
            this.cacheCtx.shadowColor = "white";
            this.cacheCtx.shadowBlur = this.r * 2;
            this.cacheCtx.beginPath();
            this.cacheCtx.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI);
            this.cacheCtx.closePath();
            this.cacheCtx.fill();
            this.cacheCtx.restore();
        },複製代碼

以後咱們須要將咱們繪製的離屏 canvas 使用 drawImage 方法插入到咱們最早開始建立的 canvas 畫布中。

這裏要注意的是,建立的離屏 canvas 的大小,由於太大的話一樣會浪費性能,因此咱們能夠建立和咱們每個 star 粒子相同的 canvas ,可是這個例子中不適用,要將離屏的 canvas 設置的稍微大一些,由於咱們還須要設置發光的效果(也就是設置陰影)。

發福利

發福利的時間到了~╰( ̄▽ ̄)╭,不少小夥伴對 canvas 不是很感興趣,可是想直接使用這個效果,因而我就將其封裝起來,你只須要引入這個 JS,在 HTML 中添加一個 id 爲 canvas 的標籤,而後設置相應的 CSS 就能夠~

github 下載地址:github.com/sunshine940…

在 README 中有使用方法~由於是第一次本身封裝函數,本身一我的在不停的摸索中前進,因此還有不少的不足,但願有大神能夠指點一二~

相關文章
相關標籤/搜索