微信 8.0 更新的一大特點就是支持動畫表情,若是發送的消息只有一個內置的表情圖標,這個表情會有一段簡單的動畫,一些特殊的表情還有全屏特效,例如煙花表情有全屏放煙花的特效,炸彈表情有爆炸動畫而且消息和頭像也會隨之震動。css
近日,前端工程師華峯用300行代碼實現微信表情包炸裂的特效,一塊兒來看看作出來的效果吧:html
據他描述:項目的核心是使用到了 lottie 動畫庫。前端
lottie 是 Airbnb 出品的、全平臺(Web、Android、IOS、React Native)的動畫庫,它的特色在於可以直接播放使用 Adobe After Effects 製做的動畫。設計師在 After Effects 中,利用 Bodymovin 插件把動畫導出爲 JSON 格式以後,開發者就可以經過相應平臺的 SDK 進行播放。編程
發送普通消息時,用戶在輸入框輸入完消息以後,點擊發送,就會把該條消息追加到消息列表中,並清空輸入框中的內容。那麼這裏先給發送按鈕添加點擊事件:json
sendBtn.addEventListener("click", () => { const msg = msgInputEle.value; if (msg) { appendMsg(msg); } });
在事件處理函數中:canvas
來看一下 appendMsg() 函數的代碼:小程序
function appendMsg(msg, type) { // 建立消息元素 const msgEle = panelEle.appendChild(document.createElement("div")); msgEle.classList.add("message", "mine"); // 設置爲「我「發送的樣式 msgEle.innerHTML = ` <img class="avatar" src="./me.png" alt="" /> <p><span>${msg}</span></p> `; // 滾動到最新消息 panelEle.scrollTop = panelEle.scrollHeight; msgInputEle.value = ""; }
函數接收兩個參數,msg 和 type,分別是要追加的消息內容和類型,type 爲可選的,不傳則認爲是普通文本消息,若是傳遞了 "stickers" 則爲表情消息,如今還用不到它。在這個函數中主要作了下面幾件事情:微信
這樣就能夠發送普通的文本消息了。前端工程師
在發送動畫表情以前,須要先加載動畫表情。在 index.js 的最上方先定義表情名稱和表情動畫文件路徑的鍵值對信息:app
const stickers = { bomb: { path: "./3145-bomb.json", }, pumpkin: { path: "./43215-pumpkins-sticker-4.json", }, };
咱們會根據 bomb 、 pumkin 這樣的 key 來找到對應的動畫路徑。接着初始化彈出層中的表情以供用戶選擇:
// 初始化表情面板,也能夠在表情選擇窗彈出時再初始化 Object.keys(stickers).forEach((key) => { const lottieEle = stickersEle.appendChild(document.createElement("span")); // 對每一個表情建立 lottie 播放器 const player = lottie.loadAnimation({ container: lottieEle, renderer: "svg", loop: true, autoplay: false, path: stickers[key].path, }); // 當選擇表情時,發送消息,並設置類型爲 sticker 表情消息 lottieEle.addEventListener("click", () => { appendMsg(key, "sticker"); }); // 當鼠標劃過期,播放動畫預覽 lottieEle.addEventListener("mouseover", () => { player.play(); }); // 當鼠標劃過期,中止動畫預覽 lottieEle.addEventListener("mouseleave", () => { player.stop(); }); });
這裏的代碼分別做了下邊這些操做:
而後後邊則註冊了幾個事件:
接着給發送表情按鈕添加事件,點擊時,切換表情彈出層的顯示狀態:
chooseStickerBtn.addEventListener("click", () => { stickersEle.classList.toggle("show"); });
這時點擊發送表情按鈕就能夠看到表情選擇彈出層了。如今還不能發送表情,由於還沒在 appendMsg() 函數中處理,如今來修改一下它裏邊的代碼。首先判斷:若是是表情消息,則不在消息中的 <p> 元素裏添加任何信息:
function appendMsg(msg, type) { // ... msgEle.innerHTML = ` <img class="avatar" src="./me.png" alt="" /> <p><span>${type === "sticker" ? "" : msg}</span></p> `; }
而後在它的下方,調用 playSticker() 函數來播放動畫:
// 處理表情消息,播放相關動畫 if (type === "sticker") { playSticker(msg, msgEle); }
playSticker() 函數接收兩個參數,一個是表情的 key,一個是消息元素。此時的 msg 變量的內容就是在 lottieEle 點擊事件中傳遞過來的表情 key。函數中的代碼以下:
function playSticker(key, msgEle) { // 表情消息,建立 lottie 動畫 const lottieEle = msgEle.querySelector("span"); lottieEle.style.width = "40px"; lottieEle.style.height = "40px"; lottie.loadAnimation({ container: lottieEle, renderer: "svg", loop: false, autoplay: true, path: stickers[key].path, }); }
在這個函數裏主要作了下邊幾項操做:
如今能夠發送表情消息了,相關的動畫也會自動播放,接下來看一下怎麼實現炸彈的全屏動畫和對消息元素的晃動效果。
發送帶全屏特效的表情
對於這種帶全屏特效的表情能夠單獨進行判斷,也能夠在保存表情的對象中定義相關的操做,這裏爲了簡單起見,咱們單獨判斷用戶是否發送了炸彈表情,而後施加相應特效。
首先在 appendMsg() 函數裏,進行判斷,若是發送的消息是表情消息,且表情爲炸彈,則播放全屏動畫並晃動消息:
function appendMsg(msg, type) { if (type === "sticker") { playSticker(msg, msgEle); if (msg === "bomb") { // 播放爆炸動畫 setTimeout(() => { playExplosion(msgEle); }, 800); // 晃動消息列表 shakeMessages(); } } }
這裏爆炸全屏動畫延遲了 800 毫秒以後再執行,目的是在炸彈表情播放到合適的時間時,再播放全屏動畫,播放動畫使用了 playExplosion() 函數,並傳遞了消息元素進去。在爆炸全屏動畫結束以後,調用 shakeMessages() 來晃動消息。這裏先看一下 playExplosion() 函數的代碼:
function playExplosion(anchor) { const explosionAnimeEle = anchor.appendChild(document.createElement("div")); explosionAnimeEle.style.position = "absolute"; explosionAnimeEle.style.width = "200px"; explosionAnimeEle.style.height = "100px"; explosionAnimeEle.style.right = 0; explosionAnimeEle.style.bottom = 0; const explosionPlayer = lottie.loadAnimation({ container: explosionAnimeEle, renderer: "svg", loop: false, autoplay: true, path: "./9990-explosion.json", }); explosionPlayer.setSpeed(0.3); // 播放完成後,銷燬爆炸相關的動畫和元素 explosionPlayer.addEventListener("complete", () => { explosionPlayer.destroy(); explosionAnimeEle.remove(); }); }
playExplosion() 函數接收一個 anchor 錨點,就是說基於哪一個位置開始播放全屏動畫,因爲示例中的動畫畫幅比較小,因此把它固定在了最新發送的消息的下方,這裏爆炸動畫的 anchor 就是消息元素,以後函數作了下邊的這些操做:
這樣全屏動畫的效果就實現了。接下來看消息晃動的代碼:
function shakeMessages() { [...panelEle.children] .reverse() .slice(0, 5) .forEach((messageEle) => { const avatarEle = messageEle.querySelector("img"); const msgContentEle = messageEle.querySelector("p"); avatarEle.classList.remove("shake"); msgContentEle.classList.remove("shake"); setTimeout(() => { avatarEle.classList.add("shake"); msgContentEle.classList.add("shake"); }, 700); }); }
這個函數的操做是:
接下來看一下 shake class 的定義,在 style.css 中添加下方代碼:
.shake { animation: shake 0.8s ease-in-out; } @keyframes shake { from { transform: translate3d(0, 0px, 0px); } 10% { transform: translate3d(6px, -6px, 0px); } 20% { transform: translate3d(-5px, 5px, 0px); } 30% { transform: translate3d(4px, -4px, 0px); } 35% { transform: translate3d(-3px, 3px, 0px); } 39% { transform: translate3d(2px, -2px, 0px); } 41% { transform: translate3d(-1px, 1px, 0px); } 42% { transform: translate3d(0px, 0px, 0px) rotate(20deg); } 52% { transform: rotate(-15deg); } 60% { transform: rotate(8deg); } 65% { transform: rotate(-3deg); } 67% { transform: rotate(1deg); } 70% { transform: rotate(0deg); } to { transform: translate3d(0px, 0px, 0px) rotate(0); } }
.shake 中使用了 shake keyframes 定義的動畫,執行時間爲 0.8s,動畫執行函數爲 ease-in-out。Keyframes 裏的代碼比較多,可是都是很簡單的,就是模擬了爆炸時的效果,移動 x 軸和 y 軸的偏移,每次的偏移幅度愈來愈小,而且愈來愈快,能夠看到百分比的間隔愈來愈小。在動畫進行到 42% 的時候,加了一些旋轉動畫,這樣就有了落地時的震動效果。因爲使用 rotate() 旋轉時的軸心在元素中間,咱們能夠把消息氣泡的軸心修改一下來實現更真實的效果:
.message p { transform-origin: left bottom; } .message.mine p { transform-origin: right bottom; }
這裏把對方發送的消息的軸心設置在左下角,本身發送的消息則設置在了右下角。
本文全部地址:
如今,這個模擬微信 8.0 動畫表情的功能就實現了。主要就是下邊幾點:
那麼問題來了,做爲編程界大佬的C語言可否實現微信對話框爆炸特效呢?這個須要你們一塊兒探索!小編相信C語言的強大,是徹底能夠作到的,甚至更加簡單,期待各位小夥伴一塊兒討論~
若是你對學習編程有興趣,也想有一天別人使用你開發的軟件或小程序、小特效,沒基礎也徹底不用擔憂,由於機會來了,點擊下方的瞭解更多連接,開啓你的編程之旅~
在這裏咱們有什麼?
一、海量學習資源
二、名師一對一指導
三、同行之間的相互切磋
四、外包項目拿到手軟
心動不如行動,趕忙進羣免費領取你的專屬福利吧~