欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

HTML5?Canvas繪制圖形從入門到精通

 更新時間:2022年12月23日 11:08:13   投稿:yin  
Canvas?API(畫布)是在HTML5中新增的標簽用于在網(wǎng)頁實時生成圖像,并且可以操作圖像內(nèi)容,基本上它是一個可以用JavaScript操作的位圖(bitmap)。Canvas?對象表示一個?HTML?畫布元素?-<canvas>。它沒有自己的行為,但是定義了一個?API?支持腳本化客戶端繪圖操作。

Canvas API(畫布)是在HTML5中新增的標簽用于在網(wǎng)頁實時生成圖像,并且可以操作圖像內(nèi)容,基本上它是一個可以用JavaScript操作的位圖(bitmap)。Canvas 對象表示一個 HTML 畫布元素 -<canvas>。它沒有自己的行為,但是定義了一個 API 支持腳本化客戶端繪圖操作。它可以用來制作照片集或者制作簡單(也不是那么簡單)的動畫,甚至可以進行實時視頻處理和渲染。

一、canvas 簡介

<canvas> 標記由 Apple 在 Safari 1.3 Web 瀏覽器中引入。<canvas> 標記和 SVG以及 VML 之間的一個重要的不同是,<canvas> 有一個基于 JavaScript 的繪圖 API,而 SVG 和 VML 使用一個 XML 文檔來描述繪圖。

Mozilla 程序從 Gecko 1.8 (Firefox 1.5) 開始支持 <canvas>, Internet Explorer 從 IE9 開始 <canvas> 。Chrome 和 Opera 9+ 也支持 <canvas>。

二、Canvas基本使用

<canvas id="tutorial" width="300" height="300"></canvas>

2.1 <canvas> 元素

?<canvas> 看起來和 <img> 標簽一樣,只是 <canvas> 只有兩個可選的屬性 width、heigth 屬性,而沒有 src、alt 屬性。

?如果不給 <canvas> 設(shè)置 widht、height 屬性時,則默認 width為300、height 為 150,單位都是 px。也可以使用 css 屬性來設(shè)置寬高,但是如寬高屬性和初始比例不一致,他會出現(xiàn)扭曲。所以,建議永遠不要使用 css 屬性來設(shè)置 <canvas> 的寬高。

替換內(nèi)容

?由于某些較老的瀏覽器(尤其是 IE9 之前的 IE 瀏覽器)或者瀏覽器不支持 HTML 元素 <canvas>,在這些瀏覽器上你應該總是能展示替代內(nèi)容。

?支持 <canvas> 的瀏覽器會只渲染 <canvas> 標簽,而忽略其中的替代內(nèi)容。不支持 <canvas> 的瀏覽器則 會直接渲染替代內(nèi)容。

用文本替換:

<canvas>
    你的瀏覽器不支持 canvas,請升級你的瀏覽器。
</canvas>

<img> 替換:

<canvas>
    <img decoding="async" src="./美女.jpg" alt=""> 
</canvas>

結(jié)束標簽 </canvas> 不可省略。

<img> 元素不同,<canvas> 元素需要結(jié)束標簽(</canvas>)。如果結(jié)束標簽不存在,則文檔的其余部分會被認為是替代內(nèi)容,將不會顯示出來。

2.2 渲染上下文(Thre Rending Context)

?<canvas> 會創(chuàng)建一個固定大小的畫布,會公開一個或多個渲染上下文(畫筆),使用渲染上下文來繪制和處理要展示的內(nèi)容。

? 我們重點研究 2D 渲染上下文。 其他的上下文我們暫不研究,比如, WebGL 使用了基于 OpenGL ES的3D 上下文 ("experimental-webgl") 。

var canvas = document.getElementById('tutorial');
//獲得 2d 上下文對象
var ctx = canvas.getContext('2d');

2.3 檢測支持性

var canvas = document.getElementById('tutorial');

if (canvas.getContext){
  var ctx = canvas.getContext('2d');
  // drawing code here
} else {
  // canvas-unsupported code here
}

