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

2,493 阅读4分钟
原文链接: github.com

写在最前

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

欢迎关注我的博客,不定期更新中——

起因

不久之前看到大搜车团队出品的easy mock产品的界面中有一个使用粒子拼出“mock so easy”的效果,感觉非常有意思,就像下面这样:
image
当然了,这个 easy mock的界面中还有粒子汇聚、散开、以及缓动等效果,这些在之后的文章中会不定时的更新实现思路。

我当时看到这个效果的时候是一段单一的英文,不知道源码能不能支持自己配置需要的字符,故想自己实现一个可以配置的版本。

PS:突然想到作者之前也封装过一个输入一段英文,输出一段可表示该字母的“黑魔法代码”:效果就像下面这样:

_20170825233827
缘由也是网上有人用这种黑魔法代码拼出了单词,但是并不是“可配置”的,也就不能想要啥就是啥,故才有了作者的一版封装实现,文章如下:(相关代码在原文中提及

效果图

image

你可以任意输入你能想到的字符,只要打得进去:)

示例:
image

image

核心问题:怎么确定粒子的摆放位置?

emmmm作者目前想到的办法是:降低像素数

来看下这个“非常粗略”的示意图:

image

这是当我在页面输入“an"之后展示的隐藏的canvas的截图,我将其放入到了ps中并放大,我们可以清晰地看到该图是由一个个很小的像素点通过每个像素点不同的颜色最终绘制出来的。而我们要做的就是用更少的“像素点”来绘制同样的内容。也就是原来100 :heavy_multiplication_x:100像素的图,我们如果用25 :heavy_multiplication_x:25来表示,那么每个像素点就会粗很多,同时粒度也会更加宽泛,之后我们如果将像素点变为圆形,最后我们就可以得到如文章开头提到的那样,由一个个粒子“拼”出了效果。

总的来说就是通过将输入的信息转化为图片后,读取图片的像素信息,同时粗略的将图片分块,遍历每块区域中的像素点判断该块是否需要画一个粒子。届时所有区域遍历完毕就可以用比像素点少很多的粒子来大体表示每一个输入的字符。那么具体实现过程如下:

  • 将输入的文字转化为图片插入到一个隐藏的canvas中
  • 按一定比例如(4像素 :heavy_multiplication_x:4像素)分割该canvas图像,形成一个拥有x * y个格子的区域,每个格子中拥有一定像素数(4 :heavy_multiplication_x: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相关文章

最后

源代码见:github.com/Aaaaaaaty/B…

本次只实现了可配置拼出字符的功能,粒子动态上没加入特效,其他效果实现思路之后可能会不定时更新——

惯例po作者的博客,不定时更新中——
有问题欢迎在issues下交流。