CreateJS入門 -- 註釋詳細到爆炸(My Style)

MagicEyes發表於2018-07-18

寫在前面

首先,還是謝謝大家的支援,謝謝!記得在之前的文章中我說過自己算是一個半文藝程式設計師,也一直想著寫一寫技術性和其他偏文學性的文章。雖然自己的底子沒有多麼優秀,但總是覺得這個過程中可以督促自己去思考,督促自己去學習和交流。畢竟每天忙忙碌碌之餘,還是要活出自己不一樣的生活。

其次,我開通了個人的 GitHub主頁,裡面有自己的技術文章,還會有個人的隨想、思考和日誌。以後所有的文章都會第一時間更新到這裡,然後同步到其他平臺。有喜歡的朋友可以沒事去逛逛,再次感謝大家的支援!

什麼是CreateJS

官網介紹中文):CreateJS 是一組模組化程式碼庫和工具套件,可以獨立工作也可以組合工作,用於通過HTML5技術來在網頁上開發豐富的互動式內容。

四個核心庫

CreateJS主要包含如下四個類庫:

  • EaselJS – 簡化處理HTML5畫布(核心)
  • TweenJS – 用來幫助設計H5動畫,調整HTML5屬性
  • SoundJS – 用來簡化處理HTML5 audio 音訊
  • PreloadJS – 幫助管理和協調載入中的一些資源

今天,主要來了解一下 EaselJS

EaselJS

CreateJS入門 -- 註釋詳細到爆炸(My Style)

EaselJS 簡介

EaselJS 是一個JavaScript庫,用來簡單快捷的操作 HTML5 Canvas 標籤。在建立H5遊戲,生成藝術作品、處理其他高階圖形化等工作中有著很友好的體驗。

EaselJS中的一些核心類

  1. Stage Class -- 建立舞臺
  2. Text Class -- 繪製文字
  3. Graphics Class -- 繪製圖形
  4. Shape Class -- 繪製圖形
  5. Bitmap Class -- 繪製圖片
  6. Ticker Class -- 定時廣播
  7. ......等

一些"栗子"

繪製文字(Text Class)

定義一個<canvas> </canvas> 畫布。

// HTML:
<!-- Text Class 文字類-->
<canvas id="demo1" width="650" height="400"></canvas>複製程式碼

呼叫EaselJS提供的API - new createjs.Text(),繪製文字

// JS
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script>
  window.onload = () => {
    /**
      * Test Class 文字類 -- demo
      */
    let stage1 = new createjs.Stage("demo1");

    let text1 = new createjs.Text("Text 1 !", "bold 26px Arial", "#ff7700");
    text1.regX = -50;     // 沿X軸負方向的偏移量
    text1.regY = -50;     // 沿Y軸負方向的偏移量
    text1.x = 100;        // 繪製源點 X座標
    text1.y = 50;         // 繪製源點 Y座標

    let text2 = new createjs.Text("旋轉+XY拉伸!", "bold 18px Arial", "#ff7700");
    text2.x = 50;
    text2.y = 50;
    text2.rotation = 50;      // 旋轉角度 DEG
    text2.scaleX = 3;         // X軸放大(拉伸)
    text2.scaleY = 2;         // X軸放大(拉伸)

    let text3 = new createjs.Text("XY軸傾斜", "bold 50px Arial", "#ff7700");
    text3.x = 300;
    text3.y = 200;
    text3.skewX = 45;         // X軸傾斜角度 DEG
    text3.skewY = 20;         // Y周傾斜角度 DEG

    let text4 = new createjs.Text("文字shadow", "bold 30px Arial", "#ff7700");
    text4.x = 400;
    text4.y = 100;
    text4.shadow = new createjs.Shadow("#000000", 5, 5, 10);      // 建立一個shadow例項Object

    stage1.addChild(text1, text2, text3, text4);
    stage1.update();    // 更新舞臺,每次修改操作後需要更新真個舞臺才有效果
  }
</script>複製程式碼

繪製圖形(Graphics Class)

定義一個<canvas> </canvas> 畫布。

// HTML:
<!-- Graphics Class 文字類-->
<canvas id="demo2" width="650" height="400"></canvas>複製程式碼

呼叫EaselJS提供的API - new createjs.Graphics(),繪製圖形