2.4 一個簡單的例子

以下實例繪制兩個長方形:

<canvas id="tutorial" width="300" height="300"></canvas>
<script type="text/javascript">
function draw(){
    var canvas = document.getElementById('tutorial');
    if(!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.fillStyle = "rgb(200,0,0)";
      //繪制矩形
    ctx.fillRect (10, 10, 55, 50);
 
    ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
    ctx.fillRect (30, 30, 55, 50);
}
draw();
</script>

三、繪制形狀

3.1 柵格 (grid) 和坐標空間

?如下圖所示,canvas 元素默認被網(wǎng)格所覆蓋。通常來說網(wǎng)格中的一個單元相當于 canvas 元素中的一像素。柵格的起點為左上角,坐標為 (0,0) 。所有元素的位置都相對于原點來定位。所以圖中藍色方形左上角的坐標為距離左邊(X 軸)x 像素,距離上邊(Y 軸)y 像素,坐標為 (x,y)。

?后面我們會涉及到坐標原點的平移、網(wǎng)格的旋轉(zhuǎn)以及縮放等。

3.2 繪制矩形

?<canvas> 只支持一種原生的圖形繪制:矩形。所有其他圖形都至少需要生成一種路徑 (path)。不過,我們擁有眾多路徑生成的方法讓復雜圖形的繪制成為了可能。

canvast 提供了三種方法繪制矩形:

  • 1、fillRect(x, y, width, height):繪制一個填充的矩形。
  • 2、strokeRect(x, y, width, height):繪制一個矩形的邊框。
  • 3、clearRect(x, y, widh, height):清除指定的矩形區(qū)域,然后這塊區(qū)域會變的完全透明。

說明:這 3 個方法具有相同的參數(shù)。

  • x, y:指的是矩形的左上角的坐標。(相對于canvas的坐標原點)
  • width, height:指的是繪制的矩形的寬和高。

function draw(){ var canvas = document.getElementById('tutorial'); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillRect(10, 10, 100, 50); // 繪制矩形,填充的默認顏色為黑色 ctx.strokeRect(10, 70, 100, 50); // 繪制矩形邊框 } draw();

ctx.clearRect(15, 15, 50, 25);

四、繪制路徑 (path)

圖形的基本元素是路徑。

路徑是通過不同顏色和寬度的線段或曲線相連形成的不同形狀的點的集合。

一個路徑,甚至一個子路徑,都是閉合的。

使用路徑繪制圖形需要一些額外的步驟:

  • 創(chuàng)建路徑起始點
  • 調(diào)用繪制方法去繪制出路徑
  • 把路徑封閉
  • 一旦路徑生成,通過描邊或填充路徑區(qū)域來渲染圖形。

下面是需要用到的方法:

  • beginPath()

    新建一條路徑,路徑一旦創(chuàng)建成功,圖形繪制命令被指向到路徑上生成路徑

  • moveTo(x, y)

    把畫筆移動到指定的坐標(x, y)。相當于設(shè)置路徑的起始點坐標。

  • closePath()

    閉合路徑之后,圖形繪制命令又重新指向到上下文中

  • stroke()

    通過線條來繪制圖形輪廓

  • fill()

    通過填充路徑的內(nèi)容區(qū)域生成實心的圖形

4.1 繪制線段

function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.beginPath(); //新建一條path
    ctx.moveTo(50, 50); //把畫筆移動到指定的坐標
    ctx.lineTo(200, 50);  //繪制一條從當前位置到指定坐標(200, 50)的直線.
    //閉合路徑。會拉一條從當前點到path起始點的直線。如果當前點與起始點重合,則什么都不做
    ctx.closePath();
    ctx.stroke(); //繪制路徑。
}
draw();

4.2 繪制三角形邊框

function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(50, 50);
    ctx.lineTo(200, 50);
    ctx.lineTo(200, 200);
      ctx.closePath(); //雖然我們只繪制了兩條線段,但是closePath會closePath,仍然是一個3角形
    ctx.stroke(); //描邊。stroke不會自動closePath()
}
draw();

