畫圓:
arc(x, y, r, start, stop)
畫線:
moveTo(x, y) 定義線條開始坐標
lineTo(x, y) 定義線條結束坐標
填充:
fill()
繪制:
stroke()
1、畫一個點
初始化
? 瀏覽器不支持canvas!
找到??元素
let canvas = document.getElementById("canvas");
創建?context?對象
let ctx = canvas.getContext("2d");
畫圓
// 坐標(x, y)、半徑、開始角度、結束角度、順時針(逆時針)
ctx.arc(70,80,30,0, Math.PI*2, false);
2、畫很多點
//生成點
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();
}
3、畫兩點一線
確定兩個點的坐標 + 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 =2ctx.stroke()
????// 實心圓 - 填充顏色,默認是黑色// 實心圓 - 畫實心圓
????ctx.fill()?
?????ctx.closePath()
}
4、畫多點多線
當點很多、元素很多的時候再進行畫線操作會很繁瑣,對于多元素的情況,創建實例對象,把變量存儲在實例對象上。
定義一個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();? ??
? }? ?
?}??
}
點與點之間連線
優化點:
限定點與點的連線距離(優化:根據點之間的距離添加連線顏色透明度)
5、requestAnimationFrame
Canvas 畫布的工作原理和顯示器工作原理一樣,都是通過不斷的刷新繪制。瀏覽器的刷新是實時的,而 Canvas 的刷新是手動觸發的,如果我們只想在 Canvas 上實現靜態的效果,就沒必不斷刷新。
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 畫布上繪制圖形的代碼,但是如果僅僅有上面代碼還不夠,如果是同一個位置不斷刷新,我們看到的還是靜止不動的效果,所以還需要一個運動變量。
?運動坐標變量:
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();
6、動起來的多點多線
動的是點,畫的是線
給 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);?
?}
附加知識點
1、canvas 畫的圓不是圓,是橢圓
不要在style里指定 Canvas 的寬度,Canvas 畫布的尺寸的大小和顯示的大小是有很大的區別的,在 canvas 里面設置的是才是 Canvas 本身的大小。
如果不給<canvas>設置 width、height 屬性時,則默認 width 為 300、height 為 150, 單位都是 px。也可以使用 css 屬性來設置寬高,但是如寬高屬性和初始比例不一致,他會出現扭曲。所以,建議永遠不要使用css屬性來設置<canvas>的寬高。
2、不要企圖通過閉合現有路徑來開始一條新路徑
畫新元素前記得要 beginPath()
不管用 moveTo 把畫筆移動到哪里,只要不調用beginPath(),一直都是在畫一條路徑
fillRect 與 strokeRect 這種直接畫出獨立區域的函數,也不會打斷當前的path