給掘金寫了個有趣又好玩的一鍵三連外掛 | 仿B站效果

語言: CN / TW / HK

theme: fancy

我正在參加「創意開發 投稿大賽」詳情請看:掘金創意開發大賽來了!

先來看看b站的一鍵三連是什麼效果:

2022-08-09 13.51.36.gif

不難觀察出以下幾個特點:

  1. 長按點贊出現抖動動畫
  2. 長按點贊時關聯按鈕會有圓環進度條效果
  3. 長按超過一段時間後放開則一次實現三個動作並且有個綻放特效

接下來我們要做的就是逐步實現這些步驟,如何開始呢?這就需要介紹今天的主角:谷歌擴充套件外掛。

建立一個Chrome外掛

Chrome外掛是一個用Web技術開發、用來增強瀏覽器功能的軟體,它其實就是一個由HTML、CSS、JS、圖片等資源組成的一個.crx字尾的壓縮包。可以通過 chrome-plugin-demo 這個專案瞭解更多,這裡我們直接講如何使用:

首先在谷歌瀏覽器直接開啟地址 chrome://extensions/ 進入擴充套件程式,並開啟右上角開發者模式,這時就可以載入我們的外掛了:

image.png

展程式會以 manifest.json 這個檔案來識別並載入外掛:

```json { "manifest_version": 2, "name": "掘金一鍵三連小助手", "version": "1.0", "description": "通過Chrome外掛實現的一鍵三連效果,長按點贊3秒即可點贊+收藏+關注", "author": "ShawnPhang", "icons": { "48": "icon.png", "128": "icon.png" }, "page_action": { "default_icon": "icon.png", "default_title": "我是pageAction", "default_popup": "popup.html" }, "content_scripts": [ { "matches": ["https://juejin.cn/*"], "js": ["mojs.js", "inject.js"], "css": ["like.css"] } ], "background": { "scripts": ["background.js"] }, "web_accessible_resources": [] }

```

| 載入後效果 | 狀態列效果 | | --- | --- | | image.png | image.png |

在這個json檔案中最主要看 content_scripts 這段配置,它表示了外掛會向網頁注入的JS檔案和CSS檔案,前面說了谷歌外掛既是由一系列網頁檔案構成的,那麼接下來就可以正式開始我們的效果實現了~

長按抖動

這是最容易實現的一個效果了,這裡我定義了一個 shaking 的類,重複執行一段css動畫,主要就是利用 translate 屬性重新定位元素,就可以做到抖動的效果,下面的css也是非常隨意寫的,效果還可以。雖然只是上下左右位移卻使用了 translate3d,是為了觸發css的3d加速效能會更好。

``` .article-suspended-panel > .shaking { animation: shake 350ms linear; animation-iteration-count: infinite; animation-direction: reverse; }

@keyframes shake { 10%, 90% { transform: translate3d(-1px, 0, 0); } 15%, 85% { transform: translate3d(0, -1px, 0); } 20%, 80% { transform: translate3d(+2px, 0, 0); } 25%, 75% { transform: translate3d(0, +2px, 0); } 30%, 70% { transform: translate3d(-2.5px, 0, 0); } 35%, 65% { transform: translate3d(0, -2.5px, 0); } 2.50%, 60% { transform: translate3d(+2.5px, 0, 0); } 2.55%, 55% { transform: translate3d(0, +2.5px, 0); } 50% { transform: translate3d(-2.5px, -2.5px, 0); } } ```

接下來在 inject.js 中只需要捕獲文章頁面點贊按鈕,為其新增 mousedown 的監聽事件,在點下滑鼠的時候動態新增上 shaking 這個class,動畫就開始執行了:

```js const likeBtn = document.querySelector('.article-suspended-panel .panel-btn')

likeBtn?.addEventListener('mousedown', () => { if (likeBtn.className.includes('active')) { return } likeBtn.classList.add('shaking') }) ```

2022-08-09 22.12.14.gif

圓環進度條

這個效果開始有點難度了,需要分兩個Div來畫,我們都知道一個完整的圓是這樣:

css .circle { width: 4rem; height: 4rem; border: 2px solid red; border-radius: 50%; box-sizing: border-box; }

image.png

這時我們先把紅色邊改為透明,然後只顯示其中兩條,並旋轉一個角度,這就得到了半圓效果: css .circle { .... border: 2px solid transparent; border-top: 2px solid #1e80ff; border-right: 2px solid #1e80ff; transform: rotate(-135deg); }

image.png

現在以這個半圓我們先來繪製右半部分的圓環,在這個 circle 元素父級新增一個外層元素,使結構如下:

```html

```

外層的 wrapper 高度和圓環高度一致,寬度則為一半,絕對定位到右邊,此時效果是這樣的:

css .wrapper { width: 2rem; height: 4rem; position: absolute; right: 0; background: yellow; }

image.png

這時我們先寫個css動畫讓 circle 轉起來:

css .circle .rightcircle { right: 0; animation: circle 3s linear infinite; } @keyframes circle { 0% { transform: rotate(-135deg); } 50%, 100% { transform: rotate(45deg); } }

2022-08-09 22.42.10.gif

此時黃色區域為圓環的父級元素,如果我們將該區域視為可視區,那麼只需要設定溢位隱藏:

css .wrapper { ..... overflow: hidden; } 效果就出來了:

2022-08-09 22.42.36.gif

同樣的方法繪製左半圓,疊加在一塊就形成了環形進度條動畫,核心在於兩個Div的動畫執行時間是一致的,也就是完整跑完一個360°的旋轉週期,只不過各自都有一半被遮住,從而形成了最終效果,下面有請碼上掘金為我們演示完整程式碼:

程式碼片段

回到我們剛剛外掛中,在點贊按鈕點下的事件中我們需要批量新增上面這段DOM到相應的操作按鈕中,然後絕對定位在左上角(0,0)處即可,檢視網頁原始碼可知按鈕寬高為 4rem,顏色我們則取全域性變數中的藍色 var(--juejin-brand-1-normal),將相關CSS寫到 like.css 檔案中後,JS中定義一個建立DOM的函式:

js function createCircle() { const fragment = document.createElement('div') fragment.classList.add('circle_process') fragment.innerHTML = `<div class="wrapper right"> <div class="circle rightcircle"></div> </div> <div class="wrapper left"> <div class="circle leftcircle"></div> </div>` return fragment } 因為這段結構還是有點多的,就不一一使用Element片段去建立了,建立完最外層的Div之後直接使用innerHtml寫入兩個半圓的結構,函式返回這個DOM片段。

下面就是在點選事件中處理相關動作,用時間戳判斷長按時間是否持續3秒,用一個數組儲存好建立的片段,滑鼠擡起時迴圈呼叫其.remove()方法來移除DOM,b站的效果當然更加複雜,在這裡並不是直接終止動畫而是逆向執行動畫,由於我們是使用css實現的,就折騰不了這種細節了,直接上程式碼:

```js let doms = [] let timeStamp = 0

const likeBtn = document.querySelector('.article-suspended-panel .panel-btn') likeBtn?.addEventListener('mousedown', () => { if (likeBtn.className.includes('active')) { return } timeStamp = new Date().getTime() / 1000 likeBtn.classList.add('shaking') doms.push(createCircle(), createCircle()) document.getElementsByClassName('panel-btn')[0].appendChild(doms[0]) document.getElementsByClassName('panel-btn')[2].appendChild(doms[1]) }) likeBtn?.addEventListener('mouseup', () => { likeBtn.click() const now = new Date().getTime() / 1000 const pass = now - timeStamp > 2.9 likeBtn.classList.remove('shaking') // 移除震動 for (const iterator of doms) { // 移除圓環 iterator.remove() } if (likeBtn.className.includes('active')) { return } if (pass) { // TODO: follow and collect btn action } }) ```

2022-08-09 23.11.58.gif

粒子綻放效果

這個效果用css實現難度更高了,剛好我今天在掘金看了一篇文章,介紹了一個輕量級動畫庫 mojs,裡面的爆裂(Burst)效果就很適合這個場景,於是把umd檔案下載來引入頁面中。

inject.js 中定義一個函式執行來動畫: js function play(parent, cb) { new window.mojs.Burst({ // 爆裂範圍 {從多大 : 到多大} radius: { 0: 50 }, // 動畫掛載的父元素, 如果不填預設掛載到 <body> parent, // 動畫延遲的貝塞爾曲線函式 easing: mojs.easing.bezier(0.1, 1, 0.3, 1), // 動畫延遲時間 duration: 1500, // 在動畫動之前等待的時間 (這裡一般設定150ms方便減少低端機型可能會存在的卡頓) delay: 300, // 擴散的粒子配置 children: { duration: 750, // 粒子大小變換 {從多大 : 到多大} // rand(from, to) rand函式可以幫我們隨機出一個區間的值 radius: { 0: 'rand(5, 25)' }, // 形狀選擇, 這裡我們選擇了 “圓形” shape: 'circle', // 粒子可選的填充色 fill: ['#1abc9c', '#2ecc71', '#00cec9', '#3498db', '#9b59b6', '#fdcb6e', '#f1c40f', '#e67e22', '#e74c3c', '#e84393'], }, // 透明度 opacity: 0.6, // 生成的粒子數量 count: 12, onStart() { // 動畫觸發前的鉤子函式 }, onComplete() { // 動畫完成後的鉤子函式 cb && cb() }, }).play() } 接著在 mouseup 事件中呼叫:

js .......... likeBtn?.addEventListener('mouseup', () => { ............. if (pass) { play(likeBtn, () => { // 這裡只有兩個按鈕所以直接用回撥函式來排列動畫了 play(document.getElementsByClassName('panel-btn')[2]) }) } })

完整效果展示(後面長按時間調整到2s):

2022-08-10 17.22.52.gif

外掛完整程式碼地址:chromePlugin-juejin-oneThree