如何實現一個 滑鼠點選特效的 chrome外掛
參考資料:chajian.baidu.com/developer/e…
預覽效果:tzc123.github.io/cursor_spec…
在這個年代,不用chrome都不好意思說自己是敲程式碼的。特別是前端,chrome對於前端來說簡直是除錯利器,不可或缺的存在。不得不說chrome的功能是極其強大的,其中最亮眼的功能莫過於擴充套件程式
(瀏覽器外掛),國內各大瀏覽器品牌也都紛紛“效仿”,今天就為大家帶來一次chrome外掛開發實踐。
準備工作
-
建立一個資料夾
cursor_special_effects
-
在資料夾中建立
manifest.json
檔案,檔案內容如下
{
"manifest_version": 2,
"name": "爆炸吧,小滑鼠!",
"version": "0.0.1",
"description": "小滑鼠線上爆炸",
"author": "田某人",
"content_scripts": [{
"matches": ["*://*/*"], // 匹配所有的網站
"js": ["index.js"] // 外掛的主要程式碼
}]
}
複製程式碼
正式開始
建立好index.js
檔案,就可以開始緊張刺激的程式設計了。
我們編寫的外掛是可以獲取到原頁面的dom元素的,而且外掛只在chrome上安裝,就不用考慮該死的相容了,可以隨心所欲的使用一些ES新特性。當然chrome外掛同樣能在360瀏覽器、百度瀏覽器上安裝的。
首先分析下需求,需要實現滑鼠點選特效,我們的外掛需要哪些功能
- 使用canvas覆蓋在原網頁上
- canvas不能影響原網頁的響應時間,所以加上
pointer-events: none;
由於只需要一個canvas,所以就直接使用js建立:
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用來渲染。
現在場景就佈置完成了,現在需要新增滑鼠的點選事件,每次點選都觸發一次特效。
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…