H5 canvas基礎(chǔ)、進(jìn)階筆記

代碼倉庫

主要是讀書筆記枕磁,里面物理知識都是通用,記錄一下渡蜻,里面的示例做小游戲時(shí)會經(jīng)常遇到很實(shí)用。

1Canvas元素

    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script type="text/javascript">
              window.onload = function () {
                  //1计济、獲取canvas對象
                  var cnv = document.getElementById("canvas");
                  //2茸苇、獲取上下文環(huán)境對象context
                  var cxt = cnv.getContext("2d");
                  //3、開始繪制圖形
                  cxt.moveTo(50, 100);
                  cxt.lineTo(150, 50);
                  cxt.stroke();
              }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="200" height="150" style="border:1px dashed
gray; "></canvas>
    </body>
    </html>

注意:width和height分別定義Canvas的寬度和高度沦寂。默認(rèn)情況下学密,Canvas的寬度為300px,高度為150px传藏。(css 設(shè)置無效)

2 直線

2.1 畫直線

    cxt.moveTo(x1, y1);
    cxt.lineTo(x2, y2);
    cxt.stroke();

2.2 描邊

    cxt.strokeStyle = 屬性值腻暮;
    cxt.strokeRect(x, y, width, height);

strokeStyle屬性 取值有三種彤守,即顏色值、漸變色哭靖、圖案具垫。

strokeRect()方法 用于確定矩形的坐標(biāo),其中x和y為矩形最左上角的坐標(biāo)试幽。

2.3 填充

    cxt.fillStyle=屬性值筝蚕;
    cxt.fillRect(x, y, width, height);

fillStyle屬性跟strokeStyle屬性一樣,取值也有三種铺坞,即顏色值饰及、漸變色、圖案康震。fillRect()方法跟strokeRect()方法一樣燎含,用于確定矩形的坐標(biāo),其中x和y為矩形最左上角的坐標(biāo)腿短,width表示矩形的寬度屏箍,height表示矩形的高度。跟描邊矩形一樣橘忱,填充矩形的fillStyle屬性也必須在fillRect()方法之前定義赴魁,否則fillStyle屬性無效。

2.4 rect() 方法

2.5 清空矩形

cxt.clearRect(x, y, width, height);

3. 曲線

圖形钝诚、弧線颖御、二次貝塞爾曲線、三次貝塞爾曲線凝颇。

3.1 圓

        cxt.beginPath();
    cxt.arc(x, y潘拱,半徑,開始角度拧略,結(jié)束角度芦岂,anticlockwise); //anticlockwise:默認(rèn)false 順時(shí)針
    cxt.closePath();、三次貝塞爾曲線垫蛆。

上面這個(gè)語法僅僅是對圓形的一個(gè)“狀態(tài)描述”禽最,我們還需要對圓形進(jìn)行“描邊”或“填充”,才會有實(shí)際效果袱饭。這一點(diǎn)跟繪制矩形是一樣的道理川无,大家別忘了對比一下

    //狀態(tài)描述
    cxt.beginPath();
    cxt.arc(x, y,半徑虑乖,開始角度懦趋,結(jié)束角度,anticlockwise);
    cxt.closePath();
    //描邊
    cxt.strokeStyle = "顏色值";
    cxt.stroke();
    
    //狀態(tài)描述
    cxt.beginPath();
    cxt.arc(x, y愕够,半徑走贪,開始角度逃沿,結(jié)束角度,anticlockwise);
    cxt.closePath();
    //填充
    cxt.fillStyle = "顏色值";
    cxt.fill();

3.2 圓弧

  • arc()畫弧線不使用closePath()來關(guān)閉路徑幻锁。這一點(diǎn)大家一定要區(qū)分開凯亮,因?yàn)榛【€不是一個(gè)閉合圖形。而closePath()是用來繪制“封閉圖形”的(作用在于關(guān)閉路徑哄尔、連接起點(diǎn)與終點(diǎn))

  • cxt.arcTo(cx, cy, x2, y2, radius);

    image.png

3.3 二次貝塞爾曲線

cxt.quadraticCurveTo(cx , cy , x2, y2);
image.png

3.4 三次貝塞爾曲線

cxt.bezierCurveTo(cx1 , cy1 , cx2 , cy2 , x , y);
image.png

4 線條操作

4.1 lineWidth屬性

lineWidth屬性:取值為整數(shù)假消,默認(rèn)值為1,默認(rèn)單位為px岭接。

lineWidth屬性不僅可以用于直線圖形富拗,也可以用于曲線圖形。此外需要注意的是鸣戴,假設(shè)線條寬度為lineWidth啃沪,則strokeRect()方法繪制的矩形實(shí)際寬度是(width+lineWidth),實(shí)際高度是(height+lineWidth)窄锅。而arc()方法繪制的圓形實(shí)際半徑為(radius+lineWidth)创千。

4.2 lineCap屬性

image.png

注意,round和square值會使線條稍微變長一點(diǎn)入偷,因?yàn)樗鼈兘o線條增加了線帽部分追驴。

我們必須使用beginPath()來開始新的路徑,才可以使用一個(gè)新的lineCap屬性值盯串。此外還需要注意氯檐,round和square值會使線條稍微變長一點(diǎn),因?yàn)樗鼈兘o線條增加了線帽部分体捏。

image.png

4.3 LineJoin屬性

cxt.lineJoin = "屬性值";

image.png

4.4 setLineDash()方法

setLineDash()方法

image.png

5. 文本操作

image.png

5.1strokeText()方法

strokeText(text , x , y , maxWidth) 

參數(shù)maxWidth為可選參數(shù),表示允許的最大文本的寬度(單位為px)糯崎。如果文本寬度超出maxWidth值几缭,文本會被壓縮至maxWidth值的寬度。

5.2 fillText()方法

fillText(text , x , y , maxWidth)

5.3 measureText()方法

var length = cxt.measureText(text).width;

參數(shù)text表示一個(gè)字符串文本沃呢,measureText().width返回文本的長度年栓,單位為px。

5.4 textBaseline屬性

image.png

6. 圖片操作

6.1 繪制圖片

cxt.drawImage(image , dx , dy);

參數(shù)image薄霜,表示頁面中的圖片某抓。該圖片可以是頁面中的img元素纸兔,也可以是JavaScript創(chuàng)建的Image對象

function $$(id) {
   return document.getElementById(id);
}
window.onload = function () {
  var cnv = $$("canvas");
  var cxt = cnv.getContext("2d");

  //創(chuàng)建image對象
  var image = new Image();
  image.src = "images/princess.png";
  
  //必須在圖片載入完成后才能將圖片繪制到Canvas上
  image.onload = function () {
    cxt.drawImage(image, 40, 20);
  }
}
2. cxt.drawImage(image , dx , dy , dw , dh)
image.png
  1. cxt.drawImage(image , sx , sy , sw, sh , dx , dy , dw , dh)

    image.png

