參考資料: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…