寫在最前
本次分享一下使用canvas實(shí)現(xiàn)粒子效果拼出你想要的文字慢哈。
歡迎關(guān)注我的博客,不定期更新中——
起因
不久之前看到大搜車團(tuán)隊(duì)出品的easy mock產(chǎn)品的界面中有一個(gè)使用粒子拼出“mock so easy”的效果垦缅,感覺非常有意思,就像下面這樣:
當(dāng)然了译荞,這個(gè)easy mock的界面中還有粒子匯聚瞪醋、散開忿晕、以及緩動(dòng)等效果,這些在之后的文章中會(huì)不定時(shí)的更新實(shí)現(xiàn)思路银受。
我當(dāng)時(shí)看到這個(gè)效果的時(shí)候是一段單一的英文践盼,不知道源碼能不能支持自己配置需要的字符,故想自己實(shí)現(xiàn)一個(gè)可以配置的版本宾巍。
PS:突然想到作者之前也封裝過一個(gè)輸入一段英文咕幻,輸出一段可表示該字母的“黑魔法代碼”:效果就像下面這樣:
緣由也是網(wǎng)上有人用這種黑魔法代碼拼出了單詞,但是并不是“可配置”的顶霞,也就不能想要啥就是啥肄程,故才有了作者的一版封裝實(shí)現(xiàn),文章如下:(相關(guān)代碼在原文中提及
效果圖
你可以任意輸入你能想到的字符选浑,只要打得進(jìn)去:)
示例:
核心問題:怎么確定粒子的擺放位置蓝厌?
emmmm作者目前想到的辦法是:降低像素?cái)?shù)
來看下這個(gè)“非常粗略”的示意圖:
這是當(dāng)我在頁面輸入“an"之后展示的隱藏的canvas的截圖,我將其放入到了ps中并放大鲜侥,我們可以清晰地看到該圖是由一個(gè)個(gè)很小的像素點(diǎn)通過每個(gè)像素點(diǎn)不同的顏色最終繪制出來的。而我們要做的就是用更少的“像素點(diǎn)”來繪制同樣的內(nèi)容诸典。也就是原來100??100像素的圖描函,我們?nèi)绻?5??25來表示,那么每個(gè)像素點(diǎn)就會(huì)粗很多狐粱,同時(shí)粒度也會(huì)更加寬泛舀寓,之后我們?nèi)绻麑⑾袼攸c(diǎn)變?yōu)閳A形,最后我們就可以得到如文章開頭提到的那樣肌蜻,由一個(gè)個(gè)粒子“拼”出了效果互墓。
總的來說就是通過將輸入的信息轉(zhuǎn)化為圖片后,讀取圖片的像素信息蒋搜,同時(shí)粗略的將圖片分塊篡撵,遍歷每塊區(qū)域中的像素點(diǎn)判斷該塊是否需要畫一個(gè)粒子判莉。屆時(shí)所有區(qū)域遍歷完畢就可以用比像素點(diǎn)少很多的粒子來大體表示每一個(gè)輸入的字符。那么具體實(shí)現(xiàn)過程如下:
- 將輸入的文字轉(zhuǎn)化為圖片插入到一個(gè)隱藏的canvas中
- 按一定比例如(4像素??4像素)分割該canvas圖像育谬,形成一個(gè)擁有x * y個(gè)格子的區(qū)域券盅,每個(gè)格子中擁有一定像素?cái)?shù)(4??4 = 16)
- 讀取該canvas中的圖片像素?cái)?shù)據(jù)
- 獲取在每一個(gè)格子中擁有除灰度顏色的像素?cái)?shù)(白底或者黑底屬于插入到canvas中的圖片的背景)
- 當(dāng)一個(gè)格子中有顏色的像素?cái)?shù)占所有像素的一定程度后,認(rèn)定該區(qū)域?qū)儆谳斎胱址囊徊糠痔盘矗瑒t在該區(qū)域畫一個(gè)粒子锰镀,否則不畫
實(shí)現(xiàn)過程
文字轉(zhuǎn)化為圖片插入canvas
function loadCanvas(value) {
var fontSize = 100,
width = calWordWidth(value, fontSize),
canvas = document.createElement('canvas')
canvas.id = 'b_canvas'
canvas.width = width
canvas.height = fontSize
var ctx = canvas.getContext('2d')
ctx.font = fontSize + "px Microsoft YaHei"
ctx.fillStyle = "orange"
ctx.fillText(value, 0, fontSize / 5 * 4) //輕微調(diào)整繪制字符位置
getImage(canvas, ctx) //導(dǎo)出為圖片再導(dǎo)入到canvas獲取圖像數(shù)據(jù)
}
function getImage(canvas, ctx) {
var image = new Image()
image.src = canvas.toDataURL("image/jpeg") //canvas導(dǎo)出
image.onload = function() {
...
}
}
降低像素?cái)?shù)
var imageData = ctx.getImageData(0, 0, this.width, this.height)
var dataLength = imageData.data.length
var diff = 4 //按4??4劃分區(qū)域,可自行改變嘗試
var newCanvas = document.getElementById('canvas')
var newCtx = newCanvas.getContext('2d')
for (var j = 0; j < this.height; j += diff) { //height為canvas高
for (var i = 0; i < this.width; i += diff) {//width為canvas寬
var colorNum = 0
for (var k = 0; k < diff * diff; k++) {
var row = k % diff
var col = ~~(k / diff)
let r = imageData.data[((j + col) * this.width + i + row) * 4 + 0]
let g = imageData.data[((j + col) * this.width + i + row) * 4 + 1]
let b = imageData.data[((j + col) * this.width + i + row) * 4 + 2]
if (r < 10 && g < 10 && b < 10) colorNum++
//如果滿足此條件說明當(dāng)前為背景色黑色(canvas轉(zhuǎn)出來的圖片背景并不是純黑的
}
if (colorNum < diff * diff / 3 * 2) {
//黑色背景占比小于一定程度說明此處應(yīng)該畫粒子咖刃,占比度可自行調(diào)整
var option = {
x: i,
y: j,
radius: 6,
color: '#fff'
}
var newBubble = new Bubble(option)
newBubble.draw(newCtx) //畫粒子
}
}
}
其他canvas相關(guān)文章
- 基于canvas使用貝塞爾曲線平滑擬合折線段
- 用canvas實(shí)現(xiàn)視頻播放與彈幕功能
- 基于canvas實(shí)現(xiàn)波浪式繪制圖片
- 基于 canvas 實(shí)現(xiàn)的一個(gè)截圖小 demo
最后
源代碼見:https://github.com/Aaaaaaaty/Blog/blob/master/canvas/canvasImitateWord/main.js
本次只實(shí)現(xiàn)了可配置拼出字符的功能泳炉,粒子動(dòng)態(tài)上沒加入特效,其他效果實(shí)現(xiàn)思路之后可能會(huì)不定時(shí)更新——
慣例po作者的博客嚎杨,不定時(shí)更新中——
有問題歡迎在issues下交流花鹅。