JavaScript圖形實例:Canvas API

来源:https://www.cnblogs.com/cs-whut/archive/2020/07/10/13280691.html
-Advertisement-
Play Games

1.Canvas概述 Canvas API(畫布)用於在網頁實時生成圖像,並且可以操作圖像內容,基本上它是一個可以用JavaScript操作的點陣圖(bitmap)。 要使用HTML5在瀏覽器視窗中繪製圖形,首先需要在HTML文檔中新建一個canvas網頁元素。一般方法如下: <canvas id=" ...


1.Canvas概述

      Canvas API(畫布)用於在網頁實時生成圖像,並且可以操作圖像內容,基本上它是一個可以用JavaScript操作的點陣圖(bitmap)。

      要使用HTML5在瀏覽器視窗中繪製圖形,首先需要在HTML文檔中新建一個canvas網頁元素。一般方法如下:

       <canvas id="myCanvas" width="400" height="300">

             您的瀏覽器不支持canvas!

      </canvas>

      上面這段代碼,表示建立了一個名為“myCanvas”的canvas網頁元素,它就是一塊畫布,該畫布的寬為400,高為300。有了這塊畫布,我們就可以使用JavaScript編寫程式,利用Canvas API在這塊畫布上繪製圖形。如果所用瀏覽器不支持Canvas API,則就會顯示canvas標簽中間的文字——“您的瀏覽器不支持canvas!”。

      每個canvas網頁元素都有一個對應的context對象(上下文對象),Canvas API定義在這個context對象上面。為了在canvas上繪製圖形,必須先得到一個畫布上下文對象的引用。為此,使用JavaScript編寫程式段如下:

      var canvas = document.getElementById('myCanvas');  // 取得網頁中的畫布對象

      var ctx = canvas.getContext('2d');       // 得到畫布上下文對象ctx

      上面代碼中,getContext方法指定參數2d,表示該canvas對象用於生成2D圖案(即平面圖案)。如果參數是3d,就表示用於生成3D圖像(即立體圖案)。

      當使用一個canvas元素的getContext(“2d”)方法時,返回的是CanvasRenderingContext2D對象,其內部表現為笛卡爾平面坐標。這就是Canvas畫布提供的一個用來作圖的平面空間,該空間的每個點都有自己的坐標,x表示橫坐標,y表示縱坐標。原點(0, 0)位於畫布左上角,x軸的正向是原點向右,y軸的正向是原點向下。

      每一個canvas元素僅有一個上下文對象。得到了這個上下文對象,就可以利用這個對象的屬性和方法進行圖形繪製了。

2.繪圖方法

2.1  繪製路徑

       在Canvas API中,上下文CanvasRenderingContext2D對象提供了一系列與圖形繪製相關的屬性和方法。其中,與路徑繪製相關的方法如下:

       void beginPath();                  //  開始繪製路徑

       void closePath();                   // 結束路徑繪製

       void moveTo(in float x, in float y);    // 設置線段的起點

       void lineTo(in float x, in float y);      // 設置線段的終點

       void bezierCurveTo(in float cp1x, in float cp1y, in float cp2x, in float cp2y, in float x, in float y);               // 繪製一條三次貝塞爾曲線

       void quadraticCurveTo(in float cpx, in float cpy, in float x, infloat y);                   // 繪製一條二次貝塞爾曲線

       void stroke();         // 給透明的線段著色,從而完成線段繪製

       void fill();            // 給閉合路徑填充顏色,填充色由fillStyle屬性指定

       與繪製路徑相關的屬性有:

       attribute float lineWidth;      // 線的寬度,預設為1

       attribute any strokeStyle;      // 著色的顏色,預設為black(黑色)

       attribute any fillStyle;         // 填充顏色,預設為black(黑色)

       attribute DOMString lineCap;  // 線段的箭頭樣式,僅有三個選項:butt(預設值)、round、square,其他值忽略

       下麵通過幾個例子來說明繪製路徑的方法和屬性的使用。

      例1  繪製一條從(20,20)到(200,20)的一條紅色橫線。

<!DOCTYPE html>

<head>

<title>繪圖方法的使用</title>

<script type="text/javascript">

  function draw(id)

  {

     var canvas=document.getElementById(id);

     if (canvas==null)

        return false;

     var ctx=canvas.getContext('2d');

     ctx.beginPath();        // 開始路徑繪製

     ctx.moveTo(20, 20);    // 設置路徑起點,坐標為(20,20)

     ctx.lineTo(200, 20);    // 繪製一條到(200,20)的直線

     ctx.lineWidth = 1.0;    // 設置線寬

     ctx.strokeStyle = "#FF0000"; // 設置線條顏色為紅色

     ctx.stroke();          // 進行線的著色,這時整條線才變得可見

   }

