有了这个Chrome插件,就可以在掘金官网愉快地撸猫了!🚀

801 阅读4分钟

一起用代码吸猫!本文正在参与【喵星人征文活动】

前言

Chrome浏览器以其开源、免费、拓展性强等优点越来越受开发者喜爱。据w3counterbacklinko统计,截止到2021年9月,世界范围内Chrome占据了Web浏览器高达63.58%的份额,其次是Safari的17.7%和Firefox的5.8%。Chrome官方扩展在13万个以上。

为了能在掘金官网愉快地撸猫,同时本着学习和开发Chrome插件的目的,折腾了几天的时间,开发了这个Chrome-raising-cat插件。并记录下插件的开发过程,方便后续的交流学习~ 🚀

首先看看效果~

0

当鼠标移动时,猫猫会跟着跑动到鼠标附近。撸起来~

需求

在掘金官网撸猫总共有几个步骤?

  • 首先需要有一只猫
  • 放进掘金官网
  • 撸起来...

实现

Chrome插件开发

项目结构

│  manifest.json // 配置文件
│  README.md
├─assets
│  │  popup.html // 弹窗页面
│  │  style.css  // 样式文件
│  │
│  └─images
│      ├─cat   // 存放图片
│      └─icons // 存放图标
├─js
│      background.js
│      content.js
│      jquery.min.js
│      popup.js
└─_locales // 多语言支持
    └─en
       └─messages.json

配置文件 manifest.json

理解插件的配置文件有助于理解插件的运行原理,并学习插件的开发方法。首先是创建配置文件 manifest.json

touch manifest.json

内容如下:

{
  "name": "__MSG_name__",
  "version": "1.0.0",
  "description": "__MSG_description__",
  "default_locale": "en",
  "permissions": [
    "activeTab",
    "storage",
    "tabs",
    "http://*/*",
    "https://*/*"
  ],
  "web_accessible_resources": [ "assets/*/*.*" ],
  "background": {
    "scripts": ["js/jquery.min.js", "js/background.js"],
    "persistent": false
  },
  "content_scripts": [{
    "css" : ["assets/style.css"],
    "js": [ "js/jquery.min.js", "js/content.js" ],
    "matches": [ "http://*/*", "https://*/*" ]
  }],
  "browser_action": {
    "default_icon": {
      "32": "assets/images/icons/icon32.png",
      "64": "assets/images/icons/icon64.png",
      "128": "assets/images/icons/icon128.png",
      "256": "assets/images/icons/icon256.png"
    },
    "default_title": "__MSG_default_title__",
    "default_popup": "assets/popup.html"
  },
  "icons": {
    "32": "assets/images/icons/icon32.png",
    "64": "assets/images/icons/icon64.png",
    "128": "assets/images/icons/icon128.png",
    "256": "assets/images/icons/icon256.png"
  },
  "manifest_version": 2
}

manifest_version

设置扩展的版本。"manifest_version": 2表示使用Manifest V2。最新的V3版本的语法和API都有了较大的改动。如:

  • 使用service workers 替代background pages
"background": {
  "service_worker": "background.js"
},

background

background设置的脚本运行在浏览器后台中,在每个tab页面中都生效。即使当前标签页关闭了仍会运行,直到所有的视图和通信端口都关闭了。

content_scripts

插件把脚本(content script)插入到满足条件的页面里,作为页面的脚本,可以操作插入的页面中的DOM

name, version, description

设置扩展的名字、版本和描述。

default_locale

设置默认语言。

permissions

设置权限。使用一些 API需要进行授权,如使用chrome.tabs.xxx需要设置tabs

web_accessible_resources

允许扩展外的页面访问的扩展内指定的资源。比如打开的一个网页可以访问到扩展内的图片。

browser_action

可以设置扩展弹窗页的页面内容、图标等。

icons

设置扩展的图标。

background.js 页面

background.js在每个tab页面中都生效。可以在background.js中设置监听,当安装或更新插件时,初始化chrome.storage的值。

class Background {
  constructor() {
  }
  onInit() {
    chrome.storage.local.set({
      is_active: true,
      is_mouse_active: true
    });
  }
  initListener() {
    chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
      if (message.method == 'callbg') {
      }
      sendResponse('get background')
    }.bind(this));
    chrome.runtime.onInstalled.addListener(function (details) {
      if (details.reason == "install") {
        this.onInit()
      } else if (details.reason == "update") {
        this.onInit()
      }
    }.bind(this))
  }
}
$(function () {
  let bg = new Background();
  bg.initListener();
});

弹窗页面 popup.html

设置弹窗页面,这里添加了两个按钮,控制猫猫的显示或隐藏。

1

// popup.html
<style>
  .cat-line{
    display: flex;
    align-items: center;
    justify-content: flex-start;
    min-width: 110px;
    min-height: 30px;
  }
  input{
    margin-left: auto;
  }
</style>
<div class="cat-line">
  <span>Show Cat Nav: </span>
  <input type="checkbox" id="catBox1">
</div>
<div class="cat-line">
  <span>Show Cat Mouse: </span>
  <input type="checkbox" id="catBox2">
</div>
<script src="../js/jquery.min.js"></script>
<script src="../js/popup.js"></script>

弹窗文件 popup.js

定义了Popup类,根据configuration配置,设置弹窗页面中两个checkbox的状态并添加click事件。

// popup.js
class Popup {
  constructor() {
  }
  onInit() {
    chrome.storage.local.get(null, function (items) {
      this.configuration = items;
      this.initBtns();
    }.bind(this));
  }
  initBtns() {
    // 根据configuration设置按钮状态
    if (this.configuration.is_active == false) {
      $('#catBox1').removeAttr("checked");
    } else {
      $('#catBox1').attr("checked", "checked");
    }
    if (this.configuration.is_mouse_active == false) {
      $('#catBox2').removeAttr("checked");
    } else {
      $('#catBox2').attr("checked", "checked");
    }
    
    // 添加按钮事件
    $("#catBox1").on('click', function() {
      this.check()
    }.bind(this));
    $("#catBox2").on('click', function() {
      this.checkMouse()
    }.bind(this));
  }
  // 更新chrome.storage
  checkMouse() {
    if (this.configuration.is_mouse_active == true) {
      this.configuration.is_mouse_active = false
      chrome.storage.local.set({is_mouse_active: false});
    } else {
      this.configuration.is_mouse_active = true
      chrome.storage.local.set({is_mouse_active: true});
    }
  }
  check() {}
}
$(function () {
  let pop = new Popup();
  pop.onInit();
});

内容页面content.js

首先是创建新的页面:

touch content.js

然后往页面中加入内容。

创建一只猫 🧐

代码如下:

class Content {
  constructor() {
  }
  onInitCat() {
    let newCat = {
      itemLeft: chrome.extension.getURL("assets/images/cat/cat3.gif"),
      itemLeftMove: chrome.extension.getURL("assets/images/cat/cat3_move.gif"),
      itemRight: chrome.extension.getURL("assets/images/cat/cat3_r.gif"),
      itemRightMove: chrome.extension.getURL("assets/images/cat/cat3_move_r.gif")
    }
    this.createCat('dearCat', newCat);
  }
  createCat(id, item) {
    // 1. 设置猫猫的属性
    this.itemLeft = item.itemLeft;
    this.itemLeftMove = item.itemLeftMove;
    this.itemRight = item.itemRight;
    this.itemRightMove = item.itemRightMove;
    this.mousePositionX = 0;
    this.mousePositionY = 0;
    this.movespeed = 2000;
    this.lookRight = false;
    this.lookLeft = false;
    this.freeview = true;
    this.goRight = true;
  }
}

放进掘金官网 📝

代码如下:

class Content {
  constructor() {
  }
  onInitCat() {
    let newCat = {
      itemLeft: chrome.extension.getURL("assets/images/cat/cat3.gif"),
      itemLeftMove: chrome.extension.getURL("assets/images/cat/cat3_move.gif"),
      itemRight: chrome.extension.getURL("assets/images/cat/cat3_r.gif"),
      itemRightMove: chrome.extension.getURL("assets/images/cat/cat3_move_r.gif")
    }
    this.createCat('dearCat', newCat);
  }
  createCat(id, item) {
    // 1. 设置猫猫的属性
    this.itemLeft = item.itemLeft;
    this.itemLeftMove = item.itemLeftMove;
    this.itemRight = item.itemRight;
    this.itemRightMove = item.itemRightMove;
    this.mousePositionX = 0;
    this.mousePositionY = 0;
    this.movespeed = 2000;
    this.lookRight = false;
    this.lookLeft = false;
    this.freeview = true;
    this.goRight = true;
    // 2. 添加到页面中
    let img = document.createElement("img");
    img.setAttribute('src', this.itemRightMove);
    img.setAttribute('id', id);
    img.style.width = '10%';
    img.style.position = 'absolute';
    img.style.display = 'block';
    img.style.top = $(document).scrollTop() + $(window).height() / 2 - $(img).height() / 2 + 'px';
    img.style.left = '0';
    img.style.zIndex = 1000;
    document.body.appendChild(img);
    // 3. 设置属性
    $('#dearCat').attr({
      "itemLeft": this.itemLeft,
      "itemLeftMove": this.itemLeftMove,
      "itemRight": this.itemRight,
      "itemRightMove": this.itemRightMove,
      "data-lookRight": this.lookRight,
      "data-lookLeft": this.lookLeft,
      "data-freeview": this.freeview,
      "data-movespeed": this.movespeed,
      "data-goRight": this.goRight
    })
      .bind("contextmenu", function (e) {
        return false;
      });
  }
}
$(function () {
  let c = new Content();
  c.onInitCat();
});

这样就可以在页面中看到内容了:

0

让猫动起来 🚚

首先,给页面监听鼠标的mousemove事件:

onInitCat() {
  $(document).mousemove(function (e) {
    this.initMouse(e)
  }.bind(this))
}
initMouse(e) {
  console.log('hhhh')
}

移动猫猫:

class Content {
  onInitCat() {
    $(document).mousemove(function (e) {
      this.initMouse(e)
    }.bind(this))
  }
  initMouse(e) {
    console.log('hhhh')
    let cat = '#dearCat';
    if ($(cat).length) {
      let DOM = $(cat)
      DOM.stop().animate({left: e.pageX, top: e.pageY}, {
        queue: false,
        duration: 2000,
        easing: "swing",
        start: function () {},
        complete: function () {}
      })
    }
  }
}
$(function () {
  let c = new Content();
  c.onInitCat();
});

效果:

0

不足:

虽然能动起来,但是我们可以看到,鼠标移动了一点距离,就打印了很多次,也就是多次触发了initMouse函数。实际上,我们可以等鼠标停止移动了,再触发initMouse函数。

优化 🚀

防抖

可以用防抖函数优化代码,只有鼠标停止移动了才触发:

constructor() {
  this.timer = null
}
onInitCat() {
  $(document).mousemove(function (e) {
    this._debounce(this.initMouse.bind(this, e))()
  }.bind(this))
}
initMouse(e) {
  console.log('hhhh')
}
_debounce(fn, wait = 500) {
  return function() {
    if(this.timer) {
      clearTimeout(this.timer);
    }
    this.timer = setTimeout(fn, wait);
  }.bind(this)
}

监听storage

监听到chrome.storage的变化时,判断是否显示猫猫。

class Content {
  constructor() {
    this.timer = null
  }
  onInit() {
    // 监听storage
    chrome.storage.onChanged.addListener((changes, namespace) => {
      if (changes.hasOwnProperty('is_mouse_active')) {
        let a = changes['is_mouse_active'].newValue
        if (a) {
          this.onInitCat()
        } else {
          $("#dearCat").remove();
        }
      }
      if (changes.hasOwnProperty('is_active')) {
        let a = changes['is_active'].newValue
        if (a) {
          this.onInitNav()
        } else {
          $("#staticCat").remove();
        }
      }
    });
    
    // 根据is_active 和 is_mouse_active,判断是否显示按钮
    chrome.storage.local.get(
      null,
      function (items) {
        if (items.is_active == true) {
          this.onInitNav();
        }
        if (items.is_mouse_active == true) {
          this.onInitCat();
        }
      }.bind(this)
    );
  }
  // ...
}

其他

可以进一步优化,让猫猫支持状态切换,撸起来更顺手... =_=

完整代码

class Content {
  constructor() {
    this.timer = null
  }
  
  onInit() {
    chrome.storage.onChanged.addListener((changes, namespace) => {
      if (changes.hasOwnProperty('is_mouse_active')) {
        let a = changes['is_mouse_active'].newValue
        if (a) {
          this.onInitCat()
        } else {
          $("#dearCat").remove();
        }
      }
      if (changes.hasOwnProperty('is_active')) {
        let a = changes['is_active'].newValue
        if (a) {
          this.onInitNav()
        } else {
          $("#staticCat").remove();
        }
      }
    });
    
    chrome.storage.local.get(
      null,
      function (items) {
        if (items.is_active == true) {
          this.onInitNav();
        }
        if (items.is_mouse_active == true) {
          this.onInitCat();
        }
      }.bind(this)
    );
  }
  
  onInitCat() {
    let newCat = {
      itemLeft: chrome.extension.getURL("assets/images/cat/cat3.gif"),
      itemLeftMove: chrome.extension.getURL("assets/images/cat/cat3_move.gif"),
      itemRight: chrome.extension.getURL("assets/images/cat/cat3_r.gif"),
      itemRightMove: chrome.extension.getURL("assets/images/cat/cat3_move_r.gif")
    }
    this.createCat('dearCat', newCat);
    $(document).mousemove(function (e) {
      this.initMouse(e)
    }.bind(this))
  }
  createCat(id, item) {
    this.itemLeft = item.itemLeft;
    this.itemLeftMove = item.itemLeftMove;
    this.itemRight = item.itemRight;
    this.itemRightMove = item.itemRightMove;
    this.mousePositionX = 0;
    this.mousePositionY = 0;
    this.movespeed = 2000;
    this.lookRight = false;
    this.lookLeft = false;
    this.freeview = true;
    this.goRight = true;
    let img = document.createElement("img");
    img.setAttribute('src', this.itemRightMove);
    img.setAttribute('id', id);
    img.style.width = '10%';
    img.style.position = 'absolute';
    img.style.display = 'block';
    img.style.top = $(document).scrollTop() + $(window).height() / 2 - $(img).height() / 2 + 'px';
    img.style.left = '0';
    img.style.zIndex = 1000;
    document.body.appendChild(img);
    $('#dearCat').attr({
      "itemLeft": this.itemLeft,
      "itemLeftMove": this.itemLeftMove,
      "itemRight": this.itemRight,
      "itemRightMove": this.itemRightMove,
      "data-lookRight": this.lookRight,
      "data-lookLeft": this.lookLeft,
      "data-freeview": this.freeview,
      "data-movespeed": this.movespeed,
      "data-goRight": this.goRight
    })
      .bind("contextmenu", function (e) {
        return false;
      });
  }
  initMouse(e) {
    console.log('hhhh')
    let cat = '#dearCat';
    if ($(cat).length) {
      let DOM = $(cat)
      DOM.stop().animate({left: e.pageX, top: e.pageY}, {
        queue: false,
        duration: 2000,
        easing: "swing",
        start: function () {},
        complete: function () {}
      })
    }
  }
}
$(function () {
  let c = new Content();
  c.onInit();
});

撸起来~

一个简单的效果就实现了,当鼠标移动时,猫猫会跟着跑动到鼠标附近。撸起来~

0

安装方法

源文件

1. git clone 本仓库
2. Chrome Extension > Load unpacked > 选择本仓库

chrome-raising-cat.crx

Chrome73禁止安装扩展离线文件。安装报错:This extension is not listed in the Chrome Web Store and may have been added without your knowledge. 可以如下操作:

1. .crx后缀改为.zip
2. 解压
3. Chrome Extension > Load unpacked > 选择本仓库

源码仓库

Github仓库

欢迎Star和交流~

参考