欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript+Canvas實現(xiàn)文字粒子流特效

 更新時間:2023年01月14日 11:09:19   作者:鄭丫頭  
看到大師級的canvas文字粒子動畫,要10個jq幣才能下載啊,我內(nèi)心的小鹿蠢蠢欲動,我也要寫一個。所以本文就來用Canvas實現(xiàn)簡單的文字粒子流特效,希望對大家有所幫助

動手前思考

首先要在特定的位置生成粒子,要獲取到canvas上像素的點位,通過canvas的getImageData函數(shù)我們可以得到canvas像素點的信息,獲取像素點中透明度大于0的位置。

繪制文字

新建一個canvas畫布,在畫布上繪制任意的文字

ctx.font = "200px Arial";
ctx.fontWeight = "900";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillStyle = "red";
ctx.fillText("稀土掘金", 0, 0);

獲取像素點位

canvas的getImageData函數(shù)返回了imageData對象,該對象復(fù)制畫布上指定矩形的像素數(shù)據(jù),數(shù)組中四個值一組存放像素的RGBA信息,其中A代表透明度,當(dāng)透明度大于0時表示該位置上可以生成粒子。

對于 ImageData 對象中的每個像素,都存在著四方面的信息,即 RGBA 值:

  • R - 紅色 (0-255)
  • G - 綠色 (0-255)
  • B - 藍色 (0-255)
  • A - alpha 通道 (0-255; 0 是透明的,255 是完全可見的)

也就是

red=imgData.data[0];
green=imgData.data[1];
blue=imgData.data[2];
alpha=imgData.data[3];

調(diào)用getImageData函數(shù),有4個參數(shù),x/y表示開始復(fù)制的左上角位置的xy坐標,width/height表示將要復(fù)制的矩形區(qū)域的寬度和高度。

var imgData=context.getImageData(x,y,width,height);

imageData對象信息如下:

如果繪制文字較小最好截取所在矩形位置,以減少獲取的data數(shù)組。獲取canvas繪制文字的寬度方法如下:

const { width } = ctx.measureText(this.text);

遍歷數(shù)組,得到可用像素點:

  • 4組一個像素點,gap表示點的間隔,我們只獲取A的位置。
  • wl表示canvas寬度,矩形像素格子排滿寬度后會換行,由此可以計算出點位的xy坐標。因為四個組合一個點,所以wl要乘以4。
const gap = 4;
for (let i = 0, wl = this.canvas.width * gap; i < length; i += gap) {
    if (data[i + gap - 1]) {
      // 根據(jù)透明度判斷
      const x = (i % wl) / gap;
      const y = parseInt(i / wl);
      this.textPoints.push([x, y]);
    }
}

渲染粒子

獲取到像素點位textPoints數(shù)據(jù)之后就可以開始渲染粒子了

  • 新建一個畫布,隱藏獲取像素點的畫布,在這個新的畫布上繪制粒子
  • 設(shè)置粒子的半徑為5,間隔10個點位生成一個粒子
  • 粒子的顏色隨機生成,最終得到如下圖:

let startX;
let startY;
const points = [];
for (let i = 0; i < this.textPoints.length; i++) {
    let point = this.textPoints[i];
    let x = point[0];
    let y = point[1];
    const radius = 5;
    // const radius = Math.random() * 10; // 隨機生成粒子寬度
    const color = parseInt(Math.random() * 0xffffff).toString(16); // 隨機生成粒子顏色
    const { x: x0, y: y0 } = this.adjustPoint(x, y); // 矯正粒子相對于畫布所在位置
    if (i == 0 || ((x - startX) % 10 == 0 && (y - startY) % 10 == 0)) {
      startX = x;
      startY = y;
      const params = { x: x0 + radius, y: y0, radius, color };
      points.push(params);
    }
}

優(yōu)化展示效果

  • 為了讓生成的文字更好看,我們可以隨機設(shè)置粒子的半徑
  • 為了避免后面生成的粒子總是擋住前面粒子,我們可以隨機生成粒子的順序
  • adjustPoint函數(shù)讓粒子在新畫布中居中展示
this.points = points.sort((a, b) => (Math.random() > 0.5 ? -1 : 1)); // 隨機排序
adjustPoint(x, y) {
  const { width, height } = this.canvasLizi;
  return {
    x: x + (width - this.textWidth) / 2,
    y: y + (height - this.textSize) / 2,
  };
}

簡單的動畫效果

  • 粒子從上下左右四個方向隨機生成,匯聚到畫布中心點
  • 點擊文字時,粒子出現(xiàn)炸開的特效

1、隨機選擇四個方向中的某一個方向,生成初始坐標

  • 從左邊進入畫布初始坐標為(0,y),從右邊進入畫布初始坐標為(canvasWidth, y),y是隨機數(shù)
  • 從上邊進入畫布初始坐標為(x,0),從下邊進入畫布初始坐標為(x,canvasHeight),x是隨機數(shù)
