前端工程師用HTML多行代碼搞定微信8.0的炸裂特效!C/C++工程師怎麼能輸

微信 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" 則爲表情消息,如今還用不到它。在這個函數中主要作了下面幾件事情:微信

  • 按照消息的 HTML 結構建立一個新的消息元素 msgEle,並追加到消息列表中。
  • 把消息的樣式設置爲我發送的。
  • 內部的元素分別爲頭像和文本消息,使用模板字符串的形式賦值給 msgEle 的 innerHTML 屬性中,並在 <p> 中使用 msg 變量的值。
  • 最後把滾動條滾動到最新的消息處,並清空輸入框中的消息。

這樣就能夠發送普通的文本消息了。前端工程師

發送動畫表情

在發送動畫表情以前,須要先加載動畫表情。在 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();
  });
});

這裏的代碼分別做了下邊這些操做:

  • 遍歷存儲表情信息的對象。
  • 建立一個 lottie 的容器,使用 span 元素,由於 lottie 動畫的播放器須要掛載到一個具體的 html 元素中。
  • 調用 lottie 的 loadAnimation() 加載動畫,它須要傳遞這樣幾個參數:
  • container: 播放器要掛載到的容器。
  • renderer:能夠選擇是使用 svg 仍是 canvas 渲染動畫。
  • loop: 是否循環播放,因爲此處是在表情選擇彈出層中預覽動畫,因此支持循環播放。
  • autoplay:是否自動播放,這裏設置爲了否,後邊讓它在鼠標劃過期再播放動畫。
  • path:動畫 json 文件路徑,直接從對象中獲取。
  • loadAnimation() 會返回 lottie 的實例,把它保存在 player 中。

而後後邊則註冊了幾個事件:

  • 當 lottieEle 也就是表情被點擊時,發送表情消息,給 appendMsg() 的 msg 參數設置爲表情的 key,type 參數設置爲 "sticker"。
  • 當鼠標劃過表情時,開始播放動畫。
  • 當鼠標劃出表情時,中止動畫。

接着給發送表情按鈕添加事件,點擊時,切換表情彈出層的顯示狀態:

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,
  });
}

在這個函數裏主要作了下邊幾項操做:

  • 獲取消息中的 span 元素,它將做爲 lottie 的動畫容器。
  • 設置表情動畫的寬高爲 40px。
  • 使用 lottie 加載動畫,並設置循環播放爲  false,自動播放爲 true,來讓表情發送時自動播放動畫,且只播放一次。

如今能夠發送表情消息了,相關的動畫也會自動播放,接下來看一下怎麼實現炸彈的全屏動畫和對消息元素的晃動效果。

發送帶全屏特效的表情

對於這種帶全屏特效的表情能夠單獨進行判斷,也能夠在保存表情的對象中定義相關的操做,這裏爲了簡單起見,咱們單獨判斷用戶是否發送了炸彈表情,而後施加相應特效。

首先在 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 就是消息元素,以後函數作了下邊的這些操做:

  • 添加全屏動畫元素,設置爲絕對定位,寬度 200px,高度 100px,放在最新消息元素的右下角。
  • 加載 lottie 動畫,不循環、自動播放。
  • 因爲原動畫速度過快,這裏調用 lottie 實例的 setSpeed() 方法,把速度設置爲 0.3 倍速。
  • 以後給 lottie 實例設置事件監聽:"complete",它會在動畫執行完成時觸發,裏邊銷燬了 lottie 實例和全屏動畫元素。

這樣全屏動畫的效果就實現了。接下來看消息晃動的代碼:

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);
    });
}

這個函數的操做是:

  • 使用 reverse()  和 slice() 對最新的 5 條消息進行晃動,也能夠把 5 改大一點,對更多消息進行晃動。
  • 而後在循環中,分別給頭像和消息添加 shake class 執行晃動動畫,這個 class 的內容稍後再介紹。
  • 要注意的是,在添加 shake  class執行動畫前,須要先刪除 shake,由於有的消息可能在以前已經晃動過了,例如當連續發了多個炸彈表情時。後邊在添加 shake class 時,使用 setTimeout() 延遲了 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;
}

這裏把對方發送的消息的軸心設置在左下角,本身發送的消息則設置在了右下角。

本文全部地址:

  • 示例地址:https://codechina.csdn.net/mirrors/zxuqian/html-css-examples
  • 代碼地址:https://codechina.csdn.net/mirrors/zxuqian/html-css-examples/-/tree/master/31-05-wechat-emoji-effect
  • lottie: https://cdnjs.com/libraries/bodymovin ,下載 lottie.min.js
  • 南瓜表情:https://lottiefiles.com/43215-pumpkins-sticker-4
  • 炸彈表情:https://lottiefiles.com/3145-bomb
  • 爆炸動畫:https://lottiefiles.com/9990-explosion
  • Lottie 官網:https://airbnb.io/lottie

總結

如今,這個模擬微信 8.0 動畫表情的功能就實現了。主要就是下邊幾點:

  • 使用 lottie 庫加載並播放動畫。
  • 肯定全屏動畫的位置和播放時間。
  • 消息晃動動畫的 CSS 實現。

那麼問題來了,做爲編程界大佬的C語言可否實現微信對話框爆炸特效呢?這個須要你們一塊兒探索!小編相信C語言的強大,是徹底能夠作到的,甚至更加簡單,期待各位小夥伴一塊兒討論~

若是你對學習編程有興趣,也想有一天別人使用你開發的軟件或小程序、小特效,沒基礎也徹底不用擔憂,由於機會來了,點擊下方的瞭解更多連接,開啓你的編程之旅~

在這裏咱們有什麼?

一、海量學習資源

二、名師一對一指導

三、同行之間的相互切磋

四、外包項目拿到手軟

心動不如行動,趕忙進羣免費領取你的專屬福利吧~

相關文章
相關標籤/搜索