4.3 填充三角形

function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(50, 50);
    ctx.lineTo(200, 50);
    ctx.lineTo(200, 200);
   
    ctx.fill(); //填充閉合區(qū)域。如果path沒有閉合,則fill()會自動閉合路徑。
}
draw();

4.4 繪制圓弧

有兩個方法可以繪制圓?。?/p>

1、arc(x, y, r, startAngle, endAngle, anticlockwise): 以(x, y) 為圓心,以r 為半徑,從 startAngle 弧度開始到endAngle弧度結(jié)束。anticlosewise 是布爾值,true 表示逆時針,false 表示順時針(默認是順時針)。

注意:

  • 這里的度數(shù)都是弧度。

  • 0 弧度是指的 x 軸正方向。

    radians=(Math.PI/180)*degrees   //角度轉(zhuǎn)換成弧度

2、arcTo(x1, y1, x2, y2, radius): 根據(jù)給定的控制點和半徑畫一段圓弧,最后再以直線連接兩個控制點。

圓弧案例 

function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(50, 50);
    ctx.lineTo(200, 50);
    ctx.lineTo(200, 200);
   
    ctx.fill(); //填充閉合區(qū)域。如果path沒有閉合,則fill()會自動閉合路徑。
}
draw();

arcTo 方法的說明:

這個方法可以這樣理解。繪制的弧形是由兩條切線所決定。

第 1 條切線:起始點和控制點1決定的直線。

第 2 條切線:控制點1 和控制點2決定的直線。

?其實繪制的圓弧就是與這兩條直線相切的圓弧。

4.5 繪制貝塞爾曲線

4.5.1 什么是貝塞爾曲線

貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用于二維圖形應用程序的數(shù)學曲線。一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節(jié)點組成,節(jié)點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。

貝塞爾曲線是計算機圖形學中相當重要的參數(shù)曲線,在一些比較成熟的位圖軟件中也有貝塞爾曲線工具如 PhotoShop 等。在 Flash4 中還沒有完整的曲線工具,而在 Flash5 里面已經(jīng)提供出貝塞爾曲線工具。

4.5.2 繪制貝塞爾曲線

一次貝塞爾曲線其實是一條直線

繪制二次貝塞爾曲線:

quadraticCurveTo(cp1x, cp1y, x, y)

說明:

  • ? 參數(shù) 1 和 2:控制點坐標
  • ? 參數(shù) 3 和 4:結(jié)束點坐標
function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(10, 200); //起始點
    var cp1x = 40, cp1y = 100;  //控制點
    var x = 200, y = 200; // 結(jié)束點
    //繪制二次貝塞爾曲線
    ctx.quadraticCurveTo(cp1x, cp1y, x, y);
    ctx.stroke();
 
    ctx.beginPath();
    ctx.rect(10, 200, 10, 10);
    ctx.rect(cp1x, cp1y, 10, 10);
    ctx.rect(x, y, 10, 10);
    ctx.fill();
 
}
draw();

繪制三次貝塞爾曲線:

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

說明:

  • ? 參數(shù) 1 和 2:控制點 1 的坐標
  • ? 參數(shù) 3 和 4:控制點 2 的坐標
  • ? 參數(shù) 5 和 6:結(jié)束點的坐標
function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(40, 200); //起始點
    var cp1x = 20, cp1y = 100;  //控制點1
    var cp2x = 100, cp2y = 120;  //控制點2
    var x = 200, y = 200; // 結(jié)束點
    //繪制二次貝塞爾曲線
    ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
    ctx.stroke();
 
    ctx.beginPath();
    ctx.rect(40, 200, 10, 10);
    ctx.rect(cp1x, cp1y, 10, 10);
    ctx.rect(cp2x, cp2y, 10, 10);
    ctx.rect(x, y, 10, 10);
    ctx.fill();
 
}
draw();

五、添加樣式和顏色

