1. 程式人生 > >基於canvas使用粒子拼出你想要的文字

基於canvas使用粒子拼出你想要的文字

寫在最前

本次分享一下使用canvas實現粒子效果拼出你想要的文字。

歡迎關注我的部落格,不定期更新中——

起因

不久之前看到大搜車團隊出品的easy mock產品的介面中有一個使用粒子拼出“mock so easy”的效果,感覺非常有意思,就像下面這樣:
image
當然了,這個easy mock的介面中還有粒子匯聚、散開、以及緩動等效果,這些在之後的文章中會不定時的更新實現思路。

我當時看到這個效果的時候是一段單一的英文,不知道原始碼能不能支援自己配置需要的字元,故想自己實現一個可以配置的版本。

PS:突然想到作者之前也封裝過一個輸入一段英文,輸出一段可表示該字母的“黑魔法程式碼”:效果就像下面這樣:

_20170825233827
緣由也是網上有人用這種黑魔法程式碼拼出了單詞,但是並不是“可配置”的,也就不能想要啥就是啥,故才有了作者的一版封裝實現,文章如下:(相關程式碼在原文中提及

效果圖

image

你可以任意輸入你能想到的字元,只要打得進去:)

示例:
image

image

核心問題:怎麼確定粒子的擺放位置?

emmmm作者目前想到的辦法是:降低畫素數

來看下這個“非常粗略”的示意圖:

image

這是當我在頁面輸入“an”之後展示的隱藏的canvas的截圖,我將其放入到了ps中並放大,我們可以清晰地看到該圖是由一個個很小的畫素點通過每個畫素點不同的顏色最終繪製出來的。而我們要做的就是用更少的“畫素點”來繪製同樣的內容。也就是原來100✖️100畫素的圖,我們如果用25✖️25來表示,那麼每個畫素點就會粗很多,同時粒度也會更加寬泛,之後我們如果將畫素點變為圓形,最後我們就可以得到如文章開頭提到的那樣,由一個個粒子“拼”出了效果。

總的來說就是通過將輸入的資訊轉化為圖片後,讀取圖片的畫素資訊,同時粗略的將圖片分塊,遍歷每塊區域中的畫素點判斷該塊是否需要畫一個粒子。屆時所有區域遍歷完畢就可以用比畫素點少很多的粒子來大體表示每一個輸入的字元。那麼具體實現過程如下:
- 將輸入的文字轉化為圖片插入到一個隱藏的canvas中
- 按一定比例如(4畫素✖️4畫素)分割該canvas影象,形成一個擁有x * y個格子的區域,每個格子中擁有一定畫素數(4✖️4 = 16)
- 讀取該canvas中的圖片畫素資料
- 獲取在每一個格子中擁有除灰度顏色的畫素數(白底或者黑底屬於插入到canvas中的圖片的背景)
- 當一個格子中有顏色的畫素數佔所有畫素的一定程度後,認定該區域屬於輸入字元的一部分,則在該區域畫一個粒子,否則不畫

實現過程

文字轉化為圖片插入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) //輕微調整繪製字元位置
    getImage(canvas, ctx) //匯出為圖片再匯入到canvas獲取影象資料
}
function getImage(canvas, ctx) {
    var image = new Image()
    image.src = canvas.toDataURL("image/jpeg") //canvas匯出
    image.onload = function() {
        ... 
    }
}

降低畫素數

var imageData = ctx.getImageData(0, 0, this.width, this.height)
var dataLength = imageData.data.length
var diff = 4 //按4✖️4劃分區域,可自行改變嘗試
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++ 
            //如果滿足此條件說明當前為背景色黑色(canvas轉出來的圖片背景並不是純黑的
        }
        if (colorNum < diff * diff / 3 * 2) {
        //黑色背景佔比小於一定程度說明此處應該畫粒子,佔比度可自行調整
            var option = {
                x: i,
                y: j,
                radius: 6,
                color: '#fff'
            }
            var newBubble = new Bubble(option)
            newBubble.draw(newCtx) //畫粒子
        } 
    }
}

其他canvas相關文章

最後

本次只實現了可配置拼出字元的功能,粒子動態上沒加入特效,其他效果實現思路之後可能會不定時更新——

慣例po作者的部落格,不定時更新中——
有問題歡迎在issues下交流。