</script>

</head>

<body onload="draw('myCanvas');">

<canvas id="myCanvas" width="400" height="300" style="border:3px double #996633;">

</canvas>

</body>

</html>

       將上述HTML代碼保存到一個html文本文件中,再在瀏覽器中打開包含這段HTML代碼的html文件,可以看到在畫布中繪製出一條長為180的紅色橫線。

       下麵的例子中我們不再給出完整的HTML文件內容,只給出JavaScript編寫的與圖形繪製直接相關的代碼。例如例1源文件中加了註釋的6條語句。讀者需要自己試一試時,只需把下列各例給出的代碼去覆蓋例1中的6條註釋語句,其餘部分保持不變即可。

       在繪製路徑時,moveto和lineto方法可以多次使用。最後,還可以使用closePath方法,自動繪製一條當前點到起點的線段,形成一個封閉圖形,省卻使用一次lineto方法。

       例2  在畫布中繪製一個紅色邊框的直角三角形和一個藍色邊框的等腰三角形。

     ctx.beginPath();

     ctx.moveTo(20,20);

     ctx.lineTo(20,100);   // 垂直直角邊

     ctx.lineTo(70,100);   // 水平直角邊

     ctx.lineTo(20,20);    // 斜邊

     ctx.strokeStyle="red";

     ctx.stroke();         // 進行著色,使得線段可見

     ctx.beginPath();

     ctx.moveTo(40,120);

     ctx.lineTo(20,180);   // 左邊的腰

     ctx.lineTo(60,180);   // 底邊

     ctx.closePath();      // 右邊的腰是通過自動封閉繪製得到

     ctx.strokeStyle="blue";

     ctx.stroke();  

       例3  線段箭頭的三種樣式的比較。

ctx.lineWidth=10;

ctx.strokeStyle="red";

ctx.beginPath();

ctx.lineCap='butt';

ctx.moveTo(100,50);

ctx.lineTo(250,50);

ctx.stroke();

ctx.beginPath();

ctx.lineCap='round';

ctx.moveTo(100,80);

ctx.lineTo(250,80);

ctx.stroke();

ctx.beginPath();

ctx.lineCap='square';

ctx.moveTo(100,110);

ctx.lineTo(250,110);

ctx.stroke(); 

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖1所示的三條線段。其中,第1根線段箭頭樣式為“butt”(為預設值),線段的頭和尾都是長方形,也就是不做任何的處理;第2根線段箭頭樣式為“round”,線段的頭和尾都增加一個半圓形的箭頭;第3根線段的樣式為“square”,線段的頭和尾都增加一個長方形,長度為線寬一半,高度為線寬。

圖1  繪製的3根紅色線段

      例4  繪製紅綠藍3個實心三角形。

     ctx.beginPath();

     ctx.moveTo(20,20);

     ctx.lineTo(20,100); 

     ctx.lineTo(70,100); 

     ctx.lineTo(20,20);  

     ctx.fillStyle="red";

     ctx.fill();         // 填充紅色三角形

     ctx.beginPath();

     ctx.moveTo(40,120);

     ctx.lineTo(20,180);

     ctx.lineTo(60,180);

     ctx.closePath();   

     ctx.fillStyle="green";

     ctx.fill();         // 填充綠色三角形

     ctx.beginPath();

     ctx.moveTo(70,20);

     ctx.lineTo(120,180);

     ctx.lineTo(140,150);

     ctx.fillStyle="blue";

     ctx.fill();         // 填充藍色三角形

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖2所示的三個實心三角形。

圖2 三個實心三角形

      通過上面的示例,我們可以知道,填充的形狀應該是封閉的路徑。如果路徑未關閉,那麼 fill() 方法會從路徑結束點到開始點之間添加一條線,以關閉該路徑,然後填充該路徑。例如,圖2中藍色三角形構成的路徑並未關閉,調用fill()時,會自動添加直線關閉。

      貝賽爾曲線(Bezier curve)是電腦圖形學中相當重要的參數曲線。Canvas API中提供了兩個繪製貝塞爾曲線的方法。其中:

       quadraticCurveTo() 方法用於繪製一條二次貝塞爾曲線。

       二次貝塞爾曲線需要兩個點。第一個點(cpx,cpy)是用於二次貝塞爾計算中的控制點,第二個點(x,y)是曲線的結束點。曲線的開始點是當前路徑中最後一個點。如果路徑不存在,需要使用 beginPath() 和 moveTo() 方法來定義開始點。

      bezierCurveTo() 方法用於繪製一條三次貝塞爾曲線。

       三次貝塞爾曲線需要三個點。前兩個點(cp1x,cp1y)和(cp2x,cp2y)是用於三次貝塞爾計算中的控制點,第三個點(x,y)是曲線的結束點。曲線的開始點是當前路徑中最後一個點。如果路徑不存在,同樣需要使用 beginPath() 和 moveTo() 方法來定義開始點。

       例5  繪製一條二次貝塞爾曲線和一條三次貝塞爾曲線。

     ctx.beginPath();

     ctx.moveTo(20,20);

     ctx.quadraticCurveTo(20,100,200,20);

     ctx.stroke();

     ctx.beginPath();

     ctx.moveTo(20,120);

     ctx.bezierCurveTo(20,220,200,180,200,120);

     ctx.stroke();

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖3所示的兩條貝塞爾曲線。

