close

Canvas教程(3):Drawing shapes繪制圖形 

 

 

http://www.inspirr.com 
 

 


 

 

 

網格 The grid

Before we can start drawing, we need to talk about the canvas grid or . The HTML template on the previous page had a canvas element 150 pixels wide and 150 pixels high. I've drawn this image with the default grid overlayed. Normally 1 unit in the grid corresponds to 1 pixel on the canvas. The origin of this grid is positioned in the top left corner (coordinate (0,0)). All elements are placed relative to this origin. So the position of the top left corner of the blue square becomes x pixels from the left and y pixels from the top (coordinate (x,y)). Later in this tutorial we'll see how we can translate the origin to a different position, rotate the grid and even scale it. For now we'll stick to the default.

在真正開始之前,我們需要先探討 canvas 的網格(grid)或者坐標空間()。在前一頁的HTML模板里有一個150像素寬, 150像素高的 canvas 對象。我在畫面上疊加上默認網格,如右圖。通常網格的1個單元對應 canvas 上的1個像素。網格的原點是定位在左上角(坐標(0,0))。畫面里的所有物體的位置都是相對這個原點。這樣,左上角的藍色方塊的位置就是距左邊x像素和距上邊Y像素(坐標(x, y))。后面的教程中我們將學會如何把移動原點,旋轉以及縮放網格。不過現在我們會使用默認的狀態。

繪制圖形 Drawing shapes

Unlike SVG, canvas only supports one primitive shape - rectangles. All other shapes must be created by combining one or more paths. Luckily, we have a collection of path drawing functions which make it possible to compose very complex shapes.

不像 SVG,canvas 只支持一種基本形狀——矩形,所以其它形狀都是有一個或多個路徑組合而成。還好,有一組路徑繪制函數讓我們可以繪制相當復雜的形狀。

矩形 Rectangles

First let's look at the rectangle. There are three functions that draw rectangles on the canvas:

我們首先看看矩形吧,有三個函數用于繪制矩形的:

fillRect(x,y,width,height): Draws a filled rectangle
strokeRect(x,y,width,height): Draws a rectangular outline
clearRect(x,y,width,height): Clears the specified area and makes it fully transparent

Each of these three functions takes the same parameters. x and y specify the position on the canvas (relative to the origin) of the top-left corner of the rectangle. width and height are pretty obvious. Let's see these functions in action.

它們都接受四個參數, x 和 y 指定矩形左上角(相對于原點)的位置,width height 是矩形的寬和高。好,實戰一下吧。

Below is the draw() function from the previous page, but now I've added the three functions above.

下面就是上頁模板里的 draw() 函數,但添加了上面的三個函數。

繪制矩形的例子 Rectangular shape example

觀看示例

function draw(){
  var canvas = document.getElementById('tutorial');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    ctx.fillRect(25,25,100,100);
    ctx.clearRect(45,45,60,60);
    ctx.strokeRect(50,50,50,50);
  }
}

The result should look something like the image on the right. The fillRect function draws a large black square 100x100 pixels. The clearRect function removes a 60x60 pixels square from the center and finally the strokeRect draws a rectangular outline 50x50 pixels inside the cleared square. In the following pages we'll see two alternative methods for the clearRect function and we'll also see how to change the color and stroke style of the rendered shapes.

出來的結果應該和右邊的是一樣的。fillRect 函數畫了一個大的黑色矩形(100x100),clearRect 函數清空了中間 60x60 大小的方塊,然后strokeRect 函數又在清空了的空間內勾勒出一個 50x50 的矩形邊框。在接下去的頁面里,我們會看到和 clearRect 函數差不多另外兩個方法,以及如何去改變圖形的填充和邊框顏色。

Unlike the path functions we'll see in the next section, all three rectangle functions draw immediately to the canvas.

與下一節的路徑函數不一樣,這三個函數的效果會立刻在 canvas 上反映出來。

繪制路徑 Drawing paths

To make shapes using paths, we need a couple of extra steps.

不像畫矩形那樣的直截了當,繪制路徑是需要一些額外的步驟的。

