###### 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

- 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添加檔案路徑。

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

.ts檔

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

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

到這裡就Canvas 應該已經能在Angular專案上成功運行了哦 !
---
## 錯誤集
### # 在angular.json中沒添加JS文件路徑
:::warning
⚡**TypeError: * is not a constructor / ReferenceError: * is not defined**
:::
查看錯誤訊息中的 *** 是否為.js檔中的參數,可能是在angular.json中少添加了,此JS文件的路徑。


在angular.json scripts補上後,`**必須讓專案重新建置**`(ng server/ng build),才會正確哦 !
:::info
💡Angular CLI 是使用 Webpack 作為打包程式工具,
所以當輸入ng server / ng build 指令時,webpack會依angular.json內容打包,
沒被打包的程式是找不到的。`
:::
若你明明已經添加了,卻依舊有錯誤,可能就是沒讓專案重新建置哦 !

### # 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的程式繼續往下執行就發生錯誤。

所以,可以將想使用.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設定幀數。
:::

---
## 補充
**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.完成