Canvas
介紹
SVG是構建XML樹的方式來達到繪製圖形的,canvas是通過呼叫相關的方法來繪製圖形的。
區別:SVG繪製圖形,通過移除或者更改DOM方式來而使用canvas需要把圖片從新擦除。
繪製的API在繪製上下文中定義。而不在畫布中定義。
需要獲得上下文物件的時候,需要呼叫畫布的getContext方法,獲得繪畫的上下文。
畫布元素和上下文,屬於兩個不同的物件,其中畫布元素為canvas畫布,而上下文物件為繪製需要的上下文。
關於3D圖形,即,webGL 為封裝了基本的OPENGL,當呼叫webGL的時候,其瀏覽器會呼叫OpenGL相關的API
繪製圓
<!DOCTYPE html> <html lang="zh_CN" xmlns="http://www.w3.org/1999/html"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>第一個園</br> <canvas id="square" width="10" height="100"> </canvas> </div> <div> 第二個園 <canvas id="circle" width="10" height="10"> </canvas> </div> <script src="./js/index.js" charset="UTF-8"></script> </body> </html>
// 獲取畫布元素 let canvas = document.getElementById("square"); // 獲取繪製2D元素上下文 let context = canvas.getContext("2d"); // 設定填充顏色為紅色 context.fillStyle = "#f00"; // 填充一個正方形 context.fillRect(10,0,10,10);
繪製線段,填充多邊形
// 獲取畫布元素 let canvas = document.getElementById("square"); // 獲取繪製2D元素上下文 let context = canvas.getContext("2d"); // 開始一條路徑 context.beginPath(); // 從100,100 開始定義一條新的子路徑 context.moveTo(100,100); // 從100 100 到 200 200 繪製一條線段 context.lineTo(200,200); // 從200 200 到 100 200 繪製一條線段 context.lineTo(100,200); // 從100 200 到 100 100 繪製一條路徑 context.lineTo(100,100); // 繪製邊 context.stroke(); // 進行填充 context.fill();
繪製多邊形
以五邊形為例子,
var canvas = document.getElementById("square"); var context = canvas.getContext("2d"); // 繪製一個以100,100為中心,半徑為20的櫃子N變形,每個定點均勻分佈在圓角上,第一個定點放置在最上下 // 偏轉角度為0 // 開始定義一條子路徑 context.moveTo(100 + 20 * Math.sin(0), 100 - 20 * Math.cos(0)); // 計算兩個頂點之間夾角 // 其中2π為一個園,除以邊數,得到需要旋轉的角度 var delta = 2 * Math.PI/5; console.log(delta); // 迴圈剩餘每個頂點 var angle = 0; for(var i = 1; i < 5; i++){ //角度累加 angle += delta; // 通過旋轉繪製下一個頂點,不斷的旋轉繪製 context.lineTo(100 + 20 * Math.sin(angle), 100 - 20*Math.cos(angle)); } // 最後一個頂點和起點進行連線 context.closePath(); // 從新開始一條新路徑 context.stroke(); context.fill();
同理,畫圓
var canvas = document.getElementById("square"); var context = canvas.getContext("2d"); // 繪製一個以100,100為中心,半徑為20的櫃子N變形,每個定點均勻分佈在圓角上,第一個定點放置在最上下 // 偏轉角度為0 // 開始定義一條子路徑 context.moveTo(100 + 20 * Math.sin(0), 100 - 20 * Math.cos(0)); // 計算兩個頂點之間夾角 // 其中2π為一個園,除以邊數,得到需要旋轉的角度 var delta = 2 * Math.PI/500000; console.log(delta); // 迴圈剩餘每個頂點 var angle = 0; for(var i = 1; i < 500000; i++){ //角度累加 angle += delta; // 通過旋轉繪製下一個頂點,不斷的旋轉繪製 context.lineTo(100 + 20 * Math.sin(angle), 100 - 20*Math.cos(angle)); } // 最後一個頂點和起點進行連線 context.closePath(); // 從新開始一條新路徑 context.stroke(); context.fill();
非零繞數原則
要檢測一個點p是否在路徑內部,使用非零繞數原則,即,一條從點p出發沿著任意方向無限延伸,或者一直延伸到路徑所在的區域外某點的射線,現在從0開始初始化一個計數器,對穿過這條射線的路徑進行列舉,每當一條路徑順時針方向穿過射線的時候,計數器加1,逆時針減1,最後,列舉完所有路徑以後,如果計時器的值不是0,那麼就認為p在路徑內,反過來,計數器的值為0,p在路徑外。
js根據非零繞數原則確定那個在路徑內,那個在路徑外,用於進行填充。
圖形屬性
可以通過設定畫布上下文的fillStyle等屬性,設定圖形的屬性,例如對畫布上下文的fillStyle的屬性進行設定,即,可以設定出填充時的顏色,漸變,圖案等樣式。
對於canvas來說,每次獲取上下文物件的時候,都會返回同一個上下文物件,即,上下文物件為單例的。
還可以使用save方法,把當前的狀態,壓入已經儲存的棧中,呼叫restore方法,把狀態進行恢復,即彈棧。
畫布尺寸座標
畫布的預設的座標系為左上角的座標原點(0,0),右邊數值大,下數值大,使用浮點數指定座標,但不會自動轉換為整數,會用反鋸齒的方式,模擬填充部分元素。
畫布尺寸不能隨意改變,對任意屬性進行操作,都會清空整個畫布。
座標系變換
每一個點的座標都會對映到css畫素上,css畫素會對映到一個或多個裝置畫素。
畫布中的特定操作,屬性使用預設座標系。
畫布還有當前變換矩陣。
畫布還有當前變換矩陣,當前變換矩陣作為圖形狀態的一部分。矩陣定義了當前畫布的座標系。
畫布的操作會把該點對映到當前的座標系中。
座標變換
當呼叫c.translate(dx,dy)方法的時候,會進行如下變換
translate會進行座標的上下移動
x' = x + dy; y' = y + dy;
縮放
如要進行縮放,進行的是如下的變換
x' = sx * x; y' = sy * y;
進行旋轉操作,進行的是如下變換
x' = x * cos(a) - y * sin(a); y' = y * cos(a) - x * sin(a);
如果要先變換再伸縮,進行如下變換
需要先把現有座標系對映成為座標系中的點x’, y' 然後再變換到x‘’ , y‘’
x'' = sx*x + dx; y'' = sy*y + dy;
如果變換順序相反進行如下變換
x'' = sx*(x + dx); y'' = sy*(y + dy);
這種變換稱為仿射變換,並且仿射變換會修改點的距離和線段間的夾角。對於平行線來說,仿射變換也會保持平行。仿射變換用6個引數描述成為如下表述
x' = ax + cy + e; y' = bx + dy + f;
通過傳入引數實現仿射變換
對於座標變換來說,除非進行重新整理,否則,已經繪製的圖形,不會進行消失,所有的變換,都不能對已經繪製的圖形進行更改。栗子如下
var canvas = document.getElementById("square"); var context = canvas.getContext("2d"); // 通過座標變換實現科赫雪花 // 開始一條路徑 context.beginPath(); // 開始繪製子路徑 context.moveTo(100,100); // 繼續繪製 context.lineTo(200,200); // 繼續繪製 context.lineTo(200,200); // 進行繪製邊 context.stroke(); context.translate(200,200); // 開始一條路徑 context.beginPath(); // 開始繪製子路徑 context.moveTo(100,100); // 繼續繪製 context.lineTo(200,200); // 繼續繪製 context.lineTo(200,200); // 進行繪製邊 context.stroke();
已經繪製的圖形不會進行改變,改變的是已經繪製的圖形
科赫雪花
var canvas = document.getElementById("square"); var context = canvas.getContext("2d"); // 通過座標變換實現科赫雪花 // 當前狀態入棧 function leg(n) { // 儲存狀態 context.save(); // 遞迴畫 if(n == 0){ context.lineTo(50, 0); }else{ // 定義為v字型 context.scale(1/2,1/2); // 遞迴第一條 context.rotate(60 * (Math.PI / 180)); leg(n - 1); context.rotate(-120 * (Math.PI / 180)); leg(n - 1); } // 座標恢復變換 context.restore(); // 恢復下一個座標為0,0 context.translate(50, 0); } context.save(); context.moveTo(50, 50); // 繪製第一條 leg(1); context.stroke();
繪製填充曲線
繪製一些常見的圖形
var canvas = document.getElementById("square"); var context = canvas.getContext("2d"); // 工具函式,角度轉弧度 function rads(x) { return Math.PI * x / 180; } // 繪製園 context.beginPath(); context.arc(100,100,40, 0, rads(360), false); context.stroke(); context.fill();
同理繪製貝塞爾曲線也是同理。
顏色,透明度,漸變,圖案
繪製一個漸變
需要使用createLinearGradient獲取一個進行漸變的上下文,對這個上下文進行處理。然後其顏色設定為這個漸變的上下文,即,fillStyle屬性。
線段繪製
封頂
對於線段,有三種封頂方式,即,butt,square,round
在繪製圖形以後,會引數尖角,圓角,平角,三種。
lineCap屬性
文字
和css類似,基線問題。
裁剪
直接調動clip即可,當前路徑也會被裁剪進入,路徑外的統統不會顯示。
陰影
設定shadow屬性即可
圖片
畫布API支援點陣圖圖片,同時也支援canvas匯出成為圖片。
// 建立一個img元素 let img = document.createElement("img"); // 設定src屬性 img.src = canvas.toDataURL(); // 追加到文件後面 document.body.appendChild(img);
合成
一些api不在闡述
畫素操作
呼叫getImageDate方法返回ImageDate物件
使用createImageDate()可以建立畫素容器
進行動態模糊先獲取畫素的ImageDate物件,然後再獲取該物件的data屬性,該data為一個數組。為一個維陣列。每四個元素代表紅色分量,綠色分量,藍色分量,透明度分量。(Alpha分量)
其色素直為0-1,即,陣列元素中儲存的陣列為色素值。
每四個每四個元素遍歷。然後把其色素值的1/ n + 上一個色塊的m/n然後賦值給新的色塊,程式碼如下
// row為行數 for(var row = 0; row < height; row++){ // 獲得每行第二個元素的偏移量,其中width為行的色素塊。 var i = row * width * 4; // 每4個的色素值進行處理 for(var col = 1; col < width; col++, i+=4){ // 對紅色分量處理 data[i] = (data[i] + data[i - 4] * m) / n; // 對綠色分量處理 data[i + 1] = (data[i + 1] + data[i + 1 - 4] * m) / n; // 對藍色分量處理 data[i + 2] = (data[i + 2] + data[i + 2 - 4] * m) / n; // 對透明度分量處理 data[i + 3] = (data[i + 3] + data[i + 3 - 4] * m) / n; } }
然後把其色素塊進行復制回去即可。
其中每個畫素佔據一個位元組,一個四個位元組。
命中檢測
isPointInPath方法用來確定一個點是否落在當前路徑中。
即命中檢測。
命中檢測可以和滑鼠事件相互轉化
但是座標需要進行轉換。