beginPath()
closePath()
stroke()
fill()

The first step to create a path is calling the beginPath method. Internally, paths are stored as a list of sub-paths (lines, arcs, etc) which together form a shape. Every time this method is called, the list is reset and we can start drawing new shapes.

第一步是用 beginPath 創建一個路徑。在內存里,路徑是以一組子路徑(直線,弧線等)的形式儲存的,它們共同構成一個圖形。每次調用 beginPath,子路徑組都會被重置,然后可以繪制新的圖形。

The second step is calling the methods that actually specify the paths to be drawn. We'll see these shortly.

第二步就是實際繪制路徑的部分,很快我們就會看到。

The third, and an optional step, would be to call the closePath method. This method tries to close the shape by drawing a straight line from the current point to the start. If the shape has already been closed or there's only one point in the list, this function does nothing.

第三步是調用 closePath 方法,它會嘗試用直線連接當前端點與起始端點來關閉路徑,但如果圖形已經關閉或者只有一個點,它會什么都不做。這一步不是必須的。

The final step will be calling the stroke and/or fill methods. Calling one of these will actually draw the shape to the canvas. stroke is used to draw an outlined shape, while fill is used to paint a solid shape.

最后一步是調用 stroke 或 fill 方法,這時,圖形才是實際的繪制到 canvas 上去。stroke 是繪制圖形的邊框,fill 會用填充出一個實心圖形。

Note: When calling the fill method any open shapes will be closed automatically and it isn't necessary to use the closePath method.

注意:當調用 fill 時,開放的路徑會自動閉合,而無須調用 closePath 。

The code for a drawing simple shape (a triangle) would look something like this.

畫一個簡單圖形(如三角形)的代碼如下。

ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fill();

moveTo

One very useful function, which doesn't actually draw anything, but is part of the path list described above, is the moveTo function. You can probably best think of this as lifting a pen or pencil from one spot on a piece of paper and placing it on the next.

moveTo 是一個十分有用的方法,雖然并不能用它來畫什么,但卻是繪制路徑的實用方法的一部分。你可以把它想象成是把筆提起,并從一個點移動到另一個點的過程。

moveTo(x, y)

The moveTo function takes two arguments - x and y, - which are the coordinates of the new starting point.

它接受 x 和 y (新的坐標位置)作為參數。

When the canvas is initialized or the beginPath method is called, the starting point is set to the coordinate (0,0). In most cases we would use the moveTo method to place the starting point somewhere else. We could also use the moveTo method to draw unconnected paths. Take a look at the smiley face on the right. I've marked the places where I used the moveTo method (the red lines).

當 canvas 初始化或者調用 beginPath 的時候,起始坐標設置就是原點(0,0)。大多數情況下,我們用 moveTo 方法將起始坐標移至其它地方,或者用于繪制不連續的路徑。看看右邊的笑臉,紅線就是使用 moveTo 移動的軌跡。

To try this for yourself, you can use the code snippet below. Just paste it into the draw function we saw earlier.

試一試下面的代碼,粘貼到之前用過的 draw 函數內在看看效果吧。

moveTo 的使用示例

ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false);   // Mouth (clockwise)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true);  // Left eye
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true);  // Right eye
ctx.stroke();

Note: remove the moveTo methods to see the connecting lines.
Note: For a description of the arc function and its parameters look below.

注意:你可以注釋 moveTo 方法來觀察那些連接起來的線。
注意:arc 方法的用法見下面。

直線 Lines

For drawing straight lines we use the lineTo method.

我們用 lineTo 方法來畫直線。

lineTo(x, y)

This method takes two arguments - x and y, - which are the coordinates of the line's end point. The starting point is dependent on previous drawn paths, where the end point of the previous path is the starting point for the following, etc. The starting point can also be changed by using the moveTo method.

lineTo 方法接受終點的坐標(x,y)作為參數。起始坐標取決于前一路徑,前一路徑的終點即當前路徑的起點,起始坐標也可以通過 moveTo 方法來設置。

lineTo 的使用示例

