---
# System prepended metadata

title: "\U0001F31D[T]Canvas"
tags: [Canvas, Angular]

---

###### tags: `Angular` `Canvas`
# 🌝[T]Canvas
## 如何將設計好的Canvas加進Angular專案中?

``` html
💡Canvas是在HTML中加入 <canvas id="canvas"></canvas>
  而定義出可繪製的區域，通常稱之為 「畫布」。
```

### STEP 1 : 在欲顯示的區域中(.html檔)插入`<canvas id="canvas"></canvas>`

``` html
<div class="vision flex-col">
   <canvas id="canvas"></canvas>
</div>
```

### STEP 2 : 匯入設計好的.js檔

> 若在Angular添加自訂義的JS文件，必須在angular.json build scripts 中添加`JS文件路徑`。

#### 設定說明

- architect：建置、測試等執行指令設定
    - targetName 命令名稱，ex: build、serve、extract-i18n、test、lint

  ![](https://i.imgur.com/SgIz8oq.png)


    - builder 命令會使用的 builder 名稱，package-name:builder-name
    - options builder 所需要的執行參數設定
    - configurations 延伸 builder 參數設定
        - configurationName 參數設定

#### 添加自訂義JS文件

例如 : 在安裝jquery時，會添加檔案路徑到angular.json組數中。

``` json
"build": {   
    "scripts": [
      "node_modules/jquery/dist/jquery.js",
      "node_modules/signalr/jquery.signalR.js",
      "node_modules/lzma/src/lzma.js",
      "node_modules/lzma/src/lzma_worker.js",
      "node_modules/echarts/dist/echarts.min.js",
      "node_modules/ua-parser-js/src/ua-parser.js"
    ]
}
```

所以，我們先在 src/assets 設置js資料夾，並將.js檔放進去 (讓檔案結構好看而已)，再到angular.json添加檔案路徑。
![](https://i.imgur.com/dq4WqHq.png)

### STEP 3 : 在.ts檔中使用.js檔的函式

若完成上面的步驟，就可以在.ts檔中直接使用.js檔裡的函式了。

.js檔
![](https://i.imgur.com/qo5uEgo.png)

.ts檔
![](https://i.imgur.com/3LOg710.png)

執行起來後你會發現，他雖然可以正常執行，但程式及終端機會有許多錯誤提示。

![](https://i.imgur.com/koaVciC.png)

這時候你可以在程式外層用declare var宣告它們(雖然這方法不是很好)。

![](https://i.imgur.com/Cggx9TE.png)

到這裡就Canvas 應該已經能在Angular專案上成功運行了哦 !

---

## 錯誤集

### # 在angular.json中沒添加JS文件路徑

:::warning
⚡**TypeError: * is not a constructor / ReferenceError: * is not defined**
:::

查看錯誤訊息中的 *** 是否為.js檔中的參數，可能是在angular.json中少添加了，此JS文件的路徑。

![](https://i.imgur.com/iyDIu9y.png)

![](https://i.imgur.com/46e7ygY.png)

在angular.json scripts補上後，`**必須讓專案重新建置**`(ng server/ng build)，才會正確哦 !

:::info
💡Angular CLI 是使用 Webpack 作為打包程式工具，
所以當輸入ng server / ng build 指令時，webpack會依angular.json內容打包，
沒被打包的程式是找不到的。`
:::

若你明明已經添加了，卻依舊有錯誤，可能就是沒讓專案重新建置哦 !

![](https://i.imgur.com/CJLFSWg.png)

### # Canvas標籤<canvas></canvas>還沒生成，.js檔的程式已跑完

:::warning
⚡**Error: Uncaught (in promise): TypeError: Cannot set property 'width' of null**
:::

當我在執行.js程式到 
``` typescript 
canvas = document.getElementById("canvas");
``` 
.html檔還沒準備好，所以canvas的值會是null，.js的程式繼續往下執行就發生錯誤。

![](https://i.imgur.com/tikJtUE.png)

所以，可以將想使用.js檔的函式移到 `ngAfterViewInit()`裡面。
``` typescript

ngAfterViewInit() {
    // use .js funcion;
 }
  
```   
### # 畫面重複載入動畫會延遲

#### window.requestAnimationFrame() / window.cancelAnimationFrame();

requestAnimationFrame() : 要求瀏覽器在下次重繪畫面前呼叫特定函數更新動畫。

cancelAnimationFrame() : 取消先前的動畫幀請求window.requestAnimationFrame()。

**tip: 可以把取消請求寫在ngOnDestroy();**
``` typescript
canvas: number;
this.canvas = window.requestAnimationFrame(callback);
window.cancelAnimationFrame(this.canvas);
```

雖然requestAnimationFrame()被視為目前最佳的JavaScript視覺更新函式，但cancelAnimationFrame()對瀏覽器的版本要求過高，所以在瀏覽器版本不支援的情況下，就會不會取消動畫更新的請求。

:::warning
💡若沒在離開有動畫頁時沒用cancelAnimationFrame()，就會導致動畫載入多次延遲。
:::

#### createjs.Ticker.addEventListener() / createjs.Ticker.removeAllEventListeners();

若是用createjs的Ticker(定時器)，設置監聽事件來刷新舞台，必須自己移除Ticker事件，Ticker在暫停時頁面仍會觸發，所以多次載入動畫亦會有延遲的情況。

**tip: 可以把移除事件寫在ngOnDestroy();**

:::info
💡CreateJS提供兩種渲染模式，setTimeout / requestAnimationFrame，預設為 setTimeout / 幀數20，可用setFPS設定幀數。
:::

![](https://i.imgur.com/BExi5E3.png)

---

## 補充

**CreateJS:**

 [https://aotu.io/notes/2017/07/19/createjs/index.html](https://aotu.io/notes/2017/07/19/createjs/index.html)

**setInterval, setTimeout, requestAnimationFrame:** 

 [https://pjchender.github.io/2018/06/25/web-計時與動畫-timer-with-setinterval-settimeout-requestanimationframe/](https://pjchender.github.io/2018/06/25/web-%E8%A8%88%E6%99%82%E8%88%87%E5%8B%95%E7%95%AB-timer-with-setinterval-settimeout-requestanimationframe/)

---

## 本次流程

### 1. 接收檔案

一開始視覺傳了一份.js檔與要加在.html內容。

login_vision.js

``` javascript
(function (cjs, an) {

var p; // shortcut to reference prototypes
var lib={};var ss={};var img={};
lib.ssMetadata = [];


// symbols:
// helper functions:

function mc_symbol_clone() {
	var clone = this._cloneProps(new this.constructor(this.mode, this.startPosition, this.loop));
	clone.gotoAndStop(this.currentFrame);
	clone.paused = this.paused;
	clone.framerate = this.framerate;
	return clone;
}

function getMCSymbolPrototype(symbol, nominalBounds, frameBounds) {
	var prototype = cjs.extend(symbol, cjs.MovieClip);
	prototype.clone = mc_symbol_clone;
	prototype.nominalBounds = nominalBounds;
	prototype.frameBounds = frameBounds;
	return prototype;
	}
//....內容太多略過
})(createjs = createjs||{}, AdobeAn = AdobeAn||{});
var createjs, AdobeAn;
```
html

``` javascript
<canvas id="canvas" ></canvas>
    -----------------------------------------------------------------------------------------
    <script>
    	var canvas, stage, exportRoot, fnStartAnimation;
    	function movie() {
    		canvas = document.getElementById("canvas");
    		var comp=AdobeAn.getComposition("53845E679F784E4AB6C6A02715097C17");
    		var lib=comp.getLibrary();
    		createjs.MotionGuidePlugin.install();
    		handleComplete({},comp);
    	}
    	function handleComplete(evt,comp) {
    		var lib=comp.getLibrary();
    		var ss=comp.getSpriteSheet();
    		exportRoot = new lib.login_vision();
    		stage = new lib.Stage(canvas);	
    		//Registers the "tick" event listener.
    		fnStartAnimation = function() {
    			stage.addChild(exportRoot);
    			createjs.Ticker.setFPS(lib.properties.fps);
    			createjs.Ticker.addEventListener("tick", stage);
    		}	    
    		
    		AdobeAn.makeResponsive(false,'both',false,1,[canvas]);	
    		AdobeAn.compositionLoaded(lib.properties.id);
    		fnStartAnimation();
    	}
    	window.requestAnimationFrame( movie );
    </script>
```

### 2.整理資訊 / 蒐集資料

因為視覺給的東西不是用Angular寫的(猜測是用工具)，又是第一次接觸canvas，所以先找該怎麼在angular專案中如何使用canvas以及js檔案要如何匯入angular專案中使用...。 以下是蒐集後的整理。

- html內必須要有<canvas></canvas>標籤
- <script></script>標籤必須放在<head></head>或 <body></body>標籤內
- 要使用外部.js檔案，必須將.js檔建置時有被打包到

### 3.調整程式

視覺給的東西必須調整才能使用，因此在撰寫過程中邊調整。以下是調整視覺的檔案。

- 將html<script></script>的內容，合併到.js檔案內
- 調整語法

login_vision.js
``` typescript
function runCanvas(cjs, an) { //(function (cjs, an) { >> modi by ruby
// ....內容太多略過
} (createjs = createjs || {}, AdobeAn = AdobeAn || {});
var createjs, AdobeAn;

// <script>

var canvas, stage, exportRoot, fnStartAnimation;
function movie() {
  canvas = document.getElementById("canvas");
  var comp = AdobeAn.getComposition("53845E679F784E4AB6C6A02715097C17");
  var lib = comp.getLibrary();
  createjs.MotionGuidePlugin.install();
  handleComplete({}, comp);
}
function handleComplete(evt, comp) {
  var lib = comp.getLibrary();
  var ss = comp.getSpriteSheet();
  exportRoot = new lib.login_vision();
  stage = new lib.Stage(canvas);
  //Registers the "tick" event listener.
  fnStartAnimation = function () {
    stage.addChild(exportRoot);
    // createjs.Ticker.setFPS(lib.properties.fps);
    createjs.Ticker.addEventListener("tick", stage);
  }

  AdobeAn.makeResponsive(false, 'both', false, 1, [canvas]);
  AdobeAn.compositionLoaded(lib.properties.id);
  fnStartAnimation();
}
// modi by ruby ---str
// window.requestAnimationFrame( movie );
function stop(){
  createjs.Ticker.removeAllEventListeners();
}
// modi by ruby ---end
```

### 4.測試

在前面的步驟是一邊查資料一邊實作，成功後多次測試，看會不會有其他錯誤。

### 5.完成