圖3  兩條貝塞爾曲線

       例6  使用多個貝塞爾曲線來繪製一個對話氣泡。

    ctx.beginPath();

    ctx.moveTo(75,25);

    ctx.quadraticCurveTo(25,25,25,62.5);

    ctx.quadraticCurveTo(25,100,50,100);

    ctx.quadraticCurveTo(50,120,30,125);

    ctx.quadraticCurveTo(60,120,65,100);

    ctx.quadraticCurveTo(125,100,125,62.5);

    ctx.quadraticCurveTo(125,25,75,25);

    ctx.stroke();

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖4所示的對話氣泡。

圖4 對話氣泡

      例7  使用多個貝塞爾曲線來繪製一個紅心。 

     ctx.fillStyle="red";

     ctx.beginPath();

     ctx.moveTo(75,40);

     ctx.bezierCurveTo(75,37,70,25,50,25);

     ctx.bezierCurveTo(20,25,20,62.5,20,62.5);

     ctx.bezierCurveTo(20,80,40,102,75,120);

     ctx.bezierCurveTo(110,102,130,80,130,62.5);

     ctx.bezierCurveTo(130,62.5,130,25,100,25);

     ctx.bezierCurveTo(85,25,75,37,75,40);

     ctx.fill();

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖5所示的紅心圖案。

 

圖5  紅心

      例8  通過迴圈繪製一個9行9列的棋盤。

     ctx.strokeStyle="red";

     ctx.lineWidth=3;

     ctx.beginPath();

     for (i=50;i<=450;i+=50)

     {

         ctx.moveTo(i,50);

         ctx.lineTo(i,450);

         ctx.moveTo(50,i);

         ctx.lineTo(450,i);

     }

     ctx.stroke();

      為顯示完整的棋盤,請將畫布的寬和高均設置為500。在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖6所示的棋盤。

 

圖6  棋盤

2.2  繪製矩形

       在Canvas API中,上下文CanvasRenderingContext2D對象提供的與矩形繪製相關的方法如下:

void rect(in float x, in float y, in float w, in float h);      // 建立一個矩形路徑

void clearRect(in float x, in float y, in float w, in float h);  // 清除給定矩形區域的內容

void fillRect(in float x, in float y, in float w, in float h);   // 填充給定的矩形區域

void strokeRect(in float x, in float y, in float w, in float h);  // 繪製給定的矩形邊框

這個幾個方法中給定的四個參數分別為矩形左上角頂點的x坐標、y坐標,以及矩形的寬w和高h。

例9  繪製紅綠藍三個矩形邊框。

     ctx.beginPath();

     ctx.lineWidth="8";

     ctx.strokeStyle="red";

     ctx.rect(15,15,150,150);

     ctx.stroke();                // 繪製紅色矩形

     ctx.beginPath();

     ctx.lineWidth="3";

     ctx.strokeStyle="#00FF00";

     ctx.strokeRect(30,30,40,50);  // 繪製綠色矩形

     ctx.beginPath();

     ctx.lineWidth="8";

     ctx.strokeStyle="blue";

     ctx.moveTo(80,50);

     ctx.lineTo(80,120);

     ctx.lineTo(140,120);

     ctx.lineTo(140,50);

     ctx.closePath();

     ctx.stroke();   //     //  繪製藍色矩形

      在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖7所示的三個矩形。

圖7  三個矩形邊框

      例10  繪製一個邊長為100的正方形,邊框採用藍色,內部用紅色填充。

     ctx.fillStyle="red";

     ctx.strokeStyle="blue";

     ctx.lineWidth=2;

     ctx.fillRect(50,50,100,100);

     ctx.strokeRect(50,50,100,100);

      在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖8所示的正方形。

 

