canvas 实现图片粒子化效果

使用canvas实现将图片粒子化处理。
HTML5 标签元素本身并没有绘制能力,它仅仅是图形的容器,需要使用getContext()方法可返回一个对象,然后利用getContext("2d")对象属性和方法,在画布上绘制文本、线条、矩形、圆形等。

要实现的效果:
在这里插入图片描述
完成思路:

  1. 创建getContext("2d")对象;
  2. 画布添加图片(或 画布添加文字);
  3. 根据 getImageData 方法获取画布指定矩形像素;
  4. 降低像素,舍弃部分像素点,跳跃性地获取获取画布像素数据,并存储当前选取像素点的位置信息;
  5. 循环储存的像素信息,并根据其位置信息在画布上绘制圆点。

首先,创建对象;

const canvas = document.getElementById('particleImg');
  if (!canvas.getContext) return;
  const ctx = canvas.getContext('2d');

绘制图片。canvas上可以直接绘制图片,创建图片的方法:

var img = new Image();   // 创建一个<img>元素
img.src = 'myImage.png'; // 设置图片源地址

脚本执行后图片开始装载。
绘制img:

// 参数 1:要绘制的 img  
// 参数 2、3:绘制的 img 在 canvas 中的坐标
ctx.drawImage(img,0,0); 

注意:考虑到图片是从网络加载,如果 drawImage 的时候图片还没有完全加载完成,则什么都不做,个别浏览器会抛异常。所以我们应该保证在 img 绘制完成之后再 drawImage。

var img = new Image();   // 创建img元素
img.onload = function(){
    ctx.drawImage(img, 0, 0)
}
img.src = 'myImage.png'; // 设置图片源地址

使用 getImageData() 获取指定矩形像素信息。
关于 getImageData() 方法:

getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。

对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:

R - 红色 (0-255)
G - 绿色 (0-255)
B - 蓝色 (0-255)
A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)
color/alpha 以数组形式存在,并存储于 ImageData 对象的 data 属性中。

故绘制图片并拿到画布信息代码:

const img = document.createElement('img');
  img.src = require('../../assets/laiang.jpg');
  img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0, img.width, img.height);  // 参数 2、3:img 在 canvas 中的坐标
    const imgData = ctx.getImageData(0, 0, img.width, img.height);  //复制画布上指定矩形的像素数据,RGBA的一维数组数据
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  }

降低像素:在画布的水平和垂直方向都每隔4个(根据实际计算)像素获取一次像素信息,并将此像素的“水平”、“垂直”以及“像素信息”记录。

 const dots = [];
 function getImgData(imgData) {
    for (var x = 0; x < imgData.width; x += 4) {
      for (var y = 0; y < imgData.height; y += 4) {
        var i = (y * imgData.width + x) * 4;   // y为行,x为列  每个像素点由RGBA四个数据组成
        if (!(imgData.data[i] >= 200 && imgData.data[i + 1] >= 200 && imgData.data[i + 2] >= 200) && imgData.data[i + 3] >= 128) {  // 有颜色值就渲染
          var dot = new Dot(x - 3, y - 3, 2, `rgba(${imgData.data[i]},${imgData.data[i + 1]},${imgData.data[i + 2]},${imgData.data[i + 3]})`);
          dots.push(dot);
        }
      }
    }
  }
 

创建构造函数Dot的代码为:

  const Dot = function (centerX, centerY, radius, fillStyle) {
    this.x = centerX;
    this.y = centerY;
    this.radius = radius;
    this.fillStyle = fillStyle;
  }
  Dot.prototype = {
    paint: function () {
      ctx.save();
      ctx.beginPath();
      // arc(x, y, r, startAngle, endAngle, anticlockwise) 以x,y为圆心,以r为半径,从startAngle弧度开始到endAngle弧度结束,anticlosewise是布尔值,true表示逆时针,false表示顺时针,默认是顺时针
      ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
      ctx.fillStyle = this.fillStyle;
      ctx.fill()
      ctx.restore();
    }
  }

最后,循环dots并绘制:

function drawData() {
    dots.forEach(function (item) {
      item.paint();
    })
  }

当然,如果不是根据图片绘制粒子,而是根据文字绘制粒子,则画布应该添加文字信息,故第二步,创建及绘制图片处应改为:

function createText() {
	ctx.save();
    ctx.font = "140px 微软雅黑 bold"
    ctx.fillStyle = "rgba(168,168,168,1)";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    // fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的
    ctx.fillText(textStr, canvas.width / 2, canvas.height / 2); 
    ctx.restore();
  }

关于 canvas 的状态保存 save 及恢复 restore :

  1. save():Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存;
    一个绘画状态包括:
    - 当前应用的变形(即移动,旋转和缩放);
    - strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值;
    - 当前的裁切路径(clipping path)。
  2. restore():每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复(类似数组的 pop())

完整代码:

<canvas id='particleImg'></canvas>
const dots = [];

  const canvas = document.getElementById('particleImg');
  const ctx = canvas.getContext('2d');

  const img = document.createElement('img');
  img.src = require('../../assets/laiang.jpg');
  img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0, img.width, img.height);  // 参数 2、3:img 在 canvas 中的坐标
    const imgData = ctx.getImageData(0, 0, img.width, img.height);  //复制画布上指定矩形的像素数据,RGBA的一维数组数据
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    getImgData(imgData);
    drawData();

  }
  // 圆点构造函数
  const Dot = function (centerX, centerY, radius, fillStyle) {
    this.x = centerX;
    this.y = centerY;
    this.radius = radius;
    this.fillStyle = fillStyle;
  }
  Dot.prototype = {
    paint: function () {
      ctx.save();
      ctx.beginPath();
      // arc(x, y, r, startAngle, endAngle, anticlockwise) 以x,y为圆心,以r为半径,从startAngle弧度开始到endAngle弧度结束,anticlosewise是布尔值,true表示逆时针,false表示顺时针,默认是顺时针
      ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
      ctx.fillStyle = this.fillStyle;
      ctx.fill()
      ctx.restore();
    }
  }

  function getImgData(imgData) {
    for (var x = 0; x < imgData.width; x += 4) {
      for (var y = 0; y < imgData.height; y += 4) {
        var i = (y * imgData.width + x) * 4;   // y为行,x为列  每个像素点由RGBA四个数据组成
        if (!(imgData.data[i] >= 200 && imgData.data[i + 1] >= 200 && imgData.data[i + 2] >= 200) && imgData.data[i + 3] >= 128) {  // 有颜色值就渲染
          var dot = new Dot(x - 3, y - 3, 2, `rgba(${imgData.data[i]},${imgData.data[i + 1]},${imgData.data[i + 2]},${imgData.data[i + 3]})`);
          dots.push(dot);
        }
      }
    }
  }

  function drawData() {
    dots.forEach(function (item) {
      item.paint();
    })
  }

参考文章:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值