// JS
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script>
  window.onload = () => {
    /**
      * Graphics Class 繪圖類 -- demo
      * 用於生成向量繪圖指令
      */
    let stage2 = new createjs.Stage('demo2')

    // 畫線
    let g = new createjs.Graphics();
    /* 同一個 Graphics 例項, 可以多次繪製,以下線段、折線都是用 g 例項繪製的*/
    g.setStrokeStyle(10).beginStroke("#d23c4f").moveTo(400,10).lineTo(600,100)
    // 簡寫形式
    g.ss(20).s('#fafa35').mt(400,100).lt(400,260)
    // 多點折線的簡寫形式
    g.ss(1).s('#000').mt(600,400).lt(600, 200).lt(400,300).lt(500, 550)

    // Graphics 例項不能直接 addChild() 到舞臺 stage 中,例項化為 Shape 例項後才可以
    let line = new createjs.Shape(g)

    // 圓
    let g1 = new createjs.Graphics();
    g1.setStrokeStyle(1);         // 描邊
    g1.beginStroke("#000000");    // 描邊顏色
    g1.beginFill("red");          // 圖形填充
    g1.drawCircle(0,0,100);        // 繪製 (X, X, R)
    let c1 = new createjs.Shape(g1)     // 例項化Shape物件

    // 矩形
    let g2 = new createjs.Graphics().beginStroke("red").beginFill("blue").drawRect(150, 0, 200, 100);     // X, Y, W, H
    let c2 = new createjs.Shape(g2)

    // 命令物件
    let g3 = new createjs.Graphics();
    // 每個圖形介面呼叫後會生成一個命令物件,可以使用.command訪問,它儲存對已建立或附加的最後一個命令的引用
    let fillCommand = g3.beginFill("green").command;
    g3.drawCircle(200,200,50);        // 繪製 (X, X, R)
    let c3 = new createjs.Shape(g3);

    // 一些非同步操作後,更新填充樣式/顏色:
    setTimeout(() => {
      fillCommand.style = "gray";
      stage2.update();                // 不更新舞臺,不會重新渲染
    }, 2000);

    // 點選事件
    //c3.addEventListener('click', () => {
    // alert(123)
    // fillCommand.style = "gray";
    // stage2.update();          // 不更新舞臺,不會重新渲染
    //})
  
    stage2.addChild(c1, c2, c3, line);
    stage2.update();
  }
</script>複製程式碼

繪製影象imgaes(Bitmap Class)

定義一個<canvas> </canvas> 畫布。

// HTML:
<!-- Bitmap Class 影象類-->
<canvas id="demo3" width="650" height="400"></canvas>複製程式碼

呼叫EaselJS提供的API - new createjs.Bitmap(),繪製影象

// JS
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script>
  window.onload = () => {
    /**
      * bitmap Class 影象類
      * 用於在畫布顯示列表中渲染影象
      */
    let stage3 = new createjs.Stage('demo3')

    // 渲染圖片
    let bitmap = new createjs.Bitmap('./assets/img/hill1.png')
    bitmap.alpha = 0.6      // 透明度
    bitmap.cursor = 'help'
    // 建立一個shadow例項Object(color, offsetX, offsetY, blur)
    bitmap.shadow = new createjs.Shadow("#97c89e", 20, 10, 20);

    // 給圖片新增遮罩
    let bitmap2 = new createjs.Bitmap('./assets/img/avatar.jpg')
    bitmap2.x = 400;        // 圖片繪製的起始點X座標
    bitmap2.y = 0;          // 圖片繪製的起始點Y座標
    //遮罩圖形
    let shape = new createjs.Shape();
    shape.graphics.beginFill('#000').drawCircle(0, 0, 100);
    shape.x = 500;          // 圓心X座標
    shape.y = 100;          // 圓心Y座標
    bitmap2.mask = shape;   //給圖片bg新增遮罩

    // 繪製一片草地
    let groundBg = new createjs.Bitmap("./assets/img/ground.png").image;
    let ground = new createjs.Shape();
    w = stage3.canvas.width;      // 650
    h = stage3.canvas.height;     // 400
    stage3.addChild(ground)

    stage3.addChild(bitmap, bitmap2)
    stage3.update()       // 此處重新整理無效

    // 監聽定時廣播
    createjs.Ticker.timingMode = createjs.Ticker.RAF;
    createjs.Ticker.addEventListener('tick',(event) => {
      ground.tileW = groundBg.width;
      ground.y = h - groundBg.height;
      ground.graphics.beginBitmapFill(groundBg).drawRect(0, 0, w, groundBg.height);
      ground.cache(0, 0, w, groundBg.height);
        
      stage3.update()
    });
  }
</script>複製程式碼

一個小遊戲(來自官網Demo) Running man

定義一個
畫布

<!-- HTML -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>running-man game</title>
</head>
<body>
  <canvas id="demoCanvas" width="960" height="400"></canvas>
  </body>
</html>複製程式碼

JS程式碼

這裡就不寫具體思路分析了,程式碼不長,註釋也很詳細,方便理解,直接上程式碼。

// JS
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script>
  window.onload = () => {
    let stage, w, h, loader;
    let sky, grant, ground, hill, hill2;

    function init() {
      stage = new createjs.StageGL("demoCanvas");

      // 獲取畫布的寬和高,後面計算使用
      w = stage.canvas.width;     // 960
      h = stage.canvas.height;    // 400

      // 定義靜態資源
      let manifest = [{
        src: "spritesheet_grant.png", id: "grant"}, {     // 人物動作雪碧圖
        src: "sky.png", id: "sky"}, {           // 天空
        src: "ground.png", id: "ground"}, {     // 地面
        src: "hill1.png", id: "hill"}, {        // 遠山
        src: "hill2.png", id: "hill2"           // 近山
      }];     // Array, String, Object
      
      // 建立資源載入佇列
      // (Boolean) 用XHR還是用HTML標籤來載入
      // 如果是false的時候,就用標籤來載入,如果不能用標籤的話,就用XHR來載入。預設是true,用XHR來載入。
      loader = new createjs.LoadQueue(false);
      // 新增"資源載入完成"事件
      loader.addEventListener("complete", handleComplete);
      // 載入資源
      loader.loadManifest(manifest, true, "./assets/img/");  // (manifest, loadNow, basePath)
    }

    /**
      * 靜態資源載入完成,處理函式
      */
    function handleComplete() {
      // 渲染天空
      sky = new createjs.Shape();
      sky.graphics.beginBitmapFill(loader.getResult("sky")).drawRect(0, 0, w, h);
      // 定義快取區域(整個天空的區域))
      sky.cache(0, 0, w, h);

      // 渲染地面
      let groundImg = loader.getResult("ground");
      ground = new createjs.Shape();
      // 注意:drawRect()寬度要躲繪製一個單位
      ground.graphics.beginBitmapFill(groundImg).drawRect(0, 0, w + groundImg.width, groundImg.height);
      ground.tileW = groundImg.width;
      ground.y = h - groundImg.height;
      // 快取區域(地面的區域)
      ground.cache(0, 0, w + groundImg.width, groundImg.height);

      // 隨機渲染遠處山脈
      hill = new createjs.Bitmap(loader.getResult("hill"));
      // 設定影象轉換
      // setTransform([x=0], [y=0], [scaleX=1], [scaleY=1], [rotation=0], [skewX=0], [skewY=0], [regX=0], [regY=0])
      hill.setTransform(Math.random() * w, h - hill.image.height * 4 - groundImg.height, 4, 4);
      hill.alpha = 0.5;     // 設定透明度

      // 隨機渲染近處山脈
      hill2 = new createjs.Bitmap(loader.getResult("hill2"));
      hill2.setTransform(Math.random() * w, h - hill2.image.height * 3 - groundImg.height, 3, 3);

      // 建立雪碧圖動畫
      let spriteSheet = new createjs.SpriteSheet({
        framerate: 30,      // 幀率 FPS
        "images": [loader.getResult("grant")],      // 雪碧圖原圖
        "frames": {"width": 165, "height": 292, "count": 64, "regX": 82, "regY": 0},  // 初始化
        // 定義動畫
        "animations": {
          "run": [0, 25, "run"],     // name: [開始索引, 結束索引, '下一個動畫名稱', 倍率]
          "jump": [26, 63, "run"]
        }
      });

      // 繪製動畫
      grant = new createjs.Sprite(spriteSheet, "run");
      // 處理雪碧圖人物下方空白
      grant.y = 35;

      // 將生成的所有內容渲染至舞臺
      stage.addChild(sky, ground, hill, hill2, grant);

      // 監聽舞臺上的滑鼠點選事件
      stage.addEventListener("stagemousedown", () => {
        // 跳轉播放 jump 動畫
        grant.gotoAndPlay("jump");
      });

      createjs.Ticker.timingMode = createjs.Ticker.RAF;     // RAF / RAF_SYNCHED / TIMEOUT
      createjs.Ticker.addEventListener("tick", tick);
    }


    /**
      * 定時器-重繪舞臺
      */
    function tick(event) {
      // event.delta -- 上一次tick到當前tick的ms
      let deltaS = event.delta / 1000;
      // 雪碧圖人物移動距離
      let position = grant.x + 150 * deltaS;

      // getBounds() -- 返回當前幀相對於雪碧圖原點的邊界
      let grantW = grant.getBounds().width * grant.scaleX;
      grant.x = (position >= w + grantW) ? -grantW : position;

      ground.x = (ground.x - deltaS * 150) % ground.tileW;

      // 從右至左移動山脈
      hill.x = (hill.x - deltaS * 30);
      // 如果山脈從左側離開螢幕
      if (hill.x + hill.image.width * hill.scaleX <= 0) {
        hill.x = w;     // 重置回螢幕最右側
      }

      // 處理如上
      hill2.x = (hill2.x - deltaS * 45);
      if (hill2.x + hill2.image.width * hill2.scaleX <= 0) {
        hill2.x = w;
      }

      stage.update();
    }

    // 程式主入口-初始化
    init()
  }
</script>複製程式碼

完整程式碼

示例demo的GitHub地址:完整程式碼


相關文章