? 在前面的繪制矩形章節(jié)中,只用到了默認的線條和顏色。

? 如果想要給圖形上色,有兩個重要的屬性可以做到。

  • fillStyle = color 設(shè)置圖形的填充顏色

  • strokeStyle = color 設(shè)置圖形輪廓的顏色

備注:

  • 1. color 可以是表示 css 顏色值的字符串、漸變對象或者圖案對象。
  • 2. 默認情況下,線條和填充顏色都是黑色。
  • 3. 一旦您設(shè)置了 strokeStyle 或者 fillStyle 的值,那么這個新值就會成為新繪制的圖形的默認值。如果你要給每個圖形上不同的顏色,你需要重新設(shè)置 fillStyle 或 strokeStyle 的值。

fillStyle

function draw(){
  var canvas = document.getElementById('tutorial');
  if (!canvas.getContext) return;
  var ctx = canvas.getContext("2d");
  for (var i = 0; i < 6; i++){
    for (var j = 0; j < 6; j++){
      ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' +
        Math.floor(255 - 42.5 * j) + ',0)';
      ctx.fillRect(j * 50, i * 50, 50, 50);
    }
  }
}
draw();

strokeStyle

function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    for (var i = 0; i < 6; i++){
        for (var j = 0; j < 6; j++){
            ctx.strokeStyle = `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`;
            ctx.strokeRect(j * 50, i * 50, 40, 40);
        }
    }
}
draw();
/**
 返回隨機的 [from, to] 之間的整數(shù)(包括from,也包括to)
 */
function randomInt(from, to){
    return parseInt(Math.random() * (to - from + 1) + from);
}

Transparency(透明度)

globalAlpha = transparencyValue: 這個屬性影響到 canvas 里所有圖形的透明度,有效的值范圍是 0.0 (完全透明)到 1.0(完全不透明),默認是 1.0。

? globalAlpha 屬性在需要繪制大量擁有相同透明度的圖形時候相當高效。不過,我認為使用rgba()設(shè)置透明度更加好一些。

1、line style

線寬。只能是正值。默認是 1.0。

起始點和終點的連線為中心,上下各占線寬的一半。

ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(100, 10);
ctx.lineWidth = 10;
ctx.stroke();
 
ctx.beginPath();
ctx.moveTo(110, 10);
ctx.lineTo(160, 10)
ctx.lineWidth = 20;
ctx.stroke()

2. lineCap = type

線條末端樣式。

