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