圖8  正方形

      在沒有進行坐標旋轉的情況下,採用rect()和strokeRect()方法繪製的矩形一定是兩條邊與x軸平行,兩條邊與y軸平行。若要繪製與坐標軸不平行的矩形,可以採用繪製4條線的方法完成。

      例11  繪製矩形中的矩形。要求矩形里的矩形其頂點在外面矩形的中點上。

     var x = [50,50,250,250];

     var y = [50,250,250,50];

     ctx.strokeStyle="red";

     ctx.lineWidth=3;

     for (i=1;i<=4;i++)

     {

         ctx.beginPath();

         ctx.moveTo(x[0],y[0]);

         for (k=1;k<=3;k++)

           ctx.lineTo(x[k],y[k]);

         ctx.closePath();

         ctx.stroke();

         var tx=x[0];

         var ty=y[0];

         for (k=0;k<3;k++)

         {

             x[k]=(x[k]+x[k+1])/2;

             y[k]=(y[k]+y[k+1])/2;

         }

         x[3]=(tx+x[3])/2;

         y[3]=(ty+y[3])/2;

     }

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖9所示的正方形。

圖9  正方形中的正方形

       例12  繪製國際象棋棋盤。

     for (i=0;i<8;i++)

     {

         for (j=0;j<8;j++)

         {

              if ((i+j)%2==0)   

                 ctx.fillStyle = 'black';

              else

                 ctx.fillStyle= 'white';

              ctx.fillRect(j*50,i*50,50,50);

         }

     }

       為顯示完整的棋盤,請將畫布的寬和高均設置為400。在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖10所示的國際象棋棋盤。

 

圖10  國際象棋棋盤 

      例13  根據給定數據繪製柱狀圖。

     var data = [100, 50, 20, 30, 100];

     var colors = [ "red","orange", "yellow","green", "blue"];

     ctx.fillStyle = "white";

     ctx.fillRect(0,0,canvas.width,canvas.height);

     for(var i=0; i<data.length; i++)

     {

         var dp = data[i];

         ctx.fillStyle = colors[i];

         ctx.fillRect(25+i*50, 280-dp*2, 50, dp*2);

     }

      在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖11所示的柱狀圖。 

圖11  柱狀圖

2.3  繪製圓形和扇形

      在Canvas API中,上下文CanvasRenderingContext2D對象提供的與圓形和弧等繪製相關的方法如下:

       void arc(in float x, in float y, in float radius, in float startAngle, in float endAngle, in boolean anticlockwise);

       void arcTo(in float x1, in float y1, in float x2, in float y2, in float radius);

      其中,arc方法用來繪製扇形。參數x和y是圓心坐標,radius是半徑,startAngle和endAngle則是扇形的起始角度和終止角度(以弧度表示),anticlockwise表示作圖時應該逆時針畫(true)還是順時針畫(false)。

      arcTo() 方法用於在畫布上創建介於兩個切線之間的弧/曲線。繪製出子路徑最後一個點(x0,y0)和(x1,y1)以及(x1,y1)和(x2,y2)構成的兩條直線間半徑為radius的最短弧線,並用直線連接(x0,y0)

例14  繪製圓弧。

     ctx.strokeStyle="red";

     ctx.fillStyle="orange";

     for(var i=0;i<3;i++)

     {

        for(var j=0;j<4;j++)

        {

            ctx.beginPath();

            x = 50+j*100;     

            y = 50+i*100;    

            radius = 45;                   

            startAngle = 0;   

            endAngle = Math.PI/2+(Math.PI*j)/2;

            anticlockwise = i%2==0 ? false : true; 

            ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

            if (i>1)

               ctx.fill();

            else

               ctx.stroke();

        }

     }

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖12所示的圓弧。

 

圖12  圓弧及填充

      通過這個例子還可以加深理解:當調用fill()函數時,所有沒有閉合的形狀都會自動閉合,因此可以不調用closePath()函數。但是調用stroke()時不會自動閉合。

      例15  繪製9個大小不一的圓。

     ctx.fillStyle="red";

     ctx.lineWidth=1;

     for (var i=1;i<10;i++)

     {

        ctx.beginPath();

        ctx.arc(i*20,i*20,i*10,0,Math.PI*2,true);

        ctx.closePath();

        ctx.fillStyle='rgba(255,0,0,0.25)';

        ctx.fill();

     }

      在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖13所示的圖形。

圖13  逐漸放大的圓

      例16  根據給定數據繪製餅圖。

     var data = [100, 50, 20, 30, 100];

     ctx.fillStyle = "white";

     ctx.fillRect(0,0,canvas.width,canvas.height);

     var colors = [ "red","orange", "yellow","green", "blue"];

     var total = 0;

     for(var i=0; i<data.length; i++)

         total += data[i];

     var prevAngle = 0;

     for(var i=0; i<data.length; i++) 

     {

         var fraction = data[i]/total;

         var angle = prevAngle + fraction*Math.PI*2;

         ctx.fillStyle = colors[i];

         ctx.beginPath();

         ctx.moveTo(150,150);

         ctx.arc(150,150, 100, prevAngle, angle, false);

         ctx.lineTo(150,150);

         ctx.fill();

         ctx.strokeStyle = "black";

         ctx.stroke();

         prevAngle = angle;

     }

      在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖14所示的餅圖。

 