共有 3 個值:

  • butt:線段末端以方形結(jié)束

  • round:線段末端以圓形結(jié)束

  • square:線段末端以方形結(jié)束,但是增加了一個寬度和線段相同,高度是線段厚度一半的矩形區(qū)域。

    var lineCaps = ["butt", "round", "square"];
     
    for (var i = 0; i < 3; i++){
        ctx.beginPath();
        ctx.moveTo(20 + 30 * i, 30);
        ctx.lineTo(20 + 30 * i, 100);
        ctx.lineWidth = 20;
        ctx.lineCap = lineCaps[i];
        ctx.stroke();
    }
     
    ctx.beginPath();
    ctx.moveTo(0, 30);
    ctx.lineTo(300, 30);
     
    ctx.moveTo(0, 100);
    ctx.lineTo(300, 100)
     
    ctx.strokeStyle = "red";
    ctx.lineWidth = 1;
    ctx.stroke();

    3. lineJoin = type

    同一個 path 內(nèi),設(shè)定線條與線條間接合處的樣式。

    共有 3 個值 round, bevelmiter

  • round 通過填充一個額外的,圓心在相連部分末端的扇形,繪制拐角的形狀。 圓角的半徑是線段的寬度。

  • bevel 在相連部分的末端填充一個額外的以三角形為底的區(qū)域, 每個部分都有各自獨立的矩形拐角。

  • miter(默認) 通過延伸相連部分的外邊緣,使其相交于一點,形成一個額外的菱形區(qū)域。

  • function draw(){
        var canvas = document.getElementById('tutorial');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
     
        var lineJoin = ['round', 'bevel', 'miter'];
        ctx.lineWidth = 20;
     
        for (var i = 0; i < lineJoin.length; i++){
            ctx.lineJoin = lineJoin[i];
            ctx.beginPath();
            ctx.moveTo(50, 50 + i * 50);
            ctx.lineTo(100, 100 + i * 50);
            ctx.lineTo(150, 50 + i * 50);
            ctx.lineTo(200, 100 + i * 50);
            ctx.lineTo(250, 50 + i * 50);
            ctx.stroke();
        }
     
    }
    draw();

  • 4. 虛線

    setLineDash 方法和 lineDashOffset 屬性來制定虛線樣式。 setLineDash 方法接受一個數(shù)組,來指定線段與間隙的交替;lineDashOffset屬性設(shè)置起始偏移量。

    function draw(){
        var canvas = document.getElementById('tutorial');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
     
        ctx.setLineDash([20, 5]);  // [實線長度, 間隙長度]
        ctx.lineDashOffset = -0;
        ctx.strokeRect(50, 50, 210, 210);
    }
    draw();

    備注: getLineDash() 返回一個包含當前虛線樣式,長度為非負偶數(shù)的數(shù)組。

    六、繪制文本

    繪制文本的兩個方法

    canvas 提供了兩種方法來渲染文本:

    round 通過填充一個額外的,圓心在相連部分末端的扇形,繪制拐角的形狀。 圓角的半徑是線段的寬度。

    bevel 在相連部分的末端填充一個額外的以三角形為底的區(qū)域, 每個部分都有各自獨立的矩形拐角。

    miter(默認) 通過延伸相連部分的外邊緣,使其相交于一點,形成一個額外的菱形區(qū)域。

    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial');
        if (!canvas.getContext) return;
        ctx = canvas.getContext("2d");
        ctx.font = "100px sans-serif"
        ctx.fillText("天若有情", 10, 100);
        ctx.strokeText("天若有情", 10, 200)
    }
    draw();

    給文本添加樣式

  • font = value 當前我們用來繪制文本的樣式。這個字符串使用和 CSS font 屬性相同的語法。 默認的字體是 10px sans-serif

  • textAlign = value 文本對齊選項。 可選的值包括:start, end, left, right or center。 默認值是 start。

  • textBaseline = value 基線對齊選項,可選的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默認值是 alphabetic。。

  • direction = value 文本方向。可能的值包括:ltr, rtl, inherit。默認值是 inherit。

  • 七、繪制圖片

    ? 我們也可以在 canvas 上直接繪制圖片。

    7.1 由零開始創(chuàng)建圖片

    var img = new Image();   // 創(chuàng)建一個<img>元素
    img.src = 'myImage.png'; // 設(shè)置圖片源地址

    腳本執(zhí)行后圖片開始裝載。

    繪制 img

    // 參數(shù) 1:要繪制的 img  
    // 參數(shù) 2、3:繪制的 img 在 canvas 中的坐標
    ctx.drawImage(img,0,0);

    注意:考慮到圖片是從網(wǎng)絡(luò)加載,如果 drawImage 的時候圖片還沒有完全加載完成,則什么都不做,個別瀏覽器會拋異常。所以我們應該保證在 img 繪制完成之后再 drawImage。

    var img = new Image();   // 創(chuàng)建img元素
    img.onload = function(){
        ctx.drawImage(img, 0, 0)
    }
    img.src = 'myImage.png'; // 設(shè)置圖片源地址

    7.2 繪制 img 標簽元素中的圖片

    ?img 可以 new 也可以來源于我們頁面的 <img>標簽。

    <img decoding="async" src="./美女.jpg" alt="" width="300"><br>
    <canvas id="tutorial" width="600" height="400"></canvas>
    function draw(){
        var canvas = document.getElementById('tutorial');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
        var img = document.querySelector("img");
        ctx.drawImage(img, 0, 0);
    }
    document.querySelector("img").onclick = function (){
        draw();
    }

    7.3 縮放圖片

    drawImage() 也可以再添加兩個參數(shù):

    drawImage(image, x, y, width, height)

    ?這個方法多了 2 個參數(shù):widthheight,這兩個參數(shù)用來控制 當像 canvas 畫入時應該縮放的大小。

    ctx.drawImage(img, 0, 0, 400, 200)

    7.4 切片(slice)

    drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

    第一個參數(shù)和其它的是相同的,都是一個圖像或者另一個 canvas 的引用。

    其他 8 個參數(shù):

    前 4 個是定義圖像源的切片位置和大小,后 4 個則是定義切片的目標顯示位置和大小。

    八、狀態(tài)的保存和恢復

    Saving and restoring state 是繪制復雜圖形時必不可少的操作。

    save()restore()

    saverestore 方法是用來保存和恢復 canvas 狀態(tài)的,都沒有參數(shù)。

    ?Canvas 的狀態(tài)就是當前畫面應用的所有樣式和變形的一個快照。

    1、關(guān)于 save() :Canvas狀態(tài)存儲在棧中,每當save()方法被調(diào)用后,當前的狀態(tài)就被推送到棧中保存。

    一個繪畫狀態(tài)包括:

  • 當前應用的變形(即移動,旋轉(zhuǎn)和縮放)

  • strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值

  • 當前的裁切路徑(clipping path

    可以調(diào)用任意多次 save方法(類似數(shù)組的 push())。

  • 可以調(diào)用任意多次 save方法(類似數(shù)組的push())。

  • 2、關(guān)于restore():每一次調(diào)用 restore 方法,上一個保存的狀態(tài)就從棧中彈出,所有設(shè)定都恢復(類似數(shù)組的 pop())。

    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
     
        ctx.fillRect(0, 0, 150, 150);   // 使用默認設(shè)置繪制一個矩形
        ctx.save();                  // 保存默認狀態(tài)
     
        ctx.fillStyle = 'red'       // 在原有配置基礎(chǔ)上對顏色做改變
        ctx.fillRect(15, 15, 120, 120); // 使用新的設(shè)置繪制一個矩形
     
        ctx.save();                  // 保存當前狀態(tài)
        ctx.fillStyle = '#FFF'       // 再次改變顏色配置
        ctx.fillRect(30, 30, 90, 90);   // 使用新的配置繪制一個矩形
     
        ctx.restore();               // 重新加載之前的顏色狀態(tài)
        ctx.fillRect(45, 45, 60, 60);   // 使用上一次的配置繪制一個矩形
     
        ctx.restore();               // 加載默認顏色配置
        ctx.fillRect(60, 60, 30, 30);   // 使用加載的配置繪制一個矩形
    }
    draw();

    九、變形

    9.1 translate

    translate(x, y)

    用來移動 canvas原點到指定的位置

    ?translate 方法接受兩個參數(shù)。x 是左右偏移量,y 是上下偏移量,如右圖所示。

    在做變形之前先保存狀態(tài)是一個良好的習慣。大多數(shù)情況下,調(diào)用 restore 方法比手動恢復原先的狀態(tài)要簡單得多。又如果你是在一個循環(huán)中做位移但沒有保存和恢復 canvas 的狀態(tài),很可能到最后會發(fā)現(xiàn)怎么有些東西不見了,那是因為它很可能已經(jīng)超出 canvas 范圍以外了。

    ? 注意:translate 移動的是 canvas 的坐標原點(坐標變換)。

    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial1');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
        ctx.save(); //保存坐原點平移之前的狀態(tài)
        ctx.translate(100, 100);
        ctx.strokeRect(0, 0, 100, 100)
        ctx.restore(); //恢復到最初狀態(tài)
        ctx.translate(220, 220);
        ctx.fillRect(0, 0, 100, 100)
    }
    draw();

    9.2 rotate

    rotate(angle)
     

    旋轉(zhuǎn)坐標軸。這個方法只接受一個參數(shù):旋轉(zhuǎn)的角度(angle),它是順時針方向的,以弧度為單位的值。旋轉(zhuǎn)的中心是坐標原點。

     
    var ctx;
    function draw(){
      var canvas = document.getElementById('tutorial1');
      if (!canvas.getContext) return;
      var ctx = canvas.getContext("2d");
     
      ctx.fillStyle = "red";
      ctx.save();
     
      ctx.translate(100, 100);
      ctx.rotate(Math.PI / 180 * 45);
      ctx.fillStyle = "blue";
      ctx.fillRect(0, 0, 100, 100);
      ctx.restore();
     
      ctx.save();
      ctx.translate(0, 0);
      ctx.fillRect(0, 0, 50, 50)
      ctx.restore();
    }
    draw();

    9.3 scale

    scale(x, y)

    我們用它來增減圖形在 canvas 中的像素數(shù)目,對形狀,位圖進行縮小或者放大。

    scale方法接受兩個參數(shù)。x,y 分別是橫軸和縱軸的縮放因子,它們都必須是正值。值比 1.0 小表示縮 小,比 1.0 大則表示放大,值為 1.0 時什么效果都沒有。

    ? 默認情況下,canvas 的 1 單位就是 1 個像素。舉例說,如果我們設(shè)置縮放因子是 0.5,1 個單位就變成對應 0.5 個像素,這樣繪制出來的形狀就會是原先的一半。同理,設(shè)置為 2.0 時,1 個單位就對應變成了 2 像素,繪制的結(jié)果就是圖形放大了 2 倍。

    9.4 transform (變形矩陣)

    transform(a, b, c, d, e, f)

    • a (m11): Horizontal scaling.
    • b (m12): Horizontal skewing.
    • c (m21):? Vertical skewing.
    • d (m22):? Vertical scaling.
    • e (dx):? Horizontal moving.
    • f (dy):? Vertical moving.
    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial1');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
        ctx.transform(1, 1, 0, 1, 0, 0);
        ctx.fillRect(0, 0, 100, 100);
    }
    draw();

    十、合成

    ?

    在前面的所有例子中、,我們總是將一個圖形畫在另一個之上,對于其他更多的情況,僅僅這樣是遠遠不夠的。比如,對合成的圖形來說,繪制順序會有限制。不過,我們可以利用 globalCompositeOperation 屬性來改變這種狀況。

    globalCompositeOperation = type
    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial1');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
     
        ctx.fillStyle = "blue";
        ctx.fillRect(0, 0, 200, 200);
     
        ctx.globalCompositeOperation = "source-over"; //全局合成操作
        ctx.fillStyle = "red";
        ctx.fillRect(100, 100, 200, 200);
    }
    draw();

    :下面的展示中,藍色是原有的,紅色是新的。

    type 是下面 13 種字符串值之一:

    1、這是默認設(shè)置,新圖像會覆蓋在原有圖像。

    2. source-in

    僅僅會出現(xiàn)新圖像與原來圖像重疊的部分,其他區(qū)域都變成透明的。(包括其他的老圖像區(qū)域也會透明)

    3. source-out

    僅僅顯示新圖像與老圖像沒有重疊的部分,其余部分全部透明。(老圖像也不顯示)

    4. source-atop

    新圖像僅僅顯示與老圖像重疊區(qū)域。老圖像仍然可以顯示。

    5. destination-over

    新圖像會在老圖像的下面。

    6. destination-in

    僅僅新老圖像重疊部分的老圖像被顯示,其他區(qū)域全部透明。

    7. destination-out

    僅僅老圖像與新圖像沒有重疊的部分。 注意顯示的是老圖像的部分區(qū)域。

    8. destination-atop

    老圖像僅僅僅僅顯示重疊部分,新圖像會顯示在老圖像的下面。

    9. lighter

    新老圖像都顯示,但是重疊區(qū)域的顏色做加處理。

    10. darken

    保留重疊部分最黑的像素。(每個顏色位進行比較,得到最小的)

    blue: #0000ff
    red: #ff0000

    所以重疊部分的顏色:#000000。

    11. lighten

    保證重疊部分最量的像素。(每個顏色位進行比較,得到最大的)

    blue: #0000ff
    red: #ff0000

    所以重疊部分的顏色:#ff00ff。

    12. xor

    重疊部分會變成透明。

    13. copy

    只有新圖像會被保留,其余的全部被清除(邊透明)。

    十一、裁剪路徑

    clip()
    ?

    把已經(jīng)創(chuàng)建的路徑轉(zhuǎn)換成裁剪路徑。

    ?

    裁剪路徑的作用是遮罩。只顯示裁剪路徑內(nèi)的區(qū)域,裁剪路徑外的區(qū)域會被隱藏。

    ?

    注意:clip() 只能遮罩在這個方法調(diào)用之后繪制的圖像,如果是 clip() 方法調(diào)用之前繪制的圖像,則無法實現(xiàn)遮罩。

    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial1');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
     
        ctx.beginPath();
        ctx.arc(20,20, 100, 0, Math.PI * 2);
        ctx.clip();
     
        ctx.fillStyle = "pink";
        ctx.fillRect(20, 20, 100,100);
    }
    draw();

    十二、動畫

    動畫的基本步驟

    清空 canvas 再繪制每一幀動畫之前,需要清空所有。清空所有最簡單的做法就是 clearRect() 方法。

    保存 canvas 狀態(tài) 如果在繪制的過程中會更改 canvas 的狀態(tài)(顏色、移動了坐標原點等),又在繪制每一幀時都是原始狀態(tài)的話,則最好保存下 canvas 的狀態(tài)

    繪制動畫圖形這一步才是真正的繪制動畫幀

    恢復 canvas 狀態(tài)如果你前面保存了 canvas 狀態(tài),則應該在繪制完成一幀之后恢復 canvas 狀態(tài)。

    控制動畫

    我們可用通過 canvas 的方法或者自定義的方法把圖像會知道到 canvas 上。正常情況,我們能看到繪制的結(jié)果是在腳本執(zhí)行結(jié)束之后。例如,我們不可能在一個 for 循環(huán)內(nèi)部完成動畫。

    也就是,為了執(zhí)行動畫,我們需要一些可以定時執(zhí)行重繪的方法。

    一般用到下面三個方法:

    setInterval()

    setTimeout()

    requestAnimationFrame()

