Canvas 點線動畫案例

Canvas 點線動畫案例

畫圓:
arc(x, y, r, start, stop)
畫線:
moveTo(x, y) 定義線條開始座標

lineTo(x, y) 定義線條結束座標css

填充:
fill()
繪製:
stroke()

一、畫一個點

初始化
<canvas id="canvas" width="700" height="600">
  瀏覽器不支持canvas!
</canvas>
找到 <canvas> 元素
let canvas = document.getElementById("canvas");
建立 context 對象
let ctx = canvas.getContext("2d");
畫圓
// 座標(x, y)、半徑、開始角度、結束角度、順時針(逆時針)
ctx.arc(70, 80, 30, 0, Math.PI * 2, false);

二、畫不少點

//生成點
for (let i = 0; i < dotsNum; i ++) {
  x = Math.random() * canvas.width;
  y = Math.random() * canvas.height;
  r = Math.random() * 4; // 隨機生成 <4 的半徑值

  ctx.beginPath();
  ctx.arc(x, y, r, 0, 2 * Math.PI);
  ctx.fillStyle = "rgba(0,0,0,.8)";
  ctx.fill();
  ctx.closePath();
}

三、畫兩點一線

肯定兩個點的座標 + lineTo 、moveTo
for (let i = 0; i < 2; i++) {
  ctx.beginPath()
  // 設置原點位置爲(100,100),半徑爲10
  ctx.arc(100 + i * 150, 100 + i * 250, 10, 0, Math.PI * 2, false)

  // 兩個點進行畫線
  ctx.moveTo(100, 100)
  ctx.lineTo(100 + i * 150, 100 + i * 250)

  // 設置線的寬度,單位是像素
  ctx.lineWidth = 2
  ctx.stroke()

  // 實心圓 - 填充顏色,默認是黑色
  // 實心圓 - 畫實心圓
  ctx.fill()
  ctx.closePath()
}

四、畫多點多線

當點不少、元素不少的時候再進行畫線操做會很繁瑣,對於多元素的狀況,建立實例對象,把變量存儲在實例對象上。web

定義一個Dots函數。
var Dots = function () {
  // 畫布
  this.canvas;
  this.ctx;

  // 畫點
  this.x;
  this.y;
  this.r;
};
添加一個用於點的生成的初始化方法。
Dots.prototype = {
  // 初始化點的方法
  init: function (canvas) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext('2d');

    this.x = Math.random() * this.canvas.width;
    this.y = Math.random() * this.canvas.height;
    this.r = Math.random() * 4; // 隨機生成 <4 的半徑值

    this.ctx.beginPath();
    this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
    this.ctx.fillStyle = "black";
    this.ctx.fill();
    this.ctx.closePath();
  }
};
在點與點之間進行畫線,每兩個點之間就有一條線,總共有C(n,2)條線。
// 繪製連線
  for (var i = 0; i < dotsNum; i ++) {
    for (var j = i + 1; j < dotsNum; j ++) {
      var tx = dotsArr[i].x - dotsArr[j].x,
          ty = dotsArr[i].y - dotsArr[j].y,
          // 三角形斜邊長
          s = Math.sqrt(Math.pow(tx, 2) + Math.pow(ty, 2));

      if (s < dotsDistance) {
        ctx.beginPath();
        ctx.moveTo(dotsArr[i].x, dotsArr[i].y);
        ctx.lineTo(dotsArr[j].x, dotsArr[j].y);
        ctx.strokeStyle = 'rgba(0,0,0,'+(dotsDistance-s)/dotsDistance+')';

        ctx.strokeWidth = 1;
        ctx.stroke();
        ctx.closePath();
      }
    }
  }

點與點之間連線canvas

優化點:
限定點與點的連線距離(優化:根據點之間的距離添加連線顏色透明度)瀏覽器

五、requestAnimationFrame

Canvas 畫布的工做原理和顯示器工做原理同樣,都是經過不斷的刷新繪製。瀏覽器的刷新是實時的,而 Canvas 的刷新是手動觸發的,若是咱們只想在 Canvas 上實現靜態的效果,就沒必不斷刷新。dom

requestAnimationFrame是瀏覽器用於定時循環操做的一個接口,相似於setTimeout,主要用途是按對網頁進行重繪。requestAnimationFrame不是本身指定回調函數運行的時間,而是跟着瀏覽器內建的刷新頻率來執行回調。函數

優點

瀏覽器能夠優化並行的動畫動做,更合理的從新排列動做序列,並把可以合併的動做放在一個渲染週期內完成,從而呈現出更流暢的動畫效果,一旦頁面不處於瀏覽器的當前標籤,就會自動中止刷新。優化

使用方式
持續調用 requestAnimFrame

清除動畫調用 cancelAnimationFrame動畫

動效繪製大體路數:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');