In the example below two triangles are drawn, one filled and one outlined. (The result can be seen in the image on the right). First the beginPath method is called to begin a new shape path. We then use the moveTo method to move the starting point to the desired position. Below this two lines are drawn which make up two sides of the triangle.

示例(如右圖)畫的是兩個三角形,一個實色填充,一個勾邊。首先調用 beginPath 方法創建一個新路徑,然后用moveTo 方法將起始坐標移至想要的位置,然后畫兩條直線來構成三角形的兩條邊。

You'll notice the difference between the filled and stroked triangle. This is, as mentioned above, because shapes are automatically closed when a path is filled. If we would have done this for the stroked triangle only two lines would have been drawn, not a complete triangle.

可以注意到 fill 和 strok 繪三角形的區別,上面也提到過,使用 fill 路徑會自動閉合,但使用 stroke 不會,如果不關閉路徑,勾畫出來的只有兩邊。

觀看示例

// 填充三角形
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(105,25);
ctx.lineTo(25,105);
ctx.fill();

// 勾邊三角形
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath();
ctx.stroke();

弧線 Arcs

For drawing arcs or circles we use the arc method. The specification also describes the arcTo method, which is supported by Safari but hasn't been implemented in the current Gecko browsers.

我們用 arc 方法來繪制弧線或圓。標準說明中還包含 arcTo 方法,當前 Safari 是支持的,但基于 Gecko 的瀏覽器還未實現。

arc(x, y, radius, startAngle, endAngle, anticlockwise)

This method takes five parameters: x and y are the coordinates of the circle's center. Radius is self explanatory. The startAngle and endAngle parameters define the start and end points of the arc in radians. The starting and closing angle are measured from the x axis. The anticlockwise parameter is a boolean value which when true draws the arc anticlockwise, otherwise in a clockwise direction.

方法接受五個參數:x,y 是圓心坐標,radius 是半徑,startAngle 和 endAngle 分別是起末弧度(以 x 軸為基準),anticlockwise 為 true 表示逆時針,反之順時針。

Warning: In the Firefox beta builds, the last parameter is clockwise. The final release will support the function as described above. All scripts that use this method in its current form will need to be updated once the final version is released.

警告:在 Firefox 的 beta 版本里,最后一個參數是 clockwise,而最終版本不是。因此如果是從 beta 升級至發行版需要做相應修改。

Note: Angles in the arc function are measured in radians, not degrees. To convert degrees to radians you can use the following JavaScript expression: var radians = (Math.PI/180)*degrees.

注意:arc 方法里用到的角度是以弧度為單位而不是度。度和弧度直接的轉換可以用這個表達式:var radians = (Math.PI/180)*degrees;。

arc 的使用示例

The following example is a little more complex than the ones we've seen above. I've drawn 12 different arcs all with different angles and fills. If I would have written this example just like the smiley face above, firstly this would have become a very long list of statements and secondly, when drawing arcs, I would need to know every single starting point. For arcs of 90, 180 and 270 degrees, like the ones I used here, this wouldn't be to much of a problem, but for more complex ones this becomes way too difficult.

這個示例比之前見到過的要復雜一些,畫了12個不同的弧形,有不同夾角和填充狀態的。如果我用上面畫笑臉的方式來畫這些弧形,那會是一大段的代碼,而且,畫每一個弧形時我都需要知道其圓心位置。像這里畫 90,180 和 270 度的弧形同樣是一個問題,如果圖形越繁雜實現起來會越困難。

The two for loops are for looping through the rows and columns of arcs. For every arc I start a new path using beginPath. Below this I've written out all the parameters as variables, so it's easier to read what's going on. Normally this would be just one statement. The x and y coordinates should be clear enough. radius and startAngle are fixed. The endAngle starts of as 180 degrees (first column) and is increased with steps of 90 degrees to form a complete circle (last column). The statement for the clockwise parameter results in the first and third row being drawn as clockwise arcs and the second and fourth row as counterclockwise arcs. Finally, the if statement makes the top half stroked arcs and the bottom half filled arcs.

