一起用代码吸猫!本文正在参与【喵星人征文活动】。
前言
Chrome浏览器以其开源、免费、拓展性强等优点越来越受开发者喜爱。据w3counter和backlinko统计,截止到2021年9月,世界范围内Chrome占据了Web浏览器高达63.58%的份额,其次是Safari的17.7%和Firefox的5.8%。Chrome官方扩展在13万个以上。
为了能在掘金官网愉快地撸猫,同时本着学习和开发Chrome插件的目的,折腾了几天的时间,开发了这个Chrome-raising-cat
插件。并记录下插件的开发过程,方便后续的交流学习~ 🚀
首先看看效果~
当鼠标移动时,猫猫会跟着跑动到鼠标附近。撸起来~
需求
在掘金官网撸猫总共有几个步骤?
- 首先需要有一只猫
- 放进掘金官网
- 撸起来...
实现
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
设置弹窗页面,这里添加了两个按钮,控制猫猫的显示或隐藏。
// 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();
});
这样就可以在页面中看到内容了:
让猫动起来 🚚
首先,给页面监听鼠标的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();
});
效果:
不足:
虽然能动起来,但是我们可以看到,鼠标移动了一点距离,就打印了很多次,也就是多次触发了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();
});
撸起来~
一个简单的效果就实现了,当鼠标移动时,猫猫会跟着跑动到鼠标附近。撸起来~
安装方法
源文件
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 > 选择本仓库
源码仓库
欢迎Star和交流~