给掘金写了个有趣又好玩的一键三连插件 | 仿B站效果

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

先来看看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 这个文件来识别并加载插件:

{
  "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.pngimage.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,动画就开始执行了:

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来画,我们都知道一个完整的圆是这样:

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

image.png

这时我们先把红色边改为透明,然后只显示其中两条,并旋转一个角度,这就得到了半圆效果:

.circle {
    ....
    border: 2px solid transparent;
    border-top: 2px solid #1e80ff;
    border-right: 2px solid #1e80ff;
    transform: rotate(-135deg);
}

image.png

现在以这个半圆我们先来绘制右半部分的圆环,在这个 circle 元素父级添加一个外层元素,使结构如下:

<div class="wrapper right">
    <div class="circle"></div>
</div>

外层的 wrapper 高度和圆环高度一致,宽度则为一半,绝对定位到右边,此时效果是这样的:

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

image.png

这时我们先写个css动画让 circle 转起来:

  .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

此时黄色区域为圆环的父级元素,如果我们将该区域视为可视区,那么只需要设置溢出隐藏:

.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的函数:

  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实现的,就折腾不了这种细节了,直接上代码:

  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 中定义一个函数执行来动画:

  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 事件中调用:

..........
  likeBtn?.addEventListener('mouseup', () => {
    .............
    if (pass) {
      play(likeBtn, () => { // 这里只有两个按钮所以直接用回调函数来排列动画了
        play(document.getElementsByClassName('panel-btn')[2])
      })
    }
  })

完整效果展示(后面长按时间调整到2s):

2022-08-10 17.22.52.gif

插件完整代码地址:chromePlugin-juejin-oneThree

posted @ 2022-08-10 11:49  茶无味的一天  阅读(33)  评论(0编辑  收藏  举报  来源