這里使用兩個 for 循環來畫多行多列的弧形。每一個弧形都用 beginPath 方法創建一個新路徑。然后為了方便閱讀和理解,我把所有參數都寫成變量形式。顯而易見,x 和 y 作為圓心坐標。 radius startAngle 都是固定,endAngle 從 180 度半圓開始,以 90 度方式遞增至圓。anticlockwise 則取決于奇偶行數。最后,通過 if 語句判斷使前兩行表現為勾邊,而后兩行為填充效果。

for (i=0;i<4;i++){
  for(j=0;j<3;j++){
    ctx.beginPath();
    var x              = 25+j*50;               // x coordinate
    var y              = 25+i*50;               // y coordinate
    var radius         = 20;                    // Arc radius
    var startAngle     = 0;                     // Starting point on circle
    var endAngle       = Math.PI+(Math.PI*j)/2; // End point on circle
    var anticlockwise  = i%2==0? false: true; // clockwise or anticlockwise

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

    if (i>1){
      ctx.fill();
    } else {
      ctx.stroke();
    }
  }
}

貝塞爾和二次方曲線 Bezier and quadratic curves

The next type of paths available are Bézier curves, available in the cubic and quadratic varieties. These are generally used to draw complex organic shapes.

接下來要介紹的路徑是 貝塞爾曲線 ,它可以是二次和三次方的形式,一般用于繪制復雜而有規律的形狀。

quadraticCurveTo(cp1x, cp1y, x, y) // BROKEN in Firefox 1.5 (see work around below)
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)


The difference between these can best be described using the image on the right. A quadratic Bézier curve has a start and an end point (blue dots) and just one (red dot) while a cubic Bézier curve uses two control points.

上面兩行代碼的區別見右圖。它們都有一個起點一個終點(圖中的藍點),但二次方貝塞爾曲線只有一個(紅色)控制點點)而三次方貝塞爾曲線有兩個。

The x and y parameters in both these methods are the coordinates of the end point. cp1x and cp1y are the coordinates of the first control point, and cp2x and cp2y are the coordinates of the second control point.

參數 x 和 y 是終點坐標,cp1x cp1y 是第一個控制點的坐標,cp2x cp2y 是第二個的。

Using quadratic and cubic Bézier curves can be quite challenging, because unlike vector drawing software like Adobe Illustrator, we don't have direct visual feedback as to what we're doing. This makes it pretty hard to draw complex shapes. In the following example, we'll be drawing some simple organic shapes, but if you have the time and, most of all, the patience, much more complex shapes can be created.

使用二次方和三次方的貝塞爾曲線是相當有挑戰的,因為不像在矢量繪圖軟件 Adobe Illustrator 里那樣有即時的視覺反饋。因為用它來畫復雜圖形是比較麻煩的。但如果你有時間,并且最重要是有耐心,再復雜的圖形都可以繪制出來的。下面我們來畫一個簡單而又規律的圖形。

There's nothing very difficult in these examples. In both cases we see a succession of curves being drawn which finally result in a complete shape.

這些例子都比較簡單。我們繪制的都是完整的圖形。

quadraticCurveTo 的使用示例

// Quadratric curves example
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();

It is possible to convert any quadratic Bézier curve to a cubic Bézier curve by correctly computing both cubic Bézier control points from the single quadratic Bézier control point, although the reverse is NOT true. An exact conversion of a cubic Bézier curve to a quadratic Bézier curve is only possible if the cubic term is zero, more commonly a subdivision method is used to approximate a cubic Bézier using multiple quadratic Bézier curves.

通過計算,可以由二次曲線的單個控制點得出相應三次方曲線的兩個控制點,因此二次方轉三次方是可能的,但是反之不然。僅當三次方程中的三次項為零是才可能轉換為二次的貝塞爾曲線。通常地可以用多條二次方曲線通過細分算法來近似模擬三次方貝塞爾曲線。

bezierCurveTo 的使用示例

// Bezier curves example
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();

Firefox 1.5 quadraticCurveTo() bug 的應對方案