圖14  餅圖

2.4  繪製文本

      在Canvas API中,上下文CanvasRenderingContext2D對象提供的與文本繪製相關的方法如下:

       void fillText(in DOMString text, in float x, in float y, optionalin float maxWidth);

       void strokeText(in DOMString text, in float x, in float y, optionalin float maxWidth);

      這兩個方法用來繪製文本,它的前三個參數分別為文本內容、起點的x坐標、y坐標。其中:fillText方法為繪製填充的文字;strokeText方法為對文字進行描邊,不填充內部區域,通常用來添加空心字。

      與文本相關的屬性有:

      attribute DOMString font;         // 設置字體,預設為10px sans-serif

      attribute DOMString textAlign;    //  設置對齊方式,有"start", "end", "left", "right", "center"等,預設為"start"

      attribute DOMString textBaseline;  //設置文字對齊基線,有"top", "hanging", "middle", "alphabetic", "ideographic", "bottom" 等取值,預設為”alphabetic"

例17  在畫布上添加兩行文字。

  ctx.font = "Bold 50px 隸書";

  ctx.fillStyle = "Black";

  ctx.fillText("我們是中國人", 10, 50);

  ctx.strokeText("我們熱愛我們的祖國", 10, 150);

      在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖15所示的文本。

 

圖15  文本繪製

      註意:fillText方法不支持文本斷行,即所有文本出現在一行內。所以,如果要生成多行文本,只有調用多次fillText方法。

例18  繪製包含數據說明的柱狀圖。

     var data = [100, 50, 20, 30, 100];

     var colors = [ "red","orange", "yellow","green", "blue"];

     ctx.fillStyle = "white";

     ctx.fillRect(0,0,canvas.width,canvas.height);

     for(var i=0; i<data.length; i++)

     {

         var dp = data[i];

         ctx.fillStyle = colors[i];

         ctx.fillRect(25+i*50, 280-dp*2, 50, dp*2);

     }

     ctx.fillStyle = "black";

     ctx.lineWidth = 2;

     ctx.beginPath();

     ctx.moveTo(25,10);

     ctx.lineTo(25,280);

     ctx.lineTo(290,280);

     ctx.stroke();

     ctx.fillStyle = "black";

     for(var i=0; i<6; i++)

     {

         ctx.fillText((5-i)*20 + "",4, i*40+80);

         ctx.beginPath();

         ctx.moveTo(25,i*40+80);

         ctx.lineTo(30,i*40+80);

         ctx.stroke();

     }

     var labels = ["JAN","FEB","MAR","APR","MAY"];

     for(var i=0; i<5; i++)

         ctx.fillText(labels[i], 40+ i*50, 290);   

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖16所示的包含數據說明的柱狀圖。

 

圖16  包含數據說明的柱狀圖

      還可以為文本等設置陰影。

例19  為文本設置陰影。

  ctx.shadowOffsetX = 3;    // 設置水平位移

  ctx.shadowOffsetY = 3;    // 設置垂直位移

  ctx.shadowBlur = 2;      // 設置模糊度

  ctx.shadowColor = "rgba(0, 0, 0, 0.5)";    // 設置陰影顏色

  ctx.font = "50px 宋體";

  ctx.fillStyle = "Black";

  ctx.fillText("我們是中國人", 10, 50);

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖17所示的文字陰影效果。

圖17  文字陰影

2.5  圖形剪切

      在Canvas API中,上下文CanvasRenderingContext2D對象提供了一個用於圖形剪切的方法。

       void clip();

       剪切(clip)路徑和普通的 canvas 圖形差不多,不同的是它的作用是遮罩,用來隱藏沒有遮罩的部分,如圖18所示。紅邊五角星就是裁切路徑,所有在路徑以外的部分都不會在 canvas 上繪製出來。預設情況下,canvas 有一個與它自身一樣大的剪切路徑(也就是沒有剪切效果)。

 

圖18  剪切示意圖

例20  一個簡單的剪切示例。

   ctx.fillStyle = 'red';

   ctx.fillRect(0,0,400,300);

   ctx.beginPath();

   ctx.moveTo(200,50);

   ctx.lineTo(100,250);

   ctx.lineTo(300,250);

   ctx.closePath();

   ctx.lineWidth = 10;

   ctx.stroke();

   ctx.clip();

   ctx.fillStyle = 'yellow';

   ctx.fillRect(0,0,400,150);

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖19所示的三角形剪切。

 

