记一次 chrome 插件编写经历,让任意页面元素脱落下来

2,455 阅读3分钟

背景

某天晚上浏览掘金时无意发现 炸掘金 的创意游戏,虎躯一震,想到自己以前很想做一个将页面元素分离的特效,但因为工作忙(lan)没有实践起来,现在因为疫情居家办公,时间比以前多了 可以大胆摸鱼,为啥不趁这个机会实现一下呢?

查阅文档

那咱撸起袖子就是干,大致想法是通过 chrome 插件实现页面元素脱落的效果,所以先查阅 谷歌插件开发文档 (需要梯子),逐渐厘清了几个要点

首先是 manifest 文件

每个插件都需要有 manifest.json 文件,提供关于插件的一些重要信息

manifest 字段一览表

其中必填的字段:

字段名解释
manifest_version描述 manifest 文件的版本
name插件名称
version插件的版本

推荐填写的字段:

字段名解释
description插件简介
icons插件图标
action使用 chrome.action API 控制浏览器工具栏中插件图标的行为

其它看起来比较厉害的字段:

字段名解释
background注册 service_worker 的地方
commands给插件添加快捷键指令
content_scripts在当前网页环境中运行的文件
permissions声明在运行时需要的插件权限

其次是插件提供的能力

写插件前咱得知道插件能提供啥炫酷功能嘛不是,开发文档里有两个 nice 的归纳总结:

然后根据自己想实现的功能开整

编写逻辑

咱是想点击工具栏的插件图标后,页面元素开始往下掉落,所以需要查查 API 文档,看怎么监听插件图标的点击事件并操作页面内容

很快啊,我的目光聚焦在这两个 API 上:

  • chrome.action.onClicked 点击插件图标
  • chrome.scripting 在不同页面中执行脚本

现在万事俱备,只差代码了,咱先组织下 manifest.json 文件,汇总下插件的信息:

    // 这里只列出运行逻辑相关的字段哈
    "background": { // 还记得之前在表格里介绍的 background 字段吗 嘿嘿
        "service_worker": "background.js" // 文件路径为插件根目录下哦
    },
    "permissions": ["activeTab", "scripting"] // 调用 chrome API 中需要声明的权限信息

是滴,对于这个插件而言,最关键就上面这两个字段

让我们 look look background.js 里写的是啥:

    // 监听插件图标的点击事件
    chrome.action.onClicked.addListener(tab => {
        // 在当前页面执行 script.js
        chrome.scripting.executeScript({
            target: { tabId: tab.id },
            files: ['script.js']
        })
    })

接下来非常丝滑地过渡到 script.js,这里是实现页面元素掉落的逻辑:

    /**
      大致思路为:
        1. 遍历页面元素,添加特定 class
        2. 在 head 插入特定 class 的样式声明,让页面按照预期动起来
        3. 动画结束后移除 head 的特定 class 声明
    */
    
    // 寻找最底层节点并执行特定逻辑
    function visit(node) {
        if (node.children.length) {
            for (const el of node.children) {
                visit(el)
            }
        } else {
            // 执行逻辑,如添加特定 class 等
            node.classList.add('drop-off-extension')
        }
    }
    
    // 添加特定样式声明
    function addDropOffStyle() {
        const style = document.createElement('style')
        // 添加特定 class 的具体样式声明
        style.innerHtml = '.drop-off-extension {transform-origin: top left; animation: hinge 2s; @keyframes hinge { 0% {animation-timing-function: ease-in-out;} 20%,60% {transform: rotate3d(0, 0, 1, 80deg);animation-timing-function: ease-in-out;} 40%,80% {transform: rotate3d(0, 0, 1, 60deg);animation-timing-function: ease-in-out;opacity: 1;} 100% {transform: translate3d(0, 700px, 0);opacity: 0;}}}'
        document.head.appendChild(style)
        // 动画执行结束后移除特定 class 样式声明
        setTimeout(() => {
            document.head.removeChild(style)
        }, 6000)
    }
    
    visit(document.body) // 从 body 开始遍历
    addDropOffStyle()

最后就是不断地调试和优化效果了 :)

最终效果展示

drop-off-demo-2.gif

完美~