// 畫布渲染
var render = function () {
    // 清除畫布
    context.clearRect(0, 0, canvas.width, canvas.height);
    // 繪製(在canvas畫布上繪製圖形的代碼)
    draw();
    // 繼續渲染
    requestAnimationFrame(render);
};
render();

上面的draw()就是在 canvas 畫布上繪製圖形的代碼,可是若是僅僅有上面代碼還不夠,若是是同一個位置不斷刷新,咱們看到的仍是靜止不動的效果,因此還須要一個運動變量。this

運動座標變量:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');

// 座標變量
var x = 0;
// 繪製方法
var draw = function () {
    ball.x = x;
};
// 畫布渲染
var render = function () {
    // 清除畫布
    context.clearRect(0, 0, canvas.width, canvas.height);
    // 位置變化
    x++;
    // 繪製
    draw();
    // 繼續渲染
    requestAnimationFrame(render);
};

render();

六、動起來的多點多線

動的是點,畫的是線prototype

給 Dots 對象添加運動變量,sx 和 sy 兩個值表示點在x軸和y軸的運動量,此處爲在[-2, 2)之間運動。
let Dots = function () {
  // 畫布
  this.canvas;
  this.ctx;

  // 畫點
  this.x;
  this.y;
  this.r;

  // 移動
  // 隨機肯定點的移動速度與方向 速度值在 [-2, 2) 之間 提升數值可加快速度 
  //(Math.random() 隨機返回[0,1)的數)
  this.sx = Math.random() * 4 - 2;
  this.sy = Math.random() * 4 - 2;
};
添加更新點的方法update()
// 更新點位置
  update: function () {
    this.x = this.x + this.sx;
    this.y = this.y + this.sy;

    // 點超出 canvas 範圍時從新初始化
    if (this.x < 0 || this.x > this.canvas.width) {
      this.init(this.canvas);
    }
    if (this.y < 0 || this.y > this.canvas.height) {
      this.init(this.canvas);
    }

    this.ctx.beginPath();
    this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
    this.ctx.fillStyle = "rgba(0,0,0,.6)";
    this.ctx.fill();
    this.ctx.closePath();
  }
動畫及連線

兼容 requestAnimationFrame

let requestAnimFrame = requestAnimationFrame || webkitRequestAnimationFrame || oRequestAnimationFrame || msRequestAnimationFrame;
  requestAnimFrame(animateUpdate); // 兼容不一樣瀏覽器的 requestAnimationFrame

或者使用 setTimeout 向下兼容:

// requestAnimationFrame的向下兼容處理
if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(fn) {
        setTimeout(fn, 17);
    };
}

因爲點的位置不斷變換,所以須要將畫線的操做放在動畫內執行,點的位置 update 一次就執行一次連線。

function animateUpdate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空canvas中原有的內容

    for (let i = 0; i < dotsNum; i ++) {
      dotsArr[i].update();
    }

    // 繪製連線
    for (let i = 0; i < dotsNum; i ++) {
      for (let j = i + 1; j < dotsNum; j ++) {
        let tx = dotsArr[i].x - dotsArr[j].x,
          ty = dotsArr[i].y - dotsArr[j].y,
          // 三角形斜邊長
          s = Math.sqrt(Math.pow(tx, 2) + Math.pow(ty, 2));
        if (s < dotsDistance) {
          ctx.beginPath();
          ctx.moveTo(dotsArr[i].x, dotsArr[i].y);
          ctx.lineTo(dotsArr[j].x, dotsArr[j].y);
          ctx.strokeStyle = 'rgba(0,0,0,'+(dotsDistance-s)/dotsDistance+')';
          ctx.strokeWidth = 1;
          ctx.stroke();
          ctx.closePath();
        }
      }
    }
    // 繼續渲染
    requestAnimFrame(animateUpdate);
  }

相似的例子

星空效果、下雨效果等

你可能不知道的點

一、canvas 畫的圓不是圓,是橢圓

不要在style裏指定 Canvas 的寬度,Canvas 畫布的尺寸的大小和顯示的大小是有很大的區別的,在 canvas 裏面設置的是纔是 Canvas 自己的大小。

若是不給 <canvas>設置 width、height 屬性時,則默認 width 爲 300、height 爲 150, 單位都是 px。也能夠使用 css 屬性來設置寬高,可是如寬高屬性和初始比例不一致,他會出現扭曲。因此, 建議永遠不要使用css屬性來設置<canvas>的寬高。
二、不要企圖經過閉合現有路徑來開始一條新路徑

畫新元素前記得要 beginPath()

  • 無論用 moveTo 把畫筆移動到哪裏,只要不調用beginPath(),一直都是在畫一條路徑
  • fillRect 與 strokeRect 這種直接畫出獨立區域的函數,也不會打斷當前的path
相關文章
相關標籤/搜索