圖19  三角形剪切

      在這個例子中,先畫了一個與 canvas 一樣大小(寬400,高300)的紅色方形作為背景,然後用 clip方法創建一個三角形的剪切路徑。剪切路徑創建之後,所有出現在它裡面的東西才會畫出來。這樣在其後繪製高度為畫布一半的矩形填充時,只有三角形剪切路徑裡面的內容才會繪製出來。

      設定了剪切區域之後,無論在Canvas上繪製什麼,只有落在剪切區域內的那部分才能得以顯示,其餘都會被遮蔽掉。

      例21  cilp方法的進一步理解。

// 繪製第一個圓

ctx.beginPath();   

ctx.fillStyle = 'red';

ctx.arc(200, 100, 100, 0, Math.PI * 2, false);

ctx.fill();

// 繪製第二個圓

ctx.beginPath();

ctx.fillStyle = 'blue';

ctx.arc(100, 150, 100, 0, Math.PI * 2, false);

ctx.fill();

// 繪製第三個圓

ctx.beginPath();

ctx.fillStyle = 'green';

ctx.arc(300, 150, 100, 0, Math.PI * 2, false);

ctx.fill();

// 繪製第四個圓

ctx.beginPath();

ctx.fillStyle = 'brown';

ctx.arc(200, 200,100, 0, Math.PI * 2, false);

ctx.fill();

ctx.lineWidth = 10;

ctx.strokeStyle='black';

ctx.stroke();

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖20所示的4個圓。

圖20  沒有使用clip方法的4個圓

      若在第3個圓繪製後插入一條語句“ctx.clip();”,則在畫布中繪製出如圖21所示的圖形。從圖21可以看出第4個圓(棕色的),只有落在第3個圓中的部分被繪製出來。

 

圖21 在第3個圓之後使用clip()方法

      若clip()方法往上移,放到第2個圓的後面,則在畫布中繪製出如圖22所示的圖形。從圖22可以看出第3個圓(藍色的)完全沒有被繪製出來,因為第3個圓與第2個圓相切,沒有交集;第4個圓(棕色的)只有落在第2個圓中的部分被繪製出來。

 

圖22 在第2個圓之後使用clip()方法

      若再把clip()方法往上移,放到第1個圓的後面,則在畫布中繪製出如圖23所示的圖形。從圖23可以看出,第1個圓為剪切區域,第2、3、4個圓只有落在第1個圓中的部分才被繪製出來。

圖23 在第1個圓之後使用clip()方法

      當使用剪切函數clip()進行繪圖後,可能需要取消該剪切區域或者重新定義剪切區域。在Canvas中,可以通過save()函數和restore()函數來實現。在構建剪切區域之前保存狀態,完成剪切區域內的繪圖之後進行狀態讀取。

       例如,在例21的程式中,在第2個圓繪製後插入語句“ctx.save();”和“ctx.clip();”,在第3個圓繪製後插入語句“ctx.restore();”,則在畫布中繪製出如圖24所示的圖形。從圖24可以看出,第3個圓(藍色的)完全沒有被繪製出來,因為第3個圓與第2個圓相切,沒有交集;第4個圓(棕色的)全部被繪製出來,此時取消了剪切區域。

圖24  在第3個圓繪製後取消剪切區域

例22  採用clip實現簡單的探照燈效果。

<!DOCTYPE html>

<head>

<title>簡單探照燈</title>

</head>

<body>

<canvas id="myCanvas" width="400" height="400" style="border:3px double #996633;">

</canvas>

<script type="text/javascript">

    var rot=10;

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

    var ctx=canvas.getContext('2d');

    setInterval("draw()",100);

    function draw()

    {

           ctx.clearRect(0,0,400,400);

           ctx.save();

           ctx.fillStyle="black";

           ctx.fillRect(0,0,400,400);

        ctx.beginPath();

        ctx.arc(rot,200,40,0,Math.PI*2,true);

        ctx.closePath();

        ctx.fillStyle="white";

        ctx.fill();

           ctx.clip();

           ctx.font="bold 45px 隸書";

        ctx.textAlign="center";

           ctx.textBaseline="middle";

           ctx.fillStyle="#FF0000";

           ctx.fillText("中國北京歡迎您!",200,200);

           ctx.restore();

        rot=rot+10;

        if (rot>400) rot=10;

    }

</script>

</body>

</html>

       在瀏覽器中打開包含這段HTML代碼的html文件,可以看到在畫布中呈現出如圖25所示的簡單探照燈效果。

 

圖25  簡單的探照燈

3.圖形變換

在圖形學中,可以對圖形進行平移、縮放和旋轉等變換操作。

