給掘金寫了個有趣又好玩的一鍵三連插件 | 仿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": ["http://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