先来看看b站的一键三连是什么效果:
不难观察出以下几个特点:
- 长按点赞出现抖动动画
- 长按点赞时关联按钮会有圆环进度条效果
- 长按超过一段时间后放开则一次实现三个动作并且有个绽放特效
接下来我们要做的就是逐步实现这些步骤,如何开始呢?这就需要介绍今天的主角:谷歌扩展插件。
创建一个Chrome插件
Chrome插件是一个用Web技术开发、用来增强浏览器功能的软件,它其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包。可以通过 chrome-plugin-demo 这个项目了解更多,这里我们直接讲如何使用:
首先在谷歌浏览器直接打开地址 chrome://extensions/ 进入扩展程序,并打开右上角开发者模式,这时就可以加载我们的插件了:
展程序会以 manifest.json
这个文件来识别并加载插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| { "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": [] }
|
加载后效果 |
状态栏效果 |
|
|
在这个json文件中最主要看 content_scripts
这段配置,它表示了插件会向网页注入的JS文件和CSS文件,前面说了谷歌插件既是由一系列网页文件构成的,那么接下来就可以正式开始我们的效果实现了~
长按抖动
这是最容易实现的一个效果了,这里我定义了一个 shaking
的类,重复执行一段css动画,主要就是利用 translate
属性重新定位元素,就可以做到抖动的效果,下面的css也是非常随意写的,效果还可以。虽然只是上下左右位移却使用了 translate3d
,是为了触发css的3d加速性能会更好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| .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,动画就开始执行了:
1 2 3 4 5 6 7 8
| const likeBtn = document.querySelector('.article-suspended-panel .panel-btn')
likeBtn?.addEventListener('mousedown', () => { if (likeBtn.className.includes('active')) { return } likeBtn.classList.add('shaking') })
|
圆环进度条
这个效果开始有点难度了,需要分两个Div来画,我们都知道一个完整的圆是这样:
1 2 3 4 5 6 7
| .circle { width: 4rem; height: 4rem; border: 2px solid red; border-radius: 50%; box-sizing: border-box; }
|
这时我们先把红色边改为透明,然后只显示其中两条,并旋转一个角度,这就得到了半圆效果:
1 2 3 4 5 6 7
| .circle { .... border: 2px solid transparent; border-top: 2px solid #1e80ff; border-right: 2px solid #1e80ff; transform: rotate(-135deg); }
|
现在以这个半圆我们先来绘制右半部分的圆环,在这个 circle
元素父级添加一个外层元素,使结构如下:
1 2 3
| <div class="wrapper right"> <div class="circle"></div> </div>
|
外层的 wrapper
高度和圆环高度一致,宽度则为一半,绝对定位到右边,此时效果是这样的:
1 2 3 4 5 6 7
| .wrapper { width: 2rem; height: 4rem; position: absolute; right: 0; background: yellow; }
|
这时我们先写个css动画让 circle
转起来:
1 2 3 4 5 6 7 8 9 10 11 12 13
| .circle .rightcircle { right: 0; animation: circle 3s linear infinite; } @keyframes circle { 0% { transform: rotate(-135deg); } 50%, 100% { transform: rotate(45deg); } }
|
此时黄色区域为圆环的父级元素,如果我们将该区域视为可视区,那么只需要设置溢出隐藏:
1 2 3 4
| .wrapper { ..... overflow: hidden; }
|
效果就出来了:
同样的方法绘制左半圆,叠加在一块就形成了环形进度条动画,核心在于两个Div的动画执行时间是一致的,也就是完整跑完一个360°的旋转周期,只不过各自都有一半被遮住,从而形成了最终效果,下面有请码上掘金为我们演示完整代码:
代码片段
回到我们刚刚插件中,在点赞按钮点下的事件中我们需要批量添加上面这段DOM
到相应的操作按钮中,然后绝对定位在左上角(0,0)处即可,查看网页源代码可知按钮宽高为 4rem
,颜色我们则取全局变量中的蓝色 var(--juejin-brand-1-normal)
,将相关CSS写到 like.css
文件中后,JS中定义一个创建DOM
的函数:
1 2 3 4 5 6 7 8 9 10 11
| 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实现的,就折腾不了这种细节了,直接上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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) { } })
|
粒子绽放效果
这个效果用css实现难度更高了,刚好我今天在掘金看了一篇文章,介绍了一个轻量级动画库 mojs,里面的爆裂(Burst)效果就很适合这个场景,于是把umd文件下载来引入页面中。
inject.js
中定义一个函数执行来动画:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| function play(parent, cb) { new window.mojs.Burst({ radius: { 0: 50 }, parent, easing: mojs.easing.bezier(0.1, 1, 0.3, 1), duration: 1500, delay: 300, children: { duration: 750, 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
事件中调用:
1 2 3 4 5 6 7 8 9
| .......... likeBtn?.addEventListener('mouseup', () => { ............. if (pass) { play(likeBtn, () => { play(document.getElementsByClassName('panel-btn')[2]) }) } })
|
完整效果展示(后面长按时间调整到2s):
插件完整代码地址:chromePlugin-juejin-oneThree