canvas图片、文字粒子化

2026-05-01 14:45:31 224
分类:canvas

一、图片粒子化

首先看一下源图和转换成粒子效果的对比图:

star.png

左侧图片为源图,右侧图片为粒子效果图。该效果是在Canvas画布上制作的。将图片制作成粒子效果相对而言是比较简单的。重点了解两个知识点即可

1、图片是通过image对象形式绘制在画布上的,然后使用Canvas的getImageData接口,获取图像的像素信息。具体可参考菜鸟教程:getImageData

var imageData=ctx.getImageData(x, y, width, height);

参数说明:x,y为画布上的x和y坐标

     width,height为获取指定区域图像的信息

返回值说明:imageData为返回值,它是一个对象,包含三个属性

imageData={
    data:Unit8ClampedArray[10000] //一个包含图片区域内每个像素点的RGBA的整型数据信息
    height:200   //读取的图片像素信息区域高度
    width:200   //读取的图片像素信息区域宽度
}

2、了解像素区域数据的排布说明,以上获取的图片数据像素信息(imageData对象中的data属性)为RGBA整型的一维数组数据。一个像素是有4个值(R,G,B,A)组成的。也就是说,数组信息每四个为一个像素点。因此,有以下规则,

  第一个像素信息为:RGBA(data[0],data[1],data[2],data[3])

      第二个像素信息为:RGBA(data[4],data[5],data[6],data[7])

      .....

      第N个像素信息为: RGBA(data[(n-1)*4],data[(n-1)*4+1],data[(n-1)*4+2],data[(n-1)*4+3])

      .....

  另外,像素区域既然是一个区域,它是有宽和高的。上面的推算公式适合单独一行使用定位一个像素点。所以计算像素点时要考虑到在整个图像区域内定位:

  以上图为例。图像的宽和高都为200,如果按照每一个像素为一行一列时。则该图像共有200行,200列。所以要取得 i 行第 j 列的像素初始位置信息为:

       var pos = (j * imageData.width + i) * 4;

其中,公式中的 i 表示行数,j 表示列数。200为图像的宽度。

下面是demo代码:呈现效果如上图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>图片粒子化</title>
</head>
<body>
<canvas id='canvas' width=1000 height=500 style='background-color:#000'>This browser does not support html5.</canvas>

<script>
 var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');

    var image = new Image();
    image.src='star.png';
    image.onload=function () {
        context.drawImage(image, 0, 0);
        getPixels();
        drawPixels();
    }

    var pixels = [];  // 存储像素数据
 var space = 1;    // 遗弃部分像素点

    // 拾取像素点
 function getPixels() {
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
        for (var x = 0; x < imageData.width; x += space) {
            for (var y = 0; y < imageData.height; y += space) {
                var i = (y * imageData.width + x) * 4;

                // 通过rgba中的r判断,拾取有色的图片部分
 if (imageData.data[i] > 0) {
                    pixels.push({
                        x: x,
                        y: y,
                        fillStyle: 'rgba(' + imageData.data[i] + ',' + imageData.data[i + 1] + ',' + imageData.data[i + 2] + ',' + imageData.data[i + 3] + ')'
 })
                }
            }
        }
        console.log(pixels.length);
    }

    // 将保存的像素点重新绘制
 function drawPixels(){
        context.clearRect(0, 0, canvas.width, canvas.height);

        var scale = 2;  // 缩放
 var pos = {x: 100, y: 50}; // 绘制位置

 for (var i = 0; i < pixels.length; i++) {
            context.fillStyle = pixels[i].fillStyle;
            context.fillRect(pixels[i].x * scale + pos.x + Math.random() * 10, pixels[i].y * scale + pos.y + Math.random() * 10, 1, 1);
            context.fillRect(pixels[i].x + 500, pixels[i].y + 200, 1, 1);
        }
    }
</script>
</body>
</html>

这是比较简单的基本实现,后面做下封装,再加上其他动画特效,就可以运用实际项目了。

二、文字粒子化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文字粒子化</title>
</head>
<body>
<canvas id='canvas' width=1000 height=500 style='background-color:#000'>This browser does not support html5.</canvas>
<script>
 var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');

    var pixels = [];  // 存储像素数据
 var space = 1;    // 遗弃部分像素点

 drawText('Canvas');
    getPixels();
    drawPixels();

    // 拾取像素点
 function getPixels() {
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
        for (var x = 0; x < imageData.width; x += space) {
            for (var y = 0; y < imageData.height; y += space) {
                var i = (y * imageData.width + x) * 4;

                // 通过rgba中的r判断,拾取有色的图片部分
 if (imageData.data[i] > 0) {
                    pixels.push({
                        x: x,
                        y: y,
                        fillStyle: 'rgba(' + imageData.data[i] + ',' + imageData.data[i + 1] + ',' + imageData.data[i + 2] + ',' + imageData.data[i + 3] + ')'
 })
                }
            }
        }
        console.log(pixels.length);
    }

    // 将保存的像素点重新绘制
 function drawPixels(){
        context.clearRect(0, 0, canvas.width, canvas.height);

        var scale = 1;  // 缩放
 var pos = {x: 0, y: 0}; // 绘制位置

 for (var i = 0; i < pixels.length; i++) {
            context.fillStyle = pixels[i].fillStyle;
            context.fillRect(pixels[i].x * scale + pos.x + Math.random() * 10, pixels[i].y * scale + pos.y + Math.random() * 10, 1, 1);
        }
    }

    function drawText(text) {
        context.font = "100px 微软雅黑 bold";
        context.fillStyle = "rgba(168,168,168,1)";
        context.textAlign = "center";
        context.textBaseline = "middle";
        context.fillText(text, canvas.width / 2, canvas.height / 2);
    }
</script>
</body>
</html>

参考: