参考资料:chajian.baidu.com/developer/e…javascript
预览效果:tzc123.github.io/cursor_spec…html
在这个年代,不用chrome都很差意思说本身是敲代码的。特别是前端,chrome对于前端来讲简直是调试利器,不可或缺的存在。不得不说chrome的功能是极其强大的,其中最亮眼的功能莫过于扩展程序
(浏览器插件),国内各大浏览器品牌也都纷纷“效仿”,今天就为你们带来一次chrome插件开发实践。前端
建立一个文件夹cursor_special_effects
java
在文件夹中建立manifest.json
文件,文件内容以下git
{
"manifest_version": 2,
"name": "爆炸吧,小鼠标!",
"version": "0.0.1",
"description": "小鼠标在线爆炸",
"author": "田某人",
"content_scripts": [{
"matches": ["*://*/*"], // 匹配全部的网站
"js": ["index.js"] // 插件的主要代码
}]
}
复制代码
建立好index.js
文件,就能够开始紧张刺激的编程了。github
咱们编写的插件是能够获取到原页面的dom元素的,并且插件只在chrome上安装,就不用考虑该死的兼容了,能够为所欲为的使用一些ES新特性。固然chrome插件一样能在360浏览器、百度浏览器上安装的。chrome
首先分析下需求,须要实现鼠标点击特效,咱们的插件须要哪些功能编程
pointer-events: none;
因为只须要一个canvas,因此就直接使用js建立:json
class CursorSpecialEffects {
constructor() {
this.computerCanvas = document.createElement('canvas')
this.renderCanvas = document.createElement('canvas')
this.computerContext = this.computerCanvas.getContext('2d')
this.renderContext = this.renderCanvas.getContext('2d')
}
// 初始化
init() {
// 设置canvas样式
const style = this.renderCanvas.style
style.position = 'fixed'
style.top = style.left = 0
style.zIndex = '999999999999999999999999999999999999999999'
style.pointerEvents = 'none'
style.width = this.renderCanvas.width = this.computerCanvas.width = this.globalWidth
style.height = this.renderCanvas.height = this.computerCanvas.height = this.globalHeight
// 挂载到页面上
document.body.append(this.renderCanvas)
}
}
const cursorSpecialEffects = new CursorSpecialEffects()
cursorSpecialEffects.init()
复制代码
这里采用离屏渲染,即一个canvas用来计算,一个canvas用来渲染。canvas
如今场景就布置完成了,如今须要添加鼠标的点击事件,每次点击都触发一次特效。
class CursorSpecialEffects {
constructor() {
...
this.runing = false // 标识特效是否在运行
this.booms = [] // 能够同时存在多个特效,因此使用数组来保存
}
init() {
...
window.addEventListener('mousedown', this.handleMouseDown.bind(this))
}
// 鼠标点击事件
handleMouseDown() {
const boom = new Boom({
// 爆炸的原点
origin: { x: e.clientX, y: e.clientY },
// canvas上下文
context: this.computerContext,
// 场景区域,当特效超出场景范围时,就应该中止了
area: {
width: this.globalWidth,
height: this.globalHeight
}
})
boom.init()
this.booms.push(boom)
// 若是特效已经在运行,则不重复开始
this.running || this.run()
}
run() {
// 特效已经开始了
this.running = true
if (this.booms.length == 0) {
// 若是全部的爆炸都消失了,则特效中止
return this.running = false
}
// 每一帧都运行一次,刷新动画
requestAnimationFrame(this.run.bind(this))
// 每次绘制以前都先清空画布
this.computerContext.clearRect(0, 0, this.globalWidth, this.globalHeight)
this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight)
this.booms.forEach((boom, index) => {
// 若是爆炸中止,则将它从特效中移除
if (boom.stop) {
return this.booms.splice(index, 1)
}
// 爆炸每有一点进展,就绘制一次
boom.move()
boom.draw()
})
// 一帧绘制完毕,将计算使用的canvas绘制到页面的canvas上
this.renderContext.drawImage(this.computerCanvas, 0, 0, this.globalWidth, this.globalHeight)
}
}
复制代码
这里引入了一个Boom
类,每次鼠标点击都会建立一个Boom
实例,直到这个实例播放完成,才会被删除。这个Boom类能够有不少实现方式,不一样的实现方式能够实现不一样的特效,前提是这个Boom类须要提供move,draw函数和stop属性。move用于推动特效进行下去,draw用来对每一帧进行绘制,stop用来表示特效是否中止。
接下来介绍一种boom的实现方式:
class Boom {
constructor ({ origin, context, circleCount = 20, area }) {
// 爆炸的原点
this.origin = origin
// canvas上下文
this.context = context
// 小球的数量
this.circleCount = circleCount
// 显示的区域
this.area = area
// 默认中止
this.stop = false
// 小球
this.circles = []
}
// 经过数组取随机值
randomArray(range) {
const length = range.length
const randomIndex = Math.floor(length * Math.random())
return range[randomIndex]
}
// 随机颜色
randomColor() {
const range = ['8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
return '#' + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range)
}
// 随机一个范围内的值
randomRange(start, end) {
return (end - start) * Math.random() + start
}
// 初始化
init() {
// 建立小球
for(let i = 0; i < this.circleCount; i++) {
const circle = new Circle({
context: this.context,
origin: this.origin,
color: this.randomColor(),
angle: this.randomRange(Math.PI - 1, Math.PI + 1),
speed: this.randomRange(1, 6)
})
this.circles.push(circle)
}
}
move() {
// 循环推动每一个小球的运动
this.circles.forEach((circle, index) => {
// 小球若是超过了可视范围,就删除该球
if (circle.position.x > this.area.width || circle.position.y > this.area.height) {
return this.circles.splice(index, 1)
}
circle.move()
})
// 若是全部的小球都被删除,就把这个boom标记为中止,等待下一帧被删除
if (this.circles.length == 0) {
this.stop = true
}
}
draw() {
// 循环绘制每一个小球
this.circles.forEach(circle => circle.draw())
}
}
复制代码
这样Boom
类就实现了,可是到如今仍是不知道特效究竟是什么样子。这里引入了一个Circle
类,具体实现特效的任务落到了Circle
类身上。将Circle
类抽象出来辅助Boom
类,有助于梳理代码逻辑。
class Circle {
constructor({ origin, speed, color, angle, context }) {
this.origin = origin
// 小球的起始位置为原点
this.position = { ...this.origin }
// 小球的颜色
this.color = color
// 小球的速度
this.speed = speed
// 小球发射的角度
this.angle = angle
this.context = context
// 绘制的帧数
this.renderCount = 0
}
draw() {
// 经过颜色、位置、绘制小球
this.context.fillStyle = this.color
this.context.beginPath()
this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2)
this.context.fill()
}
move() {
// 小球移动
this.position.x = (Math.sin(this.angle) * this.speed) + this.position.x
this.position.y = (Math.cos(this.angle) * this.speed) + this.position.y + (this.renderCount * 0.3)
this.renderCount++
}
}
复制代码
这里须要解释的是小球的移动规则,根据角度和速度计算出每一帧小球移动的横坐标Math.sin(this.angle) * this.speed
、纵坐标Math.cos(this.angle) * this.speed
,再加上本来的坐标。为了实现万有引力,将0.3设置为重力加速度。
接下来只须要进入chrome的扩展程序页面,点击打包扩展程序
(没有这个按钮的,须要打开开发者模式),选择cursor_special_effects
文件夹进行打包就能够了。
cursor_special_effects
文件夹的同级目录下看到
cursor_special_effects.pem
、
cursor_special_effects.crx
两个文件,前面一个是秘钥,后面一个就是打包后的文件了。双击或者把
.crx
文件拖到扩展程序页面便可安装。若是chrome安装不了,那就是由于没有发布,使用360浏览器同样能够安装。
安装后刷新页面,效果以下:
看起来还不错,可是由于发布须要5刀的开发费用,因此就算了,就当是闲着无聊的写的小玩意,以后应该会作些更有意思的特效。
github: github.com/tzc123/curs…