本文翻自【互動網頁程式教學】用 GSAP 製作直播互動動態效果,若是對文章內容有疑問,或是想要老闆手把手帶你飛,都可以觀看影片跟著動手做,也附上這次成品。
這次要帶大家使用 vue.js, vue-cli, gsap 來模擬 facebook 手機版的直播畫面,首先將視訊鏡頭中使用者的畫面做為直播畫面,下半部留言區塊點擊表情符號後,表情符號加上彈幕的動畫效果。輸入訊息後,訊息會經由轉場動畫出現在畫面中,使用者也能點選刪除留言來看到訊息的離場動畫。
製作表情符號進出場的動畫會使用到 gsap ,gsap 是由 greenSock所開發,常被用來取代以前的 flash,提供許多製作動畫的套件,包含這次會使用到的 tweenMax 及 timelineMax,由於是透過 js 所撰寫,動畫呈現有更大的自由度。但要注意個人和商用部分功能是免費的,若需引入專案時要多留意。範例中,也會帶大家使用 vue transition 提供的兩種模式,來觸發表情符號與留言的進出場效果。
這次直播筆記會帶大家學會以下內容:
- 認識 vue 中的 ref, $refs
- 使用原生 js 載入視訊影像
- 使用 gsap 製作表情符號過場動畫
- 使用 vue 中的 transition 製作過場動畫
事前準備
開發環境
老闆在這次專案改使用 CodeSandbox進行開發,關於環境和其他套件的設定,同學可以參考老闆的成品。
CodeSandbox 比起之前老闆示範時常用的Codepen來說,功能較完整一些,除了提供大家建立 project、安裝需要使用的 library 之外,也能在上面跑 npm 的 package 設定。製作大型專案需要測試時,老闆習慣會使用 CodeSandbox ,但小缺點就是不支援 emmet(註:輸入簡化碼後會自動產生完整HTML & CSS程式碼,加快程式碼輸入,也降低手誤機率),撰寫程式碼較不方便一些。
透過 new sandbox 創建新的專案,選擇 Vue( vue2 的 cli)後,可以看到左邊有 files 欄,包含專案所有資料夾及檔案,這次專案只會在 App.vue 這支檔案中開發,同學們不用被資料夾結構嚇到。
打開 App.vue 檔案之後可以發現有三個區塊分別為:
- <template>:撰寫 html,改使用 pug
- <script>:撰寫 vue 及 js
- <style>:撰寫 css,改使用 scss
首先,將使用不到的元件 HelloWorld 相關的敘述全部拔除,準備好基本的結構後,可以看到右邊的畫面只剩下一個 vue 的 logo。
//將HTML撰寫語言改成pug <template lang="pug"> #app img(alt="Vue logo", src="./assets/logo.png", width="25%") </template> <script> export default { name: "App" }; </script> //將撰寫語言改成scss <style lang="scss"> #app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
接著,讓我們來安裝這次會使用的套件。畫面的左欄有 Dependencies,因為我們在創建專案時選擇 vue,可以看到 codeSandbox 已經幫我們裝了兩個套件。我們只需要再將 gsap 載入即可,在放大鏡區塊(Add Dependency)打入 gsap 並選擇安裝,就大功告成。
接下來會使用到的 API:
這次專案會使用以下的內容,這邊先重點整理給大家,不清楚的地方,可以透過後面跟著老闆操作,或是觀看相關文件,了解每個 api 使用時機。
Vue
- script
- el:資料要綁定的區塊
- data:vue 要綁定的資料放置處
- mounted:vue 的生命週期, el 被掛載之後會執行裡面的程式碼
- methods:使用到的 function 放置處
- computed:計算屬性,會因為 data 內的值改變,而跟著變動
- $refs:可以搭配 ref 屬性來取得 DOM 元件 (延伸參考資料)
// javascript var vm = new Vue({ el: '#app', data: { text: 'Hello World', texts: ['H', 'i'] }, mounted () {...}, methods: { changeText () { this.$refs.input.focus() } }, computed: { showText () { return ...} } })
- template:畫面部分會使用到以下內容
- {{text}}:將資料綁定到畫面中顯示
- v-model:將資料綁定到畫面中顯示或修改
- v-for:讓陣列資料重複產生 dom,可以搭配索引值綁定
- :key:可以搭配 v-for 使用,提供 vue 識別每個 dom 是不同的,在傳入 v-for 的陣列中,key 要是獨特的值,避免識別上出錯。
- @click=””:當點擊目標物會觸發傳入 click 的內容
- ref:可以在程式碼中搭配 $refs 取得 DOM 元件(延伸參考資料)
// html #app p {{text}} input (v-model="text ref="input") p(v-model="showText") div(:class="") div(v-for="(item, idx) in texts", :key="idx") {{item}} button(@click="changeText()")
- vue – transition-group:vue 提供給在 dom 要被加入、移除或更新時的動態效果,使用方法會在後面實做中解說。若想要參閱官方說明文件可點此閱讀。
- js:Math.random():會產出一個大於等於 0、小於 1 之間的隨機小數。若想要參閱更詳細的說明文件可點此閱讀。
gsap
- tweenMax:針對指定的 DOM 在動畫時間內執行動畫
TweenMax.to(執行動畫的DOM, 動畫時間, { y: 200, // 位移 200 px rotate: 360, // 旋轉 360 度 delay: 3, // 3 秒後才執行動畫 repeat: 2, // 會重複執行兩次 yoyo: true // 會倒帶後再執行一次 });
- timeLineMax:可讓動畫多段依序進行 ,使用 to 去接後續要播放的動畫
let tl = new TimelineMax() // 新增 tl 變數 tl.to(this.$refs.logo, 1, { // 使用 this.$refs 去取得 dom y: 200, rotate: 360 }).to(this.$refs.logo, 1, { scale: 2 })
js – getUserMedia
提供瀏覽器獲得使用者影像,navigator 會詢問瀏覽器有沒有影片可以使用,找到之後將其放到 video tag 中,要記得提供瀏覽器取用麥克風或錄影機的權限。(延伸參考資料)
var constraints = { audio: true, video: { width: 1280, height: 720 } }; navigator.mediaDevices .getUserMedia(constraints) .then((mediaStream) => { var video = this.$refs.myVideo; video.srcObject = mediaStream; // 將 video 指定到指定的 DOM 中 video.onloadedmetadata = function (e) { video.play(); }; }) .catch(function (err) { // 出錯時的處裡 console.log(err.name + ": " + err.message); });
跟著老闆開始動手做
操作一段與多段動畫
我們在環境準備階段已經把 gsap 裝到專案中,首先我們使用 vue 的 logo 來練習 gsap 製作動畫方式。gsap 內有很多個製作動畫的方式,老闆帶大家操作兩種型式的動畫,分別為 tweenMax, timelineMax 兩種。
讓 logo 動起來之前,先介紹兩種方式讓 gsap 抓到 logo 這個 dom 元件。
- html 賦予 id,使用 #logo 讓 gsap 取得 dom
- html 賦予 ref,使用 $refs 讓 gsap 取得 dom
我們想要讓 logo 一秒內下滑,並旋轉,這邊會使用到 gsap 的 TweenMax,所以我們將它 import 到專案中,並在 vue 的 mounted 階段操作動畫,mounted 是 vue 的生命週期,會在 vue app 載入後執行裡面的動畫,寫法及參數如下。gsap 有許多的動畫值可以操作,建議同學們不用死背,需要時去查文件即可。
- TweenMax.to(dom, 秒數, 動畫物件) (延伸參考資料https://greensock.com/docs/v2/TweenMax)
<template lang="pug"> #app img#logo(alt="Vue logo", src="./assets/logo.png", width="25%") </template> <script> import { TweenMax } from "gsap"; export default { name: "App", mounted() { TweenMax.to("#logo", 1, { y: 200, // 位移 200 px rotate: 360, // 旋轉 360 度 delay: 3, // 3 秒後才執行動畫 repeat: 2, // 會重複執行兩次 yoyo: true // 會倒帶後再執行一次 }); }, }; </script>
完成一段式的動畫後,會發現 TweenMax.to() 無法滿足多段式的動畫需求。如果我們希望動畫是多段小動畫依序進行,那要一直寫許多 TweenMax.to 並加上 delay 嗎?
其實,gsap 有另一個功能 TimelineMax 就可以達成我們的需求,使用 TimeLineMax 時,要注意需要先新增 new TimelineMax 的變數,使用的方式是第一段動畫完成後,使用 to 去接後續要播放的動畫,傳入的參數與 TweenMax 一樣。使用方法如下:
我們前面提到有兩種方式可以取得 dom 元件,這邊改使用 vue 所提供的 ref 及 $refs 去取得要執行動畫的 dom 元件。
<template lang="pug"> #app img(ref="logo", alt="Vue logo", src="./assets/logo.png", width="25%") //- img 多加 ref 屬性 </template> <script> import { TweenMax, TimelineMax } from "gsap"; export default { name: "App", mounted() { let tl = new TimelineMax() // 新增 tl 變數 tl.to(this.$refs.logo, 1, { // 使用 this.$refs 去取得 dom y: 200, rotate: 360 }).to(this.$refs.logo, 1, { scale: 2 }) }, }; </script>
直播畫面與 live 標籤
接著來處理畫面,會處理的內容分別為:模擬手機直播畫面的樣式切版、使用 video 視訊畫面做為直播影片、利用 ref 來取得 video 的位置、 live 動畫效果與時間計數器。
- 模擬手機畫面:只需要針對畫面樣式進行調整,在幫 live 區塊做定位時,記得在 #app 多加上 position: relative,否則預設會以 body 做為參考。
- 使用 video 作為直播影片:在畫面上準備待會要放置 video 的 dom,並在裡面放上一個 video tag ,這邊可以對 video tag 使用 muted 屬性,待會的影像就會是靜音的狀態。接著在 mounted 中使用 getUserMedia 來獲得影像。vue 初始化時,會先建立一個空的 video DOM,到了 mounted (vue app 載入之後)階段,navigator會詢問瀏覽器有沒有影片可以使用,找到之後將其放到 video tag 中,要記得提供瀏覽器取用麥克風或錄影機的權限。
- 這邊我們也練習前面提到的 ref ,來取得 video tag,MDN 上面提供的範例是使用 function,因為 function 有自己的 scope,無法在函式內部使用 this 取得 vue 本身。有兩種解法,在外面宣告 _this 變數,或是用 es6 的 arrow function。
- live 動畫效果:要幫 live 字樣加上呼吸燈的亮暗亮暗效果,除了前面有練習過的 repeat, yoyo 屬性外,也會使用到 gsap 中的 easing api,可以選擇自己喜歡的時間曲線後,在專案中引入。
- 時間計數器:在 mounted 中使用 setInterval 來進行每秒都會增加時間的值,利用這個值換算成需要的格式。透過 computed 來回傳需要的字串,computed 的使用時機為「已經知道資料是什麼,基於原本的值去加工後回傳,不會影響到原本的資料」。也利用 padStart 將時間中的時分秒三個資料都能是2位數,在最後回傳結果字串時,利用 es6 的頓號`來組裝字串。
<template lang="pug"> #app .liveLabel .red(ref="liveTag") LIVE //LIVE小標 .counter {{timeLabel}} //時間計數器 .videoContainer video(ref="myVideo", autoplay="true", muted) </template> <script> import { TweenMax, Power0 } from "gsap"; export default { name: "App", data() { return { time: 0, }; }, computed: { timeLabel() { let sec = this.time % 60; let min = Math.floor(this.time / 60) % 60; let hour = Math.floor(this.time / 3600) % 24; let pd = (num) => (num + "").padStart(2, "0"); // padStart api, 不足長度字串在前面補上0 return `${pd(hour)}:${pd(min)}:${pd(sec)}`; }, }, mounted() { // 每秒執行一次增加 time 的值 setInterval(() => { this.time++; }, 1000); // Live 呼吸燈 TweenMax.to(this.$refs.liveTag, 1, { css: { backgroundColor: "rgba(255, 0, 0, 0.3)", }, ease: Power0.easeNone, repeat: -1, yoyo: true, }); // 影片串流 var constraints = { audio: true, video: { width: 1280, height: 720 } }; navigator.mediaDevices .getUserMedia(constraints) .then((mediaStream) => { // 改成 arrow function, this就不會抓到內部而是外層的元件 var video = this.$refs.myVideo; video.srcObject = mediaStream; video.onloadedmetadata = function (e) { video.play(); }; }) .catch(function (err) { console.log(err.name + ": " + err.message); }); }, }; </script> <style lang="scss"> html, body { background-color: #333; display: flex; justify-content: center; align-items: center; } #app { position: relative; font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; width: 390px; height: 744px; background-color: white; } .videoContainer { display: flex; justify-content: center; align-items: center; height: 450px; overflow: hidden; video { height: 100%; } } .liveLabel { position: absolute; color: #fff; display: flex; left: 50%; top: 30px; transform: translateX(-50%); .red { padding: 5px 10px; background-color: red; font-weight: 900; } .counter { padding: 5px 10px; background-color: rgba(black, 0.6); } } </style>
表情符號功能
接下來我們要來做表情符號清單與點擊表情符號後的效果。
- 表情符號清單:新增一個變數記錄所有表情符號,結合 split 就能將表情字串轉換成陣列,調整樣式後,將選單 #emojiToolBar 放置在畫面的右下方。
- 表情符號被點擊後動畫效果:清單中的表情符號被點擊後,使用 css 的類別選擇器 :active 來改變 transition 做為被點擊的動態回饋。我們也在清單中的每顆表情符號使用 vue 的語法 @click,當表情符號有點擊事件時,會觸發 addEmoji 函式,同時將被點擊的表情做為參數傳入函式中。
- 記錄有哪些表情符號被點擊:當使用者點擊表情符號後,我們需要記錄有什麼表情符號被觸發,才有辦法去跑對應的動畫,所以在 data 中我們新增一個變數 currentEmojiList ,當清單中的符號被按壓後,會將新的表情符號 push 到陣列裡。
- nextTick 確保資料已更新(延伸參考資料):因為 vue 不是即時更新,資料更新和畫面更新有時間差,所以在更新資料後,馬上去抓新的 dom 會失敗,改使用 nextTick 確定資料更新完畢才跑後續的程式碼。
- tweenMax 初始化設定:使用了 gsap.set() 這個 api,可以針對準備進場的動畫做初始設定,老闆希望表情剛進場時能從小變到大,所以我們在 set 中新增一個 scale: 0.2。
- 表情符號進出場動畫:期望的動畫流程為,按壓表情符號後,先往上飄並慢慢放大到定點,往左飄變小並離場,因為每個表情符號都要兩段式的動畫,這時就可以使用前面提到的 TimelineMax 來達成效果。若是有超出畫面則被隱藏,只要透過 css 去對 #app 做 overflow: hidden 即可。
- 加上隨機數值:完成前面幾點,目前的動畫會有點死板,為了讓動畫更自然,我們讓每個表情起始點不同,上移的距離也不同,製造出交錯的表情符號動畫。分別在 set 內新增一個 x 的值,隨機從0~-100 中挑一個數並加上20;也讓每個表情符號上移的 y 位置不同 ,所以在第一段動畫的終點,讓 y 的值組合不同的 random數。
- 時間函數:大致功能都完成後,希望兩段動畫能再自然一點,所以為兩段動畫都加上速度曲線的值 ease,大家也可以參考相關文件,動手試試不同種的速度效果。
<template lang="pug"> #app ... .contentArea ul.floatingEmojiList li.floatingEmoji( v-for="(emoji, emojiId) in currentEmojiList", :class="`emoji_${emojiId}`" ) {{ emoji }} ul#emojiToolBar li.emojiBtn(v-for="emoji in emojis", @click="addEmoji(emoji)") {{ emoji }} </template> <script> import { TweenMax, TimelineMax, Power0, Power1, Power4 } from "gsap"; const emojiList = "👍,🎉,😂,😯,😢,😡"; export default { name: "App", data() { return { time: 0, emojis: emojiList.split(","), currentEmojiList: [], }; }, computed: { ... }, mounted() { ... }, methods: { addEmoji(emoji) { this.currentEmojiList.push(emoji); let tl = new TimelineMax(); this.$nextTick(() => { let _id = `.emoji_${this.currentEmojiList.length - 1}`; tl.set(_id, { scale: 0.2, x: Math.random() * -100 + 20, }) .to(_id, 1, { y: -200 + Math.random() * -100, scale: 1, ease: Power4.easeOut, }) .to(_id, 3, { x: -500, scale: 0.6, ease: Power1.easeIn, }); }); }, }, }; </script> <style> ... #app { ... overflow: hidden; } ... #emojiToolBar { position: absolute; right: 0; bottom: 0; margin: 0; display: flex; list-style: none; .emojiBtn { font-size: 40px; width: 50px; cursor: pointer; transition: 0.5s; &:active { transition: 0s; transform: scale(0.8); } } } .contentArea { position: relative; } .floatingEmojiList { list-style: none; .floatingEmoji { position: absolute; right: 50px; top: 50px; font-size: 50px; } } </style>
改使用 transition 元件製作表情符號動畫
接下來我們來使用 vue 中 transition-group 元件改寫表情符號進場的過程。vue 提供了 transition 與 transition-group 兩種元件,讓元件在特定的時間點觸發指定的 function 或加上特定的 class 名稱(詳細請參考延伸資料)。transition 與 transition-group 的差別在於,如果只有一個元件會改變使用前者,這個專案是用在由 for 產出的 li 元件們上,所以使用後者。接著就可以把原本在 addEmoji 裡的程式碼搬到 enter 中。
此時,也可以拔掉 nextTick ,因為在 transition-group 上的屬性 v-on:enter 會在確定資料更新才觸發進場,就不用再使用 nextTick 去監聽元件是否生成。要注意的是,如果有使用 v-for ,記得要補上 key 值。
<template lang="pug"> #app ... .contentArea ul.floatingEmojiList transition-group(v-on:enter="enter") //子元件進場時會觸發 enter 函式 li.floatingEmoji( v-for="(emoji, emojiId) in currentEmojiList", :key="emojiId", // 補上 key :class="`emoji_${emojiId}`" ) {{ emoji }} ul#emojiToolBar li.emojiBtn(v-for="emoji in emojis", @click="addEmoji(emoji)") {{ emoji }} </template> <script> import { TweenMax, TimelineMax, Power0, Power1, Power4 } from "gsap"; const emojiList = "👍,🎉,😂,😯,😢,😡"; export default { ... mounted() { ... }, methods: { enter(el) { // 動畫進場時觸發的動畫 let tl = new TimelineMax(); tl.set(el, { scale: 0.2, x: Math.random() * -100 + 20, }) .to(el, 1, { y: -200 + Math.random() * -100, scale: 1, ease: Power4.easeOut, }) .to(el, 3, { x: -500, scale: 0.8, ease: Power1.easeIn, }); }, addEmoji(emoji) { // 將動畫內容搬到 enter 函式中 this.currentEmojiList.push(emoji); }, }, }; </script>
留言區塊
製作送出留言的功能,分別有以下項目需要完成:
- 準備假資料:先準備單筆資料的格式,分別有頭像顏色、發言人、內容。
- 輸入框及送出按鈕:這次只是模擬訊息送出的狀態,機制會是使用者輸入留言,成功送出訊息時,將這個訊息加到 comments 中,並將輸入框清空。若是輸入框為空的,則使用預設的內容送出。
- 預設訊息轉成 json 字串格式再轉回來:要多做這層處理,是因為預設留言 message 是物件,直接賦值的話會是傳參考,需要透過這種方式,創造一個全新的物件。
- 調整表情符號 bar 樣式:將表情工具的寬度改為 100%,加上透明背景。
<template lang="pug"> #app ... .contentArea input(v-model="message") button(@click="addMessage") Add Comment .comments(v-for="(comment, commentId) in comments", :key="commentId") .head(:style="{ backgroundColor: comment.color }") .content .name {{ comment.name }} .sentence {{ comment.content }} </template> <script> import { TweenMax, TimelineMax, Power0, Power1, Power4 } from "gsap"; const emojiList = "👍,🎉,😂,😯,😢,😡"; let message = { color: "#333", name: "Lorem ipsum", content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", }; export default { name: "App", data() { return { ... comments: [], message: "", }; }, computed: {...}, mounted() {...}, methods: { addMessage() { const newMessage = JSON.parse(JSON.stringify(message)); // 創造一個全新的物件 if (this.message !== "") { newMessage.content = this.message; this.message = ""; } this.comments.push(newMessage); } ... }, }; </script> <style lang="scss"> #emojiToolBar { position: absolute; right: 0; bottom: 0; margin: 0; padding: 5px; display: flex; justify-content: flex-end; list-style: none; width: 100%; background-color: rgba(#fff, 0.8); ... } ... .contentArea { position: relative; list-style: none; padding-left: 0px; margin-left: 10px; .comments { display: flex; list-style: none; padding: 5px; font-size: 15px; .head { width: 40px; height: 40px; border-radius: 50%; margin-top: 10px; margin-right: 20px; margin-left: 10px; flex-shrink: 0; } .content { text-align: left; .name { font-weight: 900; } } } } </style>
新增/刪除訊息
最後我們利用新增訊息功能,來練習 transition,首先因為每筆訊息都是用 v-for 跑出來,所以我們要用 transition-group。
- 使用 name 來幫訊息加上動畫:前面的表情符號我們是用 v-on:enter ,當元件被監聽到加入畫面中時,觸發 enter 函式。這邊改使用 name 來觸發(延伸閱讀了解Transition),動態加上 class , vue 總共提供六個時間點,讓使用者為他們加上進場或離場動畫,同學可以去觀察 vue 在 dom 上做了什麼事。
- 調整對應時間點的動畫樣式:大家可以觀察當我們使用 name 來製作動畫後,vue 會在特定時間幫我們在對應的元件上新增 class。利用這些 class 我們就可以來製作過場動畫。要注意動畫的權重如果太小,有些效果無法順利觸發。
- 刪除訊息:既然完成了新增訊息,刪除訊息也能快速完成,老闆希望保留訊息的完整性,所以這邊調整成,當使用者點擊移除訊息的按鈕,只會在這則訊息的物件上新增一個 delete: true 的值,搭配 v-if 就能將這則訊息隱藏。
<template lang="pug"> #app ... .contentArea input(v-model="message") button(@click="addMessage") Add Comment transition-group(name="fade") // 改使用 name 製作動畫 .comments( v-for="(comment, commentId) in comments", :key="commentId", v-if="comment.delete != true" // 當delete 的值不為 true 時,隱藏訊息 ) .head(:style="{ backgroundColor: comment.color }") .content .name {{ comment.name }} .sentence {{ comment.content }} button(@click="removeComment(comment)") - // 點擊後觸發 removeComment 函式 ul.floatingEmojiList transition-group(v-on:enter="enter") // 當元件進入時,觸發 enter 函式 li.floatingEmoji( v-for="(emoji, emojiId) in currentEmojiList", :key="emojiId", :class="`emoji_${emojiId}`" ) {{ emoji }} ul#emojiToolBar li.emojiBtn(v-for="emoji in emojis", @click="addEmoji(emoji)") {{ emoji }} </template> <script> import { TweenMax, TimelineMax, Power0, Power1, Power4 } from "gsap"; const emojiList = "👍,🎉,😂,😯,😢,😡"; let message = { color: "#333", name: "Lorem ipsum", content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", }; export default { name: "App", data() { return { time: 0, emojis: emojiList.split(","), currentEmojiList: [], comments: [], message: "", }; }, computed: { timeLabel() { let sec = this.time % 60; let min = Math.floor(this.time / 60) % 60; let hour = Math.floor(this.time / 3600) % 60; let pd = (num) => (num + "").padStart(2, "0"); return `${pd(hour)}:${pd(min)}:${pd(sec)}`; }, }, mounted() {...}, methods: { removeComment(comment) { comment.delete = true; }, addMessage() { console.log("hi"); const newMessage = JSON.parse(JSON.stringify(message)); if (this.message !== "") { newMessage.content = this.message; this.message = ""; } this.comments.push(newMessage); }, ... }, }; </script> <style lang="scss"> ... .contentArea { .comments { ... &.fade-enter-active, // 利用 transition name 做進出場動畫 &.fade-leave-active { transition: all 0.5s; } &.fade-enter, &.fade-leave-to { opacity: 0; transform: translateY(10px); } ... } } </style>
老闆來結語
這邊再提供一次範例的成果,讓大家在實作時參考,也帶大家快速回顧一次製作流程:
- 使用 codeSandbox 來開發專案,安裝 vue-cli, gsap 後,整理預設提供的檔案。
- 結合 vue 的 ref, $refs 來取得元件。
- 透過 gsap 中的 tweemMax, timelineMax 來製作一段或多段式的動畫。
- 利用原生 js 的影片串流模擬直播畫面,並加上 live 與時間計數器的效果。
- 了解 vue 提供的 nextTick 能夠確保資料更新後才進行畫面渲染。
- 製作表情符號工具欄,在使用者點擊後,能使用 timelineMax 製作表情符號動畫,結合 random 的 api 讓表情動畫更加自然。
- 使用 vue transition-group 來做為表情符號與新增刪除留言的進出場動畫。
這次利用 fb 的直播畫面做為目標,帶大家練習 gsap 製作動畫的方式,大家也可以挑戰自己,看看線上有哪些產品或網站有使用到動畫,想辦法使用 gsap 來實現,做為刻意練習的目標。萬事起頭難,一個作品不可能一步到位,大家在開發時,可以先將最終目標拆分成不同階段任務,從一開始的雛型慢慢開發出每個區塊,最後組裝在一起,就會十分有成就感啦!
跟著老闆上課去 👉 動態互動網頁程式入門(HTML/CSS/JS) 👉 動畫互動網頁特效入門(JS/CANVAS)
此篇直播筆記由幫手 H 協助整理