案例:太陽系 

let sun;
let earth;
let moon;
let ctx;
function init(){
    sun = new Image();
    earth = new Image();
    moon = new Image();
    sun.src = "sun.png";
    earth.src = "earth.png";
    moon.src = "moon.png";
 
    let canvas = document.querySelector("#solar");
    ctx = canvas.getContext("2d");
 
    sun.onload = function (){
        draw()
    }
 
}
init();
function draw(){
    ctx.clearRect(0, 0, 300, 300); //清空所有的內(nèi)容
    /*繪制 太陽*/
    ctx.drawImage(sun, 0, 0, 300, 300);
 
    ctx.save();
    ctx.translate(150, 150);
 
    //繪制earth軌道
    ctx.beginPath();
    ctx.strokeStyle = "rgba(255,255,0,0.5)";
    ctx.arc(0, 0, 100, 0, 2 * Math.PI)
    ctx.stroke()
 
    let time = new Date();
    //繪制地球
    ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())
    ctx.translate(100, 0);
    ctx.drawImage(earth, -12, -12)
 
    //繪制月球軌道
    ctx.beginPath();
    ctx.strokeStyle = "rgba(255,255,255,.3)";
    ctx.arc(0, 0, 40, 0, 2 * Math.PI);
    ctx.stroke();
 
    //繪制月球
    ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());
    ctx.translate(40, 0);
    ctx.drawImage(moon, -3.5, -3.5);
    ctx.restore();
 
    requestAnimationFrame(draw);
}

相關(guān)文章

最新評論