在Canvas API中,上下文CanvasRenderingContext2D對象提供的與圖形變換相關的方法如下:

void translate(in float x, in float y);  // 平移Canvas的原點到指定的坐標點(x,y)

void rotate(in float angle);         //  按給定的弧度angle順時針旋轉

void scale(in float x, in float y);    //  按給定的縮放倍率進行縮放

void setTransform(in float m11, in float m12, in float m21, infloat m22, in float dx, in float dy);                  // 將當前轉換重置為單位矩陣

void transform(in float m11, in float m12, in float m21, in floatm22, in float dx, in float dy);                      // 按矩陣進行變換

       在進行圖形變換前先保存上下文環境(狀態)是一個良好的習慣。大多數情況下,調用 restore()方法比手動恢複原先的狀態要簡單得多。例如,在一個迴圈中做平移操作但沒有保存和恢復canvas 的狀態,很可能到最後會發現有些東西不見了,那是因為它很可能已經超出 canvas 範圍以外了。

3.1  save和restore方法

       save方法用於保存上下文環境,restore方法用於恢復到上一次保存的上下文環境。

       在Canvas中,每個上下文對象都包含一個繪圖狀態的堆,繪圖狀態包含下列內容:

(1)當前的變換矩陣;

(2)當前的剪切區域(clip);

       (3)當前的屬性值:fillStyle、font、globalAlpha、globalCompositeOperation、lineCap、 lineJoin、lineWidth、miterLimit、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、 strokeStyle、textAlign、textBaseline等。

例23  save方法和restore方法的簡單應用示例。

     ctx.fillStyle = "red";

     ctx.fillRect(10,10,80,80);

     ctx.save();

     ctx.shadowOffsetX = 10;

     ctx.shadowOffsetY = 10;

     ctx.shadowBlur = 5;

     ctx.shadowColor = "rgba(0,0,0,0.5)";

     ctx.fillStyle = "blue";

     ctx.fillRect(100,10,80,80);

     ctx.restore();

     ctx.fillRect(200,10,80,80);

     ctx.fillStyle = "orange";

     ctx.fillRect(300,10,80,80);

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖26所示的4個矩形。

 

圖26  4個矩形

      這4個矩形中,第1個矩形填充紅色,之後保存狀態(填充色為紅色),第2個矩形是一個有黑色陰影的填充色為藍色的矩形;接著,使用restore方法,恢復了保存前的設置,繪製了一個沒有陰影的填充色為紅色的第3個矩形,第4個矩形是一個填充色為橙色的矩形,也沒有陰影。

      若去掉代碼中的“ctx.restore();”語句,不恢復狀態,則繪製的4個矩形如圖27所示。體會圖27與圖26的區別。

圖27  不執行“ctx.restore();”繪製的4個矩形

3.2  translate、scale和rotate方法

       translate() 方法實現坐標平移,例如,進行ctx.translate(dx,dy);後,坐標原點移到(dx,dy)處,這樣程式繪圖時給出的坐標值(x,y),相對於canvas預設的坐標原點(0,0),應該為(x+dx,y+dy)。

例24  translate簡單應用示例。

      ctx.fillStyle = "red";

      ctx.fillRect(10,10,80,80);

      ctx.fillStyle = "blue";

      ctx.translate(80,80);

      ctx.fillRect(10,10,80,80);

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖26所示的2個矩形。從圖中看出,在坐標位置 (10,10) 處繪製一個紅色填充矩形後,將坐標原點平移到(80,80),這樣再次繪製填充藍色的矩形從位置 (90,90) 處開始繪製。

 

圖28  平移後的藍色矩形

  例25  連續坐標平移的示例。

    ctx.fillStyle = 'rgba(255,0,0,0.5)';

    for (i = 0; i<5; i++)

     {

         ctx.translate(50,50);

         ctx.fillRect(0,0,100,100);

      }

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖29所示的5個矩形。

圖29  坐標平移後的5個矩形

       scale() 方法實現圖形的縮放。需要註意的是使用scale方法對繪圖進行縮放後,所有之後的繪圖也會被縮放,包括坐標定位也會被縮放。例如,執行ctx.scale(2,2)後,繪圖將定位於距離畫布左上角兩倍遠的位置。

例26  scale簡單應用示例。

    ctx.fillStyle = "red";

    ctx.fillRect(10,10,80,80);

    ctx.save();

    ctx.fillStyle = "blue";

    ctx.scale(2,2);

    ctx.fillRect(50,10,80,80);

    ctx.restore();

    ctx.fillStyle = "green";

    ctx.scale(0.5,0.5);

    ctx.fillRect(10,200,80,80);

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖30所示的3個矩形。從圖中看出,在坐標位置 (10,10) 處繪製一個紅色填充矩形後,ctx.scale(2,2);將繪圖放大兩倍,這樣繪製填充藍色的矩形從位置 (100,20) 處開始繪製,其寬和高均是紅色填充矩形的2倍;恢覆上下文環境後,ctx.scale(0.5,0.5);將繪圖縮小1倍,這樣繪製填充綠色的矩形從位置 (5,100) 處開始繪製,其寬和高均是紅色填充矩形的一半。

 

圖30  矩形的縮放

        若去掉代碼中的“ctx.restore();”語句,不恢復狀態,則繪製的3個矩形如圖31所示。體會圖31與圖30的區別。綠色矩形的大小之所以與紅色矩形一樣,是因為一個東西放大2倍後再縮小1倍,正好恢複原樣。

       通過這個示例一定得明白,圖形變換的設置一定是在前一個狀態的基礎上進行的。因此在進行圖形變換時,根據需要通過save()方法保存狀態,restore()方法恢復狀態是非常重要的。

圖31  不執行“ctx.restore();”繪製的3個矩形

例27  連續圖形放大的示例。

    ctx.fillStyle = 'rgba(255,0,0,0.5)';

    for (i = 0; i<4; i++)

    {

         ctx.scale(2,2);

         ctx.fillRect(5,5,10,10);

     }

       在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖32所示的4個矩形。

 

圖32  依次放大後的4個矩形

      rotate()方法實現圖形的旋轉,其中參數給出的旋轉角度angle以弧度計。如需

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 封裝變化之對象創建 在對象創建的過程中,經常會出現的一個問題就是通過顯示地指定一個類來創建對象,從而導致緊耦合。這是因為創建對象時指定類名將使你受特定實現的約束而不是特定介面的約束。這會使未來的變化更加複雜。要避免這種情況,就應該間接地創建對象。 這種緊耦合的問題很大程度是由new關鍵字帶來的,由於 ...
  • 一 Nginx 連接限制 1.1 HTTP協議的連接與請求 HTTP是建立在TCP, 一次HTTP請求需要先建立TCP三次握手(稱為TCP連接),在連接的基礎上再進行HTTP請求。 HTTP請求建立在一次TCP連接基礎上,對於HTTP會話,一次TCP連接可以建立多次HTTP請求。 HTTP協議版本 ...
  • SQL自學筆記 約束 1.0概念及分類 2.0非空約束 3.0 唯一約束 4.0 主鍵約束 自動增長 5.0 外鍵約束 級聯操作 多表關係 三種情況概述 資料庫的備份和還原 多表查詢 1.0 內連接查詢 隱式內連接 : 用where條件消除無用數據 顯示內連接 : 2.0 外連接查詢 3.0 子查詢 ...
  • 先給大家看一下大佬們對這本書的評價,免得說我“標題黨” 版本控制是管理數據變更的藝術,無論數據變更是來自同一個人,還是來自不同的人(一個團隊)。版本控制系統不但要忠實地記錄數據的每一次變更, 還要能夠幫助還原任何一-次歷史變更,以及實現團隊的協同工作等。Git就是版本控制系統中的佼佼者。 當開源軟體 ...
  • 同步部分數據有兩個思路: master只發送需要的; 優點:中繼日誌小;如果多從庫,只需要在主庫中統一控制 缺點:中途修改比較麻煩,不能控制同步的表 slave只接收想要的 優點:中途修改同步的表或庫方便;可以控制需要的表和庫 缺點:中繼日誌大;如果從庫比較多,需要一個一個配置; master端 b ...
  • 前幾天HBase出現了RIT告警,忽然發現發出告警的Region所屬的表並不是我創建出來的,於是就想看看這些表是怎麼來的。 一時也沒什麼頭緒,就先看看這些表是什麼時候創建出來的吧,然後再根據時間點看看有誰操作了資料庫。 那麼怎麼看表的創建時間呢?desc看一下,也沒有這個屬性啊。再細想呢,hbase ...
  • 告警 正在開會,突然釘釘告警聲響個不停,同時市場人員反饋客戶在投訴系統登不進了,報504錯誤。查看釘釘上的告警信息,幾台業務伺服器節點全部報CPU超過告警閾值,達100%。 趕緊從會上下來,SSH登錄伺服器,使用 top 命令查看,幾個Java進程CPU占用達到180%,190%,這幾個Java進程 ...
  • 為學習spark,虛擬機中開4台虛擬機安裝spark3.0.0底層hadoop集群已經安裝好,見ol7.7安裝部署4節點hadoop 3.2.1分散式集群學習環境首先,去http://spark.apache.org/downloads.html下載對應安裝包解壓[hadoop@master ~]$... ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...