There is a bug in the Firefox 1.5 implementation of quadatricCurveTo(). It does NOT draw a quadratic curve, as it is just calling the same cubic curve function bezierCurveTo() calls, and repeating the single quadratic control point (x,y) coordinate twice. For this reason quadraticCurveTo() will yield incorrect results. If you require the use of quadraticCurveTo() you must convert your quadratic Bézier curve to a cubic Bézier curve yourself, so you can use the working bezierCurveTo() method.

在 Firefox 1.5 里,quadatricCurveTo() 的實現是有 bug 的,它不是直接繪制二次方曲線,而是調用 bezierCurveTo() ,其中兩個控制點都是二次方曲線的那個單控制點。因此,它會繪制出不正確的曲線。如果必須使用到 quadraticCurveTo(),你需要自行去將二次方曲線轉換成三次方的,這樣就可以用 bezierCurveTo() 方法了。

var currentX, currentY;  // set to last x,y sent to lineTo/moveTo/bezierCurveTo or quadraticCurveToFixed()

function quadraticCurveToFixed( cpx, cpy, x, y ) {
  /*
   For the equations below the following variable name prefixes are used:
     qp0 is the quadratic curve starting point (you must keep this from your last point sent to moveTo(), lineTo(), or bezierCurveTo() ).
     qp1 is the quadatric curve control point (this is the cpx,cpy you would have sent to quadraticCurveTo() ).
     qp2 is the quadratic curve ending point (this is the x,y arguments you would have sent to quadraticCurveTo() ).
   We will convert these points to compute the two needed cubic control points (the starting/ending points are the same for both
   the quadratic and cubic curves.

   The equations for the two cubic control points are:
     cp0=qp0 and cp3=qp2
     cp1 = qp0 + 2/3 *(qp1-qp0)
     cp2 = cp1 + 1/3 *(qp2-qp0) 

   In the code below, we must compute both the x and y terms for each point separately. 

    cp1x = qp0x + 2.0/3.0*(qp1x - qp0x);
    cp1y = qp0y + 2.0/3.0*(qp1y - qp0y);
    cp2x = cp1x + (qp2x - qp0x)/3.0;
    cp2y = cp1y + (qp2y - qp0y)/3.0;

   We will now 
     a) replace the qp0x and qp0y variables with currentX and currentY (which *you* must store for each moveTo/lineTo/bezierCurveTo)
     b) replace the qp1x and qp1y variables with cpx and cpy (which we would have passed to quadraticCurveTo)
     c) replace the qp2x and qp2y variables with x and y.
   which leaves us with: 
  */
  var cp1x = currentX + 2.0/3.0*(cpx - currentX);
  var cp1y = currentY + 2.0/3.0*(cpy - currentY);
  var cp2x = cp1x + (x - currentX)/3.0;
  var cp2y = cp1y + (y - currentY)/3.0;

  // and now call cubic Bezier curve to function 
  bezierCurveTo( cp1x, cp1y, cp2x, cp2y, x, y );

  currentX = x;
  currentY = y;
}

又是矩形 Rectangles

Besides the three methods we saw above which draw rectangular shapes directly to the canvas, we also have a method rect which adds a rectangular path to the path list.

除了上面提到的三個方法可以直接繪制矩形之外,我們還有一個 rect 方法是用于繪制矩形路徑的。

rect(x, y, width, height)

This method takes four arguments. The x and y parameters define the coordinate of the top left corner of the new rectangular path. width and height define the width and the height of the rectangle.

它接受四個參數,x y 是其左上角坐標,width height 是其寬和高。

When this method is executed, the moveTo method is automatically called with the parameters (0,0) (i.e. it resets the starting point to its default location).

當它被調用時,moveTo 方法會自動被調用,于是起始坐標又恢復成原點了。

大雜燴 Making combinations

In all examples on this page I've only used one type of path function per shape. However there's absolutely no limitation to the amount or type of paths you can use to create a shape. So in this last example I've tried to combine all of the path functions to make a set of very famous game characters.

上面所用到的例子都只用到了一種類型的路徑,當然 canvas 不會限制所使用的路徑類型的多少。所以,我們來看一個路徑大雜燴。

大雜燴樣例

I'm not going to run through this complete script, but the most important things to note are the function roundedRect and the use of the fillStyle property. It can be very usefull and time saving to define your own functions to draw more complex shapes. In this script it would have taken me twice as many lines of code as I have now.
We will look at the fillStyle property in greater depth later in this tutorial. Here I'm using it to change the fill color from the default black, to white, and back again.

在整個例子里,最值得注意的是 roundedRect 函數的使用和 fillStyle 屬性的設置。自定義函數對于封裝復雜圖形的繪制是非常有用的。在這個例子里使用自定義函數就省掉了大約一半的代碼。

在接下來的例子里會深入探討 fillStyle 屬性的使用。這里是用它來改變填充顏色,從默認的黑色,到白色,然后再回到黑色。

查看示例

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  roundedRect(ctx,12,12,150,150,15);
  roundedRect(ctx,19,19,150,150,9);
  roundedRect(ctx,53,53,49,33,10);
  roundedRect(ctx,53,119,49,16,6);
  roundedRect(ctx,135,53,49,33,10);
  roundedRect(ctx,135,119,25,49,10);

  ctx.beginPath();
  ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,true);
  ctx.lineTo(31,37);
  ctx.fill();
  for(i=0;i<8;i++){
    ctx.fillRect(51+i*16,35,4,4);
  }
  for(i=0;i<6;i++){
    ctx.fillRect(115,51+i*16,4,4);
  }
  for(i=0;i<8;i++){
    ctx.fillRect(51+i*16,99,4,4);
  }
  ctx.beginPath();
  ctx.moveTo(83,116);
  ctx.lineTo(83,102);
  ctx.bezierCurveTo(83,94,89,88,97,88);
  ctx.bezierCurveTo(105,88,111,94,111,102);
  ctx.lineTo(111,116);
  ctx.lineTo(106.333,111.333);
  ctx.lineTo(101.666,116);
  ctx.lineTo(97,111.333);
  ctx.lineTo(92.333,116);
  ctx.lineTo(87.666,111.333);
  ctx.lineTo(83,116);
  ctx.fill();
  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.moveTo(91,96);
  ctx.bezierCurveTo(88,96,87,99,87,101);
  ctx.bezierCurveTo(87,103,88,106,91,106);
  ctx.bezierCurveTo(94,106,95,103,95,101);
  ctx.bezierCurveTo(95,99,94,96,91,96);
  ctx.moveTo(103,96);
  ctx.bezierCurveTo(100,96,99,99,99,101);
  ctx.bezierCurveTo(99,103,100,106,103,106);
  ctx.bezierCurveTo(106,106,107,103,107,101);
  ctx.bezierCurveTo(107,99,106,96,103,96);
  ctx.fill();
  ctx.fillStyle = "black";
  ctx.beginPath();
  ctx.arc(101,102,2,0,Math.PI*2,true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(89,102,2,0,Math.PI*2,true);
  ctx.fill();
}
function roundedRect(ctx,x,y,width,height,radius){
  ctx.beginPath();
  ctx.moveTo(x,y+radius);
  ctx.lineTo(x,y+height-radius);
  ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
  ctx.lineTo(x+width-radius,y+height);
  ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
  ctx.lineTo(x+width,y+radius);
  ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
  ctx.lineTo(x+radius,y);
  ctx.quadraticCurveTo(x,y,x,y+radius);
  ctx.stroke();
}

 

 

   

 

  Tag: 設計公司 | 網頁設計公司 | 廣告公司 | 網站設計 | 平面設計 | 互動媒體 | 網頁設計 | Web design | Website design | design house | 媒體公司 | Iphone app | 程式設計 | Flash 網頁 | Flash game | 動畫設計 | 後期製作 | 網上商店 | 網上宣傳 | 網頁服務 |

arrow
arrow
    文章標籤
    網站設計 互動媒體
    全站熱搜
    創作者介紹
    創作者 inspirr 的頭像
    inspirr

    inspirr

    inspirr 發表在 痞客邦 留言(0) 人氣()