如何實現一個 滑鼠點選特效的 chrome外掛
參考資料: ofollow,noindex">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瀏覽器、百度瀏覽器上安裝的。
首先分析下需求,需要實現滑鼠點選特效,我們的外掛需要哪些功能
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…