for (let item of this.points) {
    let direction;
    const num = Math.random() * 1;
    if (num < 0.25) {
      direction = "left";
      item.initX = 0;
      item.initY = Math.random() * height;
    } else if (num < 0.5) {
      direction = "right";
      item.initX = width;
      item.initY = Math.random() * height;
    } else if (num < 0.75) {
      direction = "top";
      item.initX = Math.random() * width;
      item.initY = 0;
    } else {
      direction = "bottom";
      item.initX = Math.random() * width;
      item.initY = height;
    }
}

2、從初始位置運動到實際位置

  • 計算實際點與運動點之間的坐標差offsetX,offsetY
  • 判斷差值是正數(shù)還是負數(shù),當(dāng)x差值為正數(shù),則每次運動的速率為正,否則為負;當(dāng)y差值為正數(shù),則每次運動的速率為正,否則為負。
  • 計算每次運動的增量或減量。因為初始坐標和實際坐標連線可能是一條斜線,當(dāng)x增加或減少一定數(shù)值,y值增量或減量等于x增量或減量乘以斜率。
animatDot() {
  if (!this.points.find((item) => item.hasOwnProperty("initX"))) {
    // 當(dāng)不存在運動點時取消動畫
    cancelAnimationFrame(this.animatDot.bind(this));
    return;
  }
  this.points.forEach((item) => {
    const offsetX = item.x - item.initX;
    const offsetY = item.y - item.initY;
    if (Math.abs(offsetX) > 0 || Math.abs(offsetY) > 0) {
      const rate = offsetX / 10; // 速率等于坐標差除以10,不斷縮小運動距離
      const x = item.initX + rate;
      if (Math.abs(rate) < 1) {
        item.initX = item.x; // 當(dāng)運動距離小于1時,等于實際坐標
      } else {
        if (offsetX > 0) {
          item.initX = x < item.x ? x : item.x;
        } else {
          item.initX = x > item.x ? x : item.x;
        }
      }
      const k = offsetY / offsetX; // 計算斜率
      const y = k * item.initX;
      if (offsetY > 0) {
        item.initY = y < item.y ? y : item.y;
      } else {
        item.initY = y > item.y ? y : item.y;
      }
    } else {
      delete item.initX; // 當(dāng)運動點坐標和實際坐標相同時,刪除初始坐標
      delete item.initY;
    }
  });
  this.drawPoint(); // 繪制粒子函數(shù)
  requestAnimationFrame(this.animatDot.bind(this), 1000 / 60);
}

3、點擊文字炸開的特效

  • 監(jiān)聽鼠標點擊事件,獲取鼠標點擊坐標clickPointX,clickPointY
  • 設(shè)置一個鼠標點擊緩沖區(qū)clickRange,使一定范圍內(nèi)的粒子都產(chǎn)生炸開的效果;設(shè)置一個炸開的最遠距離spreadRange,當(dāng)大于該距離就停止運動
  • y軸的移動還是跟斜率有關(guān)系,要計算運動點跟鼠標點擊位置的斜率
this.clickRange = 30; // 點擊范圍
this.spreadRange = 30; // 擴散范圍
PBomb() {
  const that = this;
  function animaion(time) {
    that.PBomb();
  }
  if (!this.points.find((item) => item.bomb)) {
    cancelAnimationFrame(animaion); // 停止動畫判斷
    return;
  }
  const step = 10; // x軸步長,每幀增加或減少的大小
  this.points.forEach((point) => {
    if (point.bomb) {
      if (point.bombX > this.clickPointX) {
        if (point.bombX < point.x + this.spreadRange) {
          point.bombX += step;
        } else {
          point.bomb = false;
        }
      }
      if (point.bombX < this.clickPointX) {
        if (point.bombX > point.x - this.spreadRange) {
          point.bombX -= step;
        } else {
          point.bomb = false;
        }
      }
      const k =
        point.x - this.clickPointX == 0
          ? 1
          : Math.abs(
              (point.y - this.clickPointY) /
                (point.x - this.clickPointX),
            ).toFixed(2); // 計算斜率
      if (point.bombY > this.clickPointY) {
        if (point.bombY < point.y + this.spreadRange) {
          point.bombY += k * step;
        } else {
          point.bomb = false;
        }
      }
      if (point.bombY < this.clickPointY) {
        if (point.bombY > point.y - this.spreadRange) {
          point.bombY -= k * step;
        } else {
          point.bomb = false;
        }
      }
    }
  });
  this.drawPoint();
  setTimeout(() => {
    requestAnimationFrame(animaion);
  }, 1000 / 10);
}

最終效果:

最后加上修改文字內(nèi)容和大小的功能就完美了

到此這篇關(guān)于JavaScript+Canvas實現(xiàn)文字粒子流特效的文章就介紹到這了,更多相關(guān)JavaScript Canvas文字粒子流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論