6.2 平鋪照片

var pattern = cxt.createPattern(image , type);    

cxt.fillStyle = pattern;    

cxt.fillRect();
image.png

6.3 切割圖片

cxt.clip();

想要使用clip()方法切割一張圖片,一般需要以下三步否副。

(1)繪制基本圖形汉矿。(2)使用clip()方法。(3)繪制圖片备禀。

//第1步洲拇,繪制基本圖形,用來切割
cxt.beginPath();
cxt.arc(70, 70, 50, 0, 360 * Math.PI / 180, true);
cxt.closePath();
cxt.stroke();

//第2步曲尸,使用clip()方法赋续,使得切割區(qū)域?yàn)樯厦胬L制的基本圖形
cxt.clip();

//第3步,繪制一張圖片
var image = new Image();
image.src = "images/princess.png";
image.onload = function () {
cxt.drawImage(image, 10, 20);
}

7 變形操作

image.png

7.1 平移

cxt.translate(x, y);  //“狀態(tài)”都必須在“動(dòng)作”之前定義

7.2 清空畫布

cxt.clearRect(0,0, cnv.width, cnv.height); //等價(jià) cxt.clearRect();

7.3 縮放

cxt.scale(x, y);

scale()方法會改變圖形的以下幾點(diǎn)另患。(1)左上角坐標(biāo)纽乱。(2)寬度或高度。(3)線條寬度昆箕。

7.4 圖形旋轉(zhuǎn)

cxt.rotate(angle); //“度數(shù)*Math.PI/180”

默認(rèn)情況下鸦列,rotate()方法的旋轉(zhuǎn)中心就是原點(diǎn)坐標(biāo)(0,0)

  1. 改變旋轉(zhuǎn)中心: 如果我們想要以某一點(diǎn)(x, y)為旋轉(zhuǎn)中心,可以先使用translate(x, y)为严,然后再使用rotate()方法敛熬。(配合save restore方法)

    cxt.clearRect(0, 0, cnv.width, cnv.height);
    cxt.save();
    cxt.translate(cnv.width / 2, cnv.height / 2); //將坐標(biāo)移
    動(dòng)到中心
    cxt.rotate(Math.PI * (i / 100));            //累進(jìn)旋轉(zhuǎn)
    cxt.fillStyle = "HotPink";
    cxt.fillRect(-rectWidth / 2, -rectHeight / 2, rectWidth,
    rectHeight); //填充矩形
    cxt.restore();
    

7.5 transform()

cxt.transform(a, b, c, d, e, f);
image.png
image.png
image.png

7.6 setTransform()方法

setTransform()方法會重置圖形的狀態(tài),然后再進(jìn)行變換

變形操作除了可以用于圖形第股,還可以用于文字和圖片

8 像素操作

8.1 getImageData()方法

var imgData = cxt.getImageData(x , y , width , height);    

var data = imgData.data;

說明:x应民、y表示所選圖片區(qū)域的坐標(biāo),width夕吻、height表示所選圖片區(qū)域的寬度和高度诲锹。getImageData()方法返回的是一個(gè)canvasPixelArray對象,我們可以用一個(gè)變量(如imgData)來保存這個(gè)對象涉馅。canvasPixelArray對象有一個(gè)data屬性归园,這個(gè)data屬性是一個(gè)保存了這一張圖片像素?cái)?shù)據(jù)的數(shù)組,數(shù)組取值如[r1, g1, b1, a1, r2,g2, b2, a2, ……]稚矿。也就是說庸诱,在imgData.data這個(gè)數(shù)組中每四個(gè)數(shù)存儲著1個(gè)像素的rgba顏色值,這四個(gè)數(shù)分別是該像素的紅(r)晤揣、綠(g)桥爽、藍(lán)(b)、透明度(a)

8.2 putImageData()方法

cxt.putImageData(image , x , y);

說明:image表示重新繪制的圖形昧识,也就是使用getImageData()方法獲取的canvasPixelArray對象钠四。x、y表示重新繪制圖形左上角的橫坐標(biāo)跪楞、縱坐標(biāo)缀去。getImageData()和putImageData()都是配合使用的侣灶,一般我們都是先用getImageData()獲取像素?cái)?shù)據(jù),然后利用一定的算法進(jìn)行像素操作缕碎,最后再使用putImageData()方法輸出像素?cái)?shù)據(jù)(即在Canvas中顯示一張圖片)褥影。上面只是簡單介紹了一下getImageData()和putImageData()這兩個(gè)方法的語法,在后面這幾節(jié)里阎曹,我們結(jié)合相應(yīng)例子并經(jīng)澄苯祝回過頭來看看這一節(jié),就很容易理解了

[圖片上傳失敗...(image-5f588d-1677137124942)]

  1. 顏色反轉(zhuǎn)
    for (var i = 0; i < data.length; i += 4)
    {
        data[i + 0] = 255- data[i + 0];
        data[i + 1] = 255- data[i + 1];
        data[i + 2] = 255- data[i + 2];
    }
  1. 黑白效果

        for (var i = 0; i < data.length; i += 4)
        {
            //var average = (data[i + 0] + data[i + 1] + data[i + 2] + data[i + 3]) / 3;
            var grayscale = data[i] * 0.3 + data[i + 1] * 0.6 + data[i + 2] * 0.1;
            data[i + 0] = average;   //紅
            data[i + 1] = average;   //綠
            data[i + 2] = average;   //藍(lán)
        }
    
       
    
  2. 亮度效果

        for (var i = 0; i < data.length; i += 4)
        {
            var a = 50;
            data[i + 0] += a;
            data[i + 1] += a;
            data[i + 2] += a;
        }
    
  1. 復(fù)古效果

        for (var i = 0; i < data.length; i += 4) {
            var r = data[i + 0];
            var g = data[i + 1];
            var b = data[i + 2];
            data[i + 0] = r * 0.39 + g * 0.76 + b * 0.18;
            data[i + 1] = r * 0.35 + g * 0.68 + b * 0.16;
            data[i + 2] = r * 0.27 + g * 0.53 + b * 0.13;
        }
    
  1. 紅色蒙版

        for (var i = 0; i < data.length; i += 4)
        {
            var r = data[i + 0];
            var g = data[i + 1];
            var b = data[i + 2];
            
            var average = (r + g + b) / 3;
            data[i + 0] = average;
            data[i + 1] = 0;
            data[i + 2] = 0;
        }
    
  2. 透明處理

        for (var i = 0; i < data.length; i += 4)
        {
            data[i + 3] = data[i + 3]*n;
        }
    

8.3 createImageData()方法

cxt.createImageData(sw, sh);       //第1種格式    

cxt.createImageData(imageData);    //第2種格式

(1)第一種格式:接收兩個(gè)參數(shù)处嫌,sw和sh分別表示要?jiǎng)?chuàng)建區(qū)域的寬度和高度栅贴。

(2)第二種格式:接收一個(gè)像素對象,表示要“創(chuàng)建區(qū)域”的寬度和高度與“這個(gè)像素對象”的寬度和高度相等熏迹。

createImageData(sw, sh) 和createImageData(imageData)這兩個(gè)方法說白了都是用來創(chuàng)建一個(gè)區(qū)域而已檐薯。只不過一個(gè)是我們自己定寬高,另外一個(gè)是“復(fù)制”別人的寬高注暗。

9. 漸變與陰影

9.1 線性漸變

    var gnt = cxt.createLinearGradient(x1, y1, x2, y2);
    gnt.addColorStop(value1, color1);
    gnt.addColorStop(value2, color2);
    cxt.fillStyle = gnt;
    cxt.fill();

參數(shù)value表示漸變位置的偏移量坛缕,取值為0~1之間任意值。value1捆昏,表示漸變開始位置赚楚,value2表示漸變結(jié)束位置。

9.2 徑向漸變

    var gnt = cxt.createRadialGradient(x1, y1, r1, x2, y2, r2);
    gnt.addColorStop(value1, color1);
    gnt.addColorStop(value2, color2);
    cxt.fillStyle = gnt;
    cxt.fill();

(1)調(diào)用createLinearGradient()方法創(chuàng)建一個(gè)radialGradient對象骗卜,并賦值給變量gnt宠页。

(2)調(diào)用radialGradient對象(即gnt)的addColorStop()方法N次,第1次表示漸變開始的顏色寇仓,第2次表示漸變結(jié)束時(shí)的顏色举户。然后第3次則以第2次漸變顏色作為開始顏色,進(jìn)行漸變遍烦,以此類推俭嘁。

(3)把radialGradient對象(即gnt)賦值給fillStyle屬性,并且調(diào)用fill()方法來繪制有漸變色的圖形服猪。(x1 , y1)表示漸變開始圓心的坐標(biāo)供填,r1表示漸變開始圓心的半徑。(x2 , y2)表示漸變結(jié)束圓心的坐標(biāo)罢猪,r2表示漸變結(jié)束圓的半徑捕虽。

image.png

9.3 陰影

              //定義繪制圖形的函數(shù)
              window.onload = function () {
                  var cnv = $$("canvas");
                  var cxt = cnv.getContext("2d");                                      
                  //設(shè)置右下方向的陰影
                  cxt.shadowOffsetX = 5;
                  cxt.shadowOffsetY = 5;
                  cxt.shadowColor = "LightSkyBlue ";
                  cxt.shadowBlur = 10;
                  cxt.fillStyle = "HotPink";
                  cxt.fillRect(100, 30, 50, 50);
              }

10 Canvas路徑

image.png

10.1 beginPath()

Canvas是基于“狀態(tài)”來繪制圖形的。每一次繪制(stroke()或fill()), Canvas會檢測整個(gè)程序定義的所有狀態(tài)坡脐,這些狀態(tài)包括strokeStyle、fillStyle房揭、lineWidth等备闲。當(dāng)一個(gè)狀態(tài)值沒有被改變時(shí)晌端,Canvas就一直使用最初的值。當(dāng)一個(gè)狀態(tài)值被改變時(shí)恬砂,需要分兩種情況考慮咧纠。

(1)如果使用beginPath()開始一個(gè)新的路徑,則不同路徑使用不同的值泻骤。

(2)如果沒有使用beginPath()開始一個(gè)新的路徑漆羔,則后面的值會覆蓋前面的值(后來者居上原則)。

//第1條直線
cxt.beginPath();
cxt.moveTo(50, 40);
cxt.lineTo(150, 40);
cxt.strokeStyle = "red";
cxt.stroke();

//第2條直線
cxt.beginPath();// 注意:這里開啟一個(gè)新的路徑
cxt.moveTo(50, 80);
cxt.lineTo(150, 80);
cxt.strokeStyle = "green";
cxt.stroke();

10.2 closePath()

“關(guān)閉路徑”并不等于“結(jié)束路徑”狱掂,所謂的“關(guān)閉路徑”一般是指將同一個(gè)路徑的起點(diǎn)與終點(diǎn)這兩點(diǎn)連接起來演痒,使其成為一個(gè)封閉的圖形。

10.3 isPointInPath()方法

cxt.isPointInPath(x , y);

注意趋惨,isPointInPath()不支持Canvas自帶的兩個(gè)方法:strokeRect()鸟顺、fillRect()。如果想要使用strokeRect()和fillRect()器虾,請使用rect()方法來代替讯嫂。

11 Canvas 狀態(tài)

Canvas為我們提供了兩個(gè)操作狀態(tài)的方法:save()和restore()。在Canvas中兆沙,我們可以使用save()方法來保存“當(dāng)前狀態(tài)”欧芽,然后可以使用restore()方法來恢復(fù)“之前保存的狀態(tài)”。save()和restore()一般情況下都是成對配合使用的葛圃。

11.1 clip()方法

cxt.clip();

我們在使用clip()方法之前千扔,必須要在Canvas中繪制一個(gè)基本圖形。然后調(diào)用clip()方法后装悲,這個(gè)基本圖形就會變?yōu)橐粋€(gè)剪切區(qū)域昏鹃。

注意,與之前接觸的isPointInPath()方法一樣诀诊,clip()方法也不支持Canvas自帶的兩個(gè)方法:strokeRect()洞渤、fillRect()。如果要使用strokeRect()和fillRect()属瓣,請使用rect()方法來代替载迄。

11.1 使用場景

Canvas狀態(tài)的保存和恢復(fù),主要用于以下三種場合抡蛙。

(1)圖形或圖片裁切护昧。

(2)圖形或圖片變形。

(3)以下屬性改變的時(shí)候:fillStyle粗截、font惋耙、globalAlpha、globalCompositeOperation、lineCap绽榛、lineJoin湿酸、lineWidth、miterLimit灭美、shadowBlur推溃、shadowColor、shadowOffsetX届腐、shadowOffsetY铁坎、strokeStyle、textAlign犁苏、textBaseline硬萍。

 cxt.save();
 cxt.fillStyle = "HotPink";
 cxt.translate(30, 30);
 cxt.fillRect(0, 0, 100, 50);
 cxt.restore();//恢復(fù) 后面的平移坐標(biāo)系會基于原點(diǎn)位置

cxt.fillStyle = "LightSkyBlue ";
cxt.translate(60, 60);
cxt.fillRect(0, 0, 100, 50);

在變形操作(平移、縮放傀顾、旋轉(zhuǎn))中霞掺,我們一般都是在變形操作之前使用save()方法保存當(dāng)前狀態(tài)禀崖,其中當(dāng)前狀態(tài)包括參考坐標(biāo)、圖形大小等。然后再使用restore()方法來恢復(fù)之前保存的狀態(tài)忍捡。save( )和restore( )定页,在變形操作中會大量用到梦湘。

12 其他應(yīng)用

12.1 Canvas對象屬性

使用cnv.width和cnv.height來獲取Canvas的寬度和高度是經(jīng)常被用到的税娜,像文本居中時(shí)需要計(jì)算Canvas的寬度和高度、clearRect()清空Canvas時(shí)也需要計(jì)算Canvas的寬度和高度婉徘。

12.1 Canvas對象方法

image.png
cnv.toDataURL(type);

參數(shù)type是可選參數(shù)漠嵌,表示輸出的MIME類型。如果參數(shù)type省略盖呼,將使用image/png類型

12.2 globalAlpha屬性

context.globalAlpha = 數(shù)值儒鹿;

globalAlpha默認(rèn)值為1.0(完全不透明),取值范圍為:0.0~1.0几晤。其中0.0表示完全透明约炎,1.0表示完全不透明。globalAlpha屬性必須在圖形繪制之前定義才有效蟹瘾。注意圾浅,定義globalAlpha之后,會對整個(gè)畫布都起作用憾朴,因此我們在定義時(shí)要多加小心狸捕。

13 Canvas事件

在Canvas中,鼠標(biāo)事件分為以下三種众雷。(1)鼠標(biāo)按下:mousedown(2)鼠標(biāo)松開:mouseup(3)鼠標(biāo)移動(dòng):mousemove

13.1 獲取鼠標(biāo)的位置封裝工具

//將tools定義為window對象的屬性灸拍,該屬性的值是一個(gè)對象
window.tools = {};
//獲取鼠標(biāo)位置
window.tools.getMouse = function (element) {
  //定義一個(gè)mouse的對象
  var mouse = { x: 0, y: 0 };
  //為傳入的元素添加mousemove事件
  addEvent(element, "mousemove", function (e) {
    var x, y;
    //在IE中做祝,event對象是作為window對象的一個(gè)屬性存在
    var e = e || window.event;
    //獲取鼠標(biāo)當(dāng)前位置,并作兼容處理
    //兼容Firefox株搔、chrome剖淀、IE9及以上
    if (e.pageX || e.pageY) {
      x = e.pageX;
      y = e.pageY;
    }
    //兼容IE8及以下,以及混雜模式下的Chrome和Safari
    else {
      x  =  e.clientX  +  document.body.scrollLeft  ||  document.
      documentElement.scrollLeft;
      y  =  e.clientY  +  document.body.scrollTop  ||  document.
      documentElement.scrollTop;
    }
    //將當(dāng)前的坐標(biāo)值減去canvas元素的偏移位置纤房,則x、y為鼠標(biāo)在canvas中的相對坐標(biāo)
    x -= element.offsetLeft;
    y -= element.offsetTop;

    mouse.x = x;
    mouse.y = y;
  })
  //返回值為mouse對象
  return mouse;
}

13.2 獲取鍵盤方向

//獲取鍵盤控制方向
window.tools.getKey = function () {
  var key = {};
  window.addEventListener("keydown", function (e) {
    if (e.keyCode == 38 || e.keyCode == 87) {
      key.direction = "up";
    } else if (e.keyCode == 39 || e.keyCode == 68) {
      key.direction = "right";
    } else if (e.keyCode == 40 || e.keyCode == 83) {
      key.direction = "down";
    } else if (e.keyCode == 37 || e.keyCode == 65) {
      key.direction = "left";
    } else {
      key.direction = "";
    }
  }, false);
  return key;
}

13.3 循環(huán)事件

在Canvas中翻诉,我們都是使用requestAnimationFrame()方法來實(shí)現(xiàn)循環(huán)炮姨,從而達(dá)到動(dòng)畫效果。

    (function frame(){
        window.requestAnimationFrame(frame);
        cxt.clearRect(0, 0, cnv.width, cnv.height);
        ……
    })();

小球向右移動(dòng)動(dòng)畫

var cnv = $$("canvas");
var cxt = cnv.getContext("2d");

//初始化變量碰煌,也就是初始化圓的x軸坐標(biāo)為0
var x = 0;
//動(dòng)畫循環(huán)
(function frame() {
  window.requestAnimationFrame(frame);
  //每次動(dòng)畫循環(huán)都先清空畫布舒岸,再重繪新的圖形
  cxt.clearRect(0, 0, cnv.width, cnv.height);

  //繪制圓
  cxt.beginPath();
  cxt.arc(x, 70, 20, 0, 360 * Math.PI / 180, true);
  cxt.closePath();
  cxt.fillStyle = "#6699FF";
  cxt.fill();

  //變量遞增
  x += 2;
  })();

14 物理動(dòng)畫

在Canvas中我們可以使用反正切函數(shù)Math.atan2()來求出兩條邊之間夾角的度數(shù),并且能夠準(zhǔn)確判斷該度數(shù)對應(yīng)的是哪一個(gè)夾角芦圾。

14.1 跟隨鼠標(biāo)旋轉(zhuǎn)

    function Arrow(x, y, color, angle)
    {
        //箭頭中心x坐標(biāo)蛾派,默認(rèn)值為0
        this.x = x || 0;
        //箭頭中心y坐標(biāo),默認(rèn)值為0
        this.y = y || 0;
        //顏色个少,默認(rèn)值為"#FF0099"
        this.color = color || "#FF0099";
        //旋轉(zhuǎn)角度洪乍,默認(rèn)值為0
        this.angle = angle || 0;
    }
    Arrow.prototype = {
        stroke: function (cxt) {
              cxt.save();
              cxt.translate(this.x, this.y);
              cxt.rotate(this.angle);
              cxt.strokeStyle = this.color;
              cxt.beginPath();
              cxt.moveTo(-20, -10);
              cxt.lineTo(0, -10);
              cxt.lineTo(0, -20);
              cxt.lineTo(20, 0);
              cxt.lineTo(0, 20);
              cxt.lineTo(0, 10);
              cxt.lineTo(-20, 10);
              cxt.closePath();
              cxt.stroke();
              cxt.restore();
          },
          fill: function (cxt) {
              cxt.save();
              cxt.translate(this.x, this.y);
              cxt.rotate(this.angle);
              cxt.fillStyle = this.color;
              cxt.beginPath();
              cxt.moveTo(-20, -10);
              cxt.lineTo(0, -10);
              cxt.lineTo(0, -20);
              cxt.lineTo(20, 0);
              cxt.lineTo(0, 20);
              cxt.lineTo(0, 10);
              cxt.lineTo(-20, 10);
              cxt.closePath();
              cxt.fill();
              cxt.restore();
          }
      };
    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/arrow.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    //實(shí)例化一個(gè)箭頭,中點(diǎn)坐標(biāo)為畫布中心坐標(biāo)
                    var arrow = new Arrow(cnv.width / 2, cnv.height / 2);
                    //獲取鼠標(biāo)坐標(biāo)
                    var mouse = tools.getMouse(cnv);
                    
                    (function drawFrame() {
                          window.requestAnimationFrame(drawFrame, cnv);
                          cxt.clearRect(0, 0, cnv.width, cnv.height);
                          
                          var dx = mouse.x - cnv.width / 2;
                          var dy = mouse.y - cnv.height / 2;
                          //使用Math.atan2()方法計(jì)算出鼠標(biāo)與箭頭中心的夾角
                          arrow.angle = Math.atan2(dy, dx);
                          
                      arrow.fill(cxt);
                    })();
              }
          </script>
    </head>
    <body>
          <canvas id="canvas" width="200" height="150" style="border:1px solid
silver; "></canvas>
    </body>
    </html>

14.2 圓周運(yùn)動(dòng)

image.png

“圓的標(biāo)準(zhǔn)方程”經(jīng)過數(shù)學(xué)推理可以得到圓上任意一點(diǎn)的坐標(biāo)夜焦。

    x = centerX + Math.cos(angle)*radius;
    y = centerY + Math.sin(angle)*radius;
image.png
//再建立一個(gè)ball.js文件壳澳,用于存放一個(gè)小球類
function Ball(x, y, radius, color)
    {
        //小球中心的x坐標(biāo),默認(rèn)值為0
        this.x = x || 0;
        //小球中心的y坐標(biāo)茫经,默認(rèn)值為0
        this.y = y || 0;
        //小球半徑巷波,默認(rèn)值為12
        this.radius = radius || 12;
        //小球顏色,默認(rèn)值為"#6699FF"
        this.color = color || "#6699FF";
        
        this.scaleX = 1;
        this.scaleY = 1;
    }
    Ball.prototype = {
        //繪制"描邊"小球
        stroke: function (cxt) {
              cxt.save();
              cxt.scale(this.scaleX, this.scaleY);
              cxt.strokeStyle = this.color;
              cxt.beginPath();
              cxt.arc(this.x, this.y, this.radius, 0, 360 * Math.PI / 180, false);
              cxt.closePath();
              cxt.stroke();
              cxt.restore();
        },
        //繪制"填充"小球
        fill: function (cxt) {
              cxt.save();
              cxt.translate(this.x, this.y);
              cxt.rotate(this.rotation);
              cxt.scale(this.scaleX, this.scaleY);
              cxt.fillStyle = this.color;
              cxt.beginPath();
              cxt.arc(0, 0, this.radius, 0, 360 * Math.PI / 180, false);
              cxt.closePath();
              cxt.fill();
              cxt.restore();
        }
    }
    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    //實(shí)例化一個(gè)小球卸伞,中心坐標(biāo)為(100,25)抹镊,半徑、顏色都取默認(rèn)值
                    var ball = new Ball(100, 25);
                    var centerX = cnv.width / 2;
                    var centerY = cnv.height / 2;
                    var radius = 50;
                    var angle = 0;
                    
                    (function frame() {
                          window.requestAnimationFrame(frame);
                          cxt.clearRect(0, 0, cnv.width, cnv.height);
                          //繪制圓形
                          cxt.beginPath();
                          cxt.arc(centerX, centerY, 50, 0, 360 * Math.PI / 180, false);
                          cxt.closePath();
                          cxt.stroke();
                          
                          //計(jì)算小球坐標(biāo)
                          ball.x = centerX + Math.cos(angle) * radius;
                          ball.y = centerY + Math.sin(angle) * radius;
                          ball.fill(cxt);
                          
                          //角度遞增
                          angle += 0.05;
                    })();
                }
          </script>
    </head>
    <body>
          <canvas id="canvas" width="200" height="150" style="border:1px solid
silver; "></canvas>
    </body>
    </html>

14.2 波形運(yùn)動(dòng)

image.png

常見的波形運(yùn)動(dòng)可以分為三種

(1)作用于x軸坐標(biāo)荤傲。

(2)作用于y軸坐標(biāo)垮耳。

(3)作用于縮放屬性(scaleX或scaleY)。

(1) 作用于x軸坐標(biāo)弃酌。

x = centerX + Math.sin(angle) * range;   

angle += speed;

(2)作用于y軸坐標(biāo)氨菇。

y = centerY + Math.sin(angle) * range;   

 angle += speed;

(3) 作用于縮放屬性(scaleX或scaleY)。

當(dāng)正弦函數(shù)sin作用于物體的縮放屬性(scaleX或scaleY)時(shí)妓湘,物體會不斷地放大然后縮小查蓉,從而產(chǎn)生一種脈沖動(dòng)畫的效果。

var ball = new Ball(cnv.width / 2, cnv.height / 2, 25);
var range = 0.5;
var angle = 0;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

ball.scaleX = 1 + Math.sin(angle) * range;
ball.scaleY = 1 + Math.sin(angle) * range;
ball.fill(cxt);

//角度遞增
angle += 0.05;
})();

14.3 勻速運(yùn)動(dòng)

小球沿著任意方向進(jìn)行勻速運(yùn)動(dòng)

    vx = speed * Math.cos(angle * Math.PI/180);
    vy = speed * Math.sin(angle * Math.PI/180);
    object.x += vx;
    object.y += vy;

14.4 加速運(yùn)動(dòng)

小球慢慢減速反向

 //實(shí)例化一個(gè)小球
 var ball = new Ball(0, cnv.height / 2);
 //初始化x軸速度以及加速度
 var vx = 8;
 var ax = -0.2;

(function frame() {
window.requestAnimationFrame(frame);
cxt.clearRect(0, 0, cnv.width, cnv.height);

ball.x += vx;
ball.fill(cxt);

vx += ax;
})();
    ax = a * Math.cos(angle * Math.PI/180);
    ay = a * Math.sin(angle * Math.PI/180);
    vx += ax;
    vy += ay;
    object.x += vx;
    object.y += vy;

14.5 重力

vy += gravity;   

object.y += vy;

小球從空中自由降落到地面榜贴,然后反彈豌研,循環(huán)往復(fù)妹田,直到它最終速度為0而停止在地面上。

    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    //初始化數(shù)據(jù)
                    var ball = new Ball(cnv.width / 2, 0);
                    //y軸初始速度為0鹃共,重力加速度為0.2鬼佣,反彈系數(shù)為-0.8
                    var vy = 0;
                    var gravity = 0.2;
                    var bounce = -0.8;
                    
                    (function drawFrame() {
                          window.requestAnimationFrame(drawFrame);
                          cxt.clearRect(0, 0, cnv.width, cnv.height);
                          
                          ball.y += vy;
                          //邊界檢測
                          if (ball.y > cnv.height - ball.radius) {
                                ball.y = cnv.height - ball.radius;
                                //速度反向并且減小
                                vy = vy * bounce;
                          }
                          ball.fill(cxt);
                          
                          vy += gravity;
                    })();
              }
            </script>
        </head>
        <body>
            <canvas id="canvas" width="200" height="150" style="border:1px solid
silver; "></canvas>
        </body>
        </html>

14.6 摩擦力

摩擦力只會改變速度的大小而不會改變它的方向。換句話說霜浴,摩擦力只能將物體的速度降至0晶衷,但它無法讓物體掉頭往相反的方向移動(dòng)。

    vx *= friction;
    vy *= friction;
    object.x += vx;
    object.y += vy;

15 邊界檢測

對于邊界檢測阴孟,將從以下四個(gè)方面來介紹晌纫。(1)邊界限制(2)邊界生成(3)邊界環(huán)繞(4)邊界反彈

15.1 邊界限制

    if (ball.x < ball.radius) {
        //小球"碰到"左邊界時(shí)
    } else if (ball.x > cnv.width - ball.radius) {
        //小球"碰到"右邊界時(shí)
    }
    if (ball.y < ball.radius) {
        //小球"碰到"上邊界時(shí)
    } else if (ball.y > cnv.height - ball.radius) {
        //小球"碰到"下邊界時(shí)
    }

15.2 邊界環(huán)繞

指的是當(dāng)物體從一個(gè)邊界消失后,它就會從對立的邊界重新出現(xiàn)永丝,從而形成一種環(huán)繞效果

    if(ball.x < -ball.radius){
        //小球"完全超出"左邊界時(shí)
    } else if(ball.x>cnv.width + ball.radius){
        //小球"完全超出"右邊界時(shí)
    }
    if(ball.y<-ball.radius){
        //小球"完全超出"上邊界時(shí)
    } else if(ball.y>cnv.height + ball.radius){
        //小球"完全超出"下邊界時(shí)
    }

15.3 邊界生成

邊界生成锹漱,指的是物體完全超出邊界之后,會在最開始的位置重新生成慕嚷。

    if (ball.x < -ball.radius ||
        ball.x > cnv.width + ball.radius ||
        ball.y < -ball.radius ||
        ball.y > cnv.height + ball.radius) {
              ……
    }

15.4 邊界反彈

指的是物體觸碰到邊界之后就會反彈回來哥牍,就像現(xiàn)實(shí)世界中小球碰到墻壁反彈一樣。

    //碰到左邊界
    if (ball.x < ball.radius) {
        ball.x = ball.radius;
        vx = -vx;
    //碰到右邊界
    } else if (ball.x > canvas.width - ball.radius) {
        ball.x = canvas.width - ball.radius;
        vx = -vx;
    }
    //碰到上邊界
    if (ball.y < ball.radius) {
        ball.y = ball.radius;
        vy = -vy;
    //碰到下邊界
    } else if (ball.y > canvas.height - ball.radius) {
        ball.y = canvas.height - ball.radius;
        vy = -vy;
    }

16 碰撞檢測

碰撞檢測比較常用的是以下兩種方法喝检。(1)外接矩形判定法(2)外接圓判定法

16.1 外接矩形判定法

兩個(gè)矩形左上角的坐標(biāo)所處的范圍嗅辣。如果兩個(gè)矩形左上角的坐標(biāo)滿足一定條件,則兩個(gè)矩形就發(fā)生了碰撞蛇耀。

    window.tools.checkRect = function (rectA, rectB) {
        return ! (rectA.x + rectA.width < rectB.x  ||
                  rectB.x + rectB.width < rectA.x  ||
                  rectA.y + rectA.height < rectB.y ||
                  rectB.y + rectB.height < rectA.y);
    }

16.2 外接圓判定法

兩個(gè)圓心之間的距離辩诞。如果兩個(gè)圓心之間的距離大于或等于兩個(gè)圓的半徑之和,則兩個(gè)圓沒有發(fā)生碰撞纺涤;如果兩個(gè)圓心之間的距離小于兩個(gè)圓的半徑之和译暂,則兩個(gè)圓發(fā)生了碰撞。

    window.tools.checkCircle = function (circleB, circleA) {
        var dx = circleB.x - circleA.x;
        var dy = circleB.y - circleA.y;
        var distance = Math.sqrt(dx * dx + dy * dy);
        if (distance < (circleA.radius + circleB.radius)) {
              return true;
        }
        else {
              return false;
        }
    }

17 用戶交互

用戶交互的效果(1)捕獲物體(2)拖曳物體(3)拋擲物體

17.1 捕獲物體

(1)矩形的捕獲撩炊。(2)圓的捕獲外永。

  1. 矩形的捕獲

        if (mouse.x > rect.x &&
            mouse.x < rect.x + rect.width &&
            mouse.y > rect.y &&
            mouse.y < rect.y + rect.height) {
            ……
        }
    
  2. 元的捕獲

        dx = mouse.x - ball.x;
        dy = mouse.y - ball.y;
        distance = Math.sqrt(dx*dx + dy*dy);
        if(distance < ball.radius){
            ……
        }
    

17.2 拖拽物體

    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    //初始化數(shù)據(jù)
                    var ball = new Ball(cnv.width / 2, cnv.height / 2, 20);
                    ball.fill(cxt);
                    var mouse = tools.getMouse(cnv);
                    //初始化2個(gè)變量:dx和dy
                    var dx = 0, dy = 0;
                    
                    cnv.addEventListener("mousedown", function () {
                          if (ball.checkMouse(mouse)) {
                              //dx為鼠標(biāo)與球心的水平偏移量
                              dx = mouse.x - ball.x;
                              //dy為鼠標(biāo)與球心的垂直偏移量
                              dy = mouse.y - ball.y;
                              document.addEventListener("mousemove", onMouseMove, false);
                              document.addEventListener("mouseup", onMouseUp, false);
                          }
                    }, false);
                    function onMouseMove() {
                          //更新小球坐標(biāo)
                          ball.x = mouse.x - dx;
                          ball.y = mouse.y - dy;
                    }
                      function onMouseUp() {
                          //鼠標(biāo)松開時(shí)伯顶,移除鼠標(biāo)松開事件:mouseup(自身事件)
                          document.removeEventListener("mouseup", onMouseUp, false);
                          //鼠標(biāo)松開時(shí)骆膝,移除鼠標(biāo)移動(dòng)事件:mousemove
                          document.removeEventListener("mousemove", onMouseMove, false);
                      }
                      
                      (function drawFrame() {
                          window.requestAnimationFrame(drawFrame, cnv);
                          cxt.clearRect(0, 0, cnv.width, cnv.height);
                          
                          ball.fill(cxt);
                      })();
              }
          </script>
    </head>
    <body>
          <canvas id="canvas" width="200" height="150" style="border:1px solid
silver; "></canvas>
    </body>
    </html>

重力反彈

    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    var ball = new Ball(cnv.width / 2, cnv.height, 20);
                    ball.fill(cxt);
                    var mouse = tools.getMouse(cnv);
                    
                    var isMouseDown = false;
                    var dx = 0, dy = 0;
                    //oldX和oldY用于存儲小球舊的坐標(biāo)
                    var oldX, oldY;
                    //初始速度vx和vy都為0
                    var vx = 0, vy = 0;
                    //加入重力和反彈消耗
                    var gravity = 1.5;
                    var bounce = -0.8;
                    
                    cnv.addEventListener("mousedown", function () {
                        //判斷鼠標(biāo)點(diǎn)擊是否落在小球上
                        if (ball.checkMouse(mouse)) {
                                //鼠標(biāo)按下小球時(shí),isMouseDown設(shè)置為true
                                isMouseDown = true;
                                //鼠標(biāo)按下小球時(shí)掐暮,將當(dāng)前鼠標(biāo)位置賦值給oldX和oldY
                                oldX = ball.x;
                                oldY = ball.y;
                                dx = mouse.x - ball.x;
                                dy = mouse.y - ball.y;
                                document.addEventListener("mousemove", onMouseMove, false);
                                document.addEventListener("mouseup", onMouseUp, false);
                        }
                    }, false);
                    function onMouseMove() {
                        //鼠標(biāo)移動(dòng)時(shí),更新小球坐標(biāo)
                        ball.x = mouse.x - dx;
                        ball.y = mouse.y - dy;
                        
                        //加入邊界限制
                        //當(dāng)小球碰到左邊界時(shí)
                        if (ball.x < ball.radius) {
                                ball.x = ball.radius;
                                //當(dāng)小球碰到右邊界時(shí)
                        } else if (ball.x > cnv.width - ball.radius) {
                                ball.x = cnv.width - ball.radius;
                        }
                        //當(dāng)小球碰到上邊界時(shí)
                        if (ball.y < ball.radius) {
                              ball.y = ball.radius;
                              //當(dāng)小球碰到下邊界時(shí)
                        } else if (ball.y > cnv.height - ball.radius) {
                            ball.y = cnv.height - ball.radius;
                        }
                  }
                  function onMouseUp() {
                        //鼠標(biāo)松開時(shí)政钟,isMouseDown設(shè)置為false
                        isMouseDown = false;
                        document.removeEventListener("mouseup", onMouseUp, false);
                        document.removeEventListener("mousemove", onMouseMove, false);
                  }
                  
                  (function drawFrame() {
                        window.requestAnimationFrame(drawFrame, cnv);
                        cxt.clearRect(0, 0, cnv.width, cnv.height);
                        
                        if (isMouseDown) {
                              //如果isMouseDown為true,用當(dāng)前小球的位置減去上一幀的坐標(biāo)
                              vx = ball.x - oldX;
                              vy = ball.y - oldY;
                              
                              //如果isMouseDown為true瓢宦,更新oldX和oldY為當(dāng)前小球中心坐標(biāo)
                              oldX = ball.x;
                              oldY = ball.y;
                        } else {
                              //如果isMouseDown為false驮履,小球沿著拋擲方向運(yùn)動(dòng)
                              vy += gravity;
                              ball.x += vx;
                              ball.y += vy;
                              //邊界檢測
                              //碰到右邊界
                              if (ball.x > canvas.width - ball.radius) {
                                    ball.x = canvas.width - ball.radius;
                                    vx = vx * bounce;
                                    //碰到左邊界
                              } else if (ball.x < ball.radius) {
                                    ball.x = ball.radius;
                                    vx = vx * bounce;
                              }
                              //碰到下邊界
                              if (ball.y > canvas.height - ball.radius) {
                                    ball.y = canvas.height - ball.radius;
                                    vy = vy * bounce;
                                    //碰到上邊界
                              } else if (ball.y < ball.radius) {
                                      ball.y = ball.radius;
                                      vy = vy * bounce;
                              }
                      }
                      
                      ball.fill(cxt);
                    })();
              }
          </script>
    </head>
    <body>
          <canvas id="canvas" width="300" height="200" style="border:1px solid
silver; "></canvas>
    </body>
    </html>

18 高級動(dòng)畫

18.1 緩動(dòng)動(dòng)畫

想要實(shí)現(xiàn)緩動(dòng)動(dòng)畫疲吸,一般需要以下五個(gè)步驟。

(1)定義一個(gè)0~1之間的緩動(dòng)系數(shù)easing舰绘。

(2)計(jì)算出物體與終點(diǎn)之間的距離捂寿。

(3)計(jì)算出當(dāng)前速度秦陋,其中當(dāng)前速度=距離×緩動(dòng)系數(shù)治笨。

(4)計(jì)算新的位置旷赖,其中新的位置=當(dāng)前位置+當(dāng)前速度等孵。

(5)重復(fù)執(zhí)行第2~4步俯萌,直到物體達(dá)到目標(biāo)咐熙。

    var targetX = 任意位置糖声;
    var targetY = 任意位置分瘦;
    //動(dòng)畫循環(huán)
    var vx = (targetX - object.x) * easing;
    var vy = (targetY- object.y) * easing;

任意方向的緩動(dòng)動(dòng)畫

    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    var ball = new Ball(0, 0);
                    //定義終點(diǎn)的x軸坐標(biāo)和y軸坐標(biāo)
                    var targetX = cnv.width * (3 / 4);
                    var targetY = cnv.height * (1 / 2);
                    //定義緩動(dòng)系數(shù)
                    var easing = 0.05;
                    
                    (function frame() {
                          window.requestAnimationFrame(frame);
                          cxt.clearRect(0, 0, cnv.width, cnv.height);
                          var vx = (targetX - ball.x) * easing;
                          var vy = (targetY - ball.y) * easing;
                          ball.x += vx;
                          ball.y += vy;
                          
                          ball.fill(cxt);
                    })();
                    
                }
          </script>
    </head>
    <body>
          <canvas id="canvas" width="200" height="150" style="border:1px solid
silver; "></canvas>
    </body>
    </html>

18.2 緩動(dòng)動(dòng)畫應(yīng)用

緩動(dòng)動(dòng)畫不僅可以用于物體的運(yùn)動(dòng),還可以應(yīng)用于物體的其他各種屬性去团,包括大小土陪、顏色鬼雀、透明度以及旋轉(zhuǎn)等

不管緩動(dòng)動(dòng)畫應(yīng)用于什么方面源哩,其實(shí)現(xiàn)思路是一樣的励烦,也就是以下兩個(gè)步驟坛掠。

(1)當(dāng)前速度 =(最終值 - 當(dāng)前值)×緩動(dòng)系數(shù)屉栓。

(2)新的值 = 當(dāng)前值 + 當(dāng)前速度系瓢。

18.3 彈性動(dòng)畫簡介

在緩動(dòng)動(dòng)畫中夷陋,物體滑動(dòng)到終點(diǎn)就停下來了骗绕;但是在彈性動(dòng)畫中酬土,物體滑動(dòng)到終點(diǎn)后還會來回反彈一會兒格带,直至停止。

從技術(shù)上來說微宝,緩動(dòng)動(dòng)畫和彈性動(dòng)畫有以下幾個(gè)共同點(diǎn)。

(1)需要設(shè)置一個(gè)終點(diǎn)嗽桩。

(2)需要確定物體到終點(diǎn)的距離碌冶。

(3)運(yùn)動(dòng)和距離是成正比的扑庞。

兩者的不同在于“運(yùn)動(dòng)和距離成正比的”這一點(diǎn)的實(shí)現(xiàn)方式是不一樣的嫩挤。

(1)在緩動(dòng)動(dòng)畫中岂昭,跟距離成正比的是“速度”约啊。物體離終點(diǎn)越遠(yuǎn)恰矩,速度就越快外傅。當(dāng)物體接近終點(diǎn)時(shí)萎胰,它就幾乎停下來了技竟。

(2)在彈性動(dòng)畫中榔组,跟距離成正比的是“加速度”搓扯。物體離終點(diǎn)越遠(yuǎn)擅编,加速度越大爱态。剛剛開始锦担,由于加速度的影響洞渔,速度會快速增大磁椒。當(dāng)物體接近終點(diǎn)時(shí)本辐,加速度變得很小慎皱,但是它還在加速叶骨。由于加速度的影響天揖,物體會越過終點(diǎn)宝剖。然后隨著距離的變大万细,反向加速度也隨之變大赖钞,就會把物體拉回來雪营。物體在終點(diǎn)附近來回反彈一會兒献起,最終在摩擦力的作用下停止谴餐。

    ax = (targetX - object.x) * spring;
    ay = (targetY - object.y) * spring;
    vx += ax;
    vy += ay;
    vx *= friction;
    vy *= friction;
    object.x += vx;
    object.y += vy;

舉例:加入摩擦力的彈性動(dòng)畫

    <! DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
              function $$(id) {
                    return document.getElementById(id);
              }
              window.onload = function () {
                    var cnv = $$("canvas");
                    var cxt = cnv.getContext("2d");
                    
                    var ball = new Ball(0, cnv.height / 2);
                    var targetX = cnv.width / 2;
                    var spring = 0.02;
                    var vx = 0;
                    var friction = 0.95;
                    
                    (function frame() {
                          window.requestAnimationFrame(frame);
                          cxt.clearRect(0, 0, cnv.width, cnv.height);
                          var ax = (targetX - ball.x) * spring;
                          
                          vx += ax;
                          vx *= friction;
                          ball.x += vx;
                          
                          ball.fill(cxt);
                    })();
                    
              }
        </script>
    </head>
    <body>
         <canvas id="canvas" width="200" height="150" style="border:1px solid
silver; "></canvas>
    </body>
    </html>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市食绿,隨后出現(xiàn)的幾起案子器紧,更是在濱河造成了極大的恐慌品洛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芦拿,居然都是意外死亡蔗崎,警方通過查閱死者的電腦和手機(jī)缓苛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門未桥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舌菜,“玉大人日月,你說我怎么就攤上這事爱咬√ǖ撸” “怎么了串前?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坛吁。 經(jīng)常有香客問我拨脉,道長玫膀,這世上最難降的妖魔是什么帖旨? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮货抄,結(jié)果婚禮上碉熄,老公的妹妹穿的比我還像新娘锈津。我一直安慰自己琼梆,他們只是感情好茎杂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布倾哺。 她就那樣靜靜地躺著羞海,像睡著了一般却邓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撬腾,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天时鸵,我揣著相機(jī)與錄音,去河邊找鬼。 笑死锁保,一個(gè)胖子當(dāng)著我的面吹牛爽柒,可吹牛的內(nèi)容都是我干的浩村。 我是一名探鬼主播心墅,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘫筐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了之众?” 一聲冷哼從身側(cè)響起酝枢,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎竣付,沒想到半個(gè)月后古胆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逸绎,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年参淹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浙值。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡负蚊,死狀恐怖家妆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哨坪,我是刑警寧澤当编,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站鲤桥,受9級特大地震影響茶凳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜箱沦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望套耕。 院中可真熱鬧冯袍,春花似錦、人聲如沸碾牌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舶吗。三九已至征冷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間检激,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工腹侣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叔收,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓傲隶,卻偏偏與公主長得像饺律,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子跺株,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1 Canvas接口元素定義 1.1 getContext()方法 為了在canvas上繪制复濒,你必須先得到一個(gè)畫布...
    Kevin_Junbaozi閱讀 1,304評論 1 2
  • 初識canvas 前段時(shí)間重新看了一下canvas,覺得很有意思帖鸦,現(xiàn)在總結(jié)一下芝薇,只限于基礎(chǔ)的,中間有一些小的案例作儿,...
    b9760a805daa閱讀 981評論 1 0
  • canvas元素的基礎(chǔ)知識 在頁面上放置一個(gè)canvas元素洛二,就相當(dāng)于在頁面上放置了一塊畫布,可以在其中進(jìn)行圖形的...
    oWSQo閱讀 10,290評論 0 19
  • h5新增標(biāo)簽canvas 1.基本概念 <!DOCTYPE html> 01-Canvas開...
    煤球快到碗里來閱讀 368評論 1 0
  • H5 meta詳解 viewport width:控制 viewport 的大小攻锰,可以指定的一個(gè)值晾嘶,如果 600,...
    FConfidence閱讀 820評論 0 3