canvas实现黑客帝国背景

340 阅读1分钟

效果

  1. 在页面顶部随机出现若干个词条,词条纵向排列透明度从0到1,从上到下移动;
  2. 词条移动到中间时,顶部出现新的词条;
  3. 用canvas实现;

实现步骤:

  1. 生成一个词条;从26个英文单词随机抽出20-30个组成一个词条;
  2. 将词条纵向画到画布上
  3. 每隔一定时间刷新画布,更改词条的y坐标,让词条往下移动;
  4. 根据文字大小和画布大小沿x轴等距生成一排词条,每个词条以随机速度向下移动;

代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    *{
      padding: 0;
      margin: 0;
    }
    body{
      position: absolute;
      left:0;
      right: 0;
      top:0;
      bottom:0;
      
    }
    body::-webkit-scrollbar{
      width:0;
      height:0;
    }
    #canvas{
      background-color: black;
    }
  </style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
  class Matrix {
    constructor() {
      this._init();
    }
    _init(){
      this.canvasEle = document.getElementById('canvas');
      this.ctx = this.canvasEle.getContext('2d');
      this.bodyEle = document.getElementsByTagName('body')[0];
      this.canvasWidth = 150;
      this.canvasHeight = 150;
      this.fontSize = 20;
      this.timer = null;
      this.xMalPositions = [-5, 0, 5]; // x坐标偏移
      this.maxCount = 300; // 限制词条个数,防止太多导致卡顿
      this.wordPool = []; // 词条数组
      this.words = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
      this.setCanvasSize();
      window.addEventListener('resize', () => {
        this.setCanvasSize();
      });
      this._initWordPool();
      this.timer = this.setTimer(() => {
        for(let i = 0; i < this.wordPool.length; i++) {
          let curWord = this.wordPool[i];
          curWord.y += curWord.speed;
          if(curWord.y  >= this.canvasHeight) {
            this.wordPool.splice(i, 1);
          };
          const needAddNewWoed = Math.random() > 0.6;
          if(this.wordPool.length < this.maxCount && needAddNewWoed && (curWord.y + curWord.textHeight) > this.canvasHeight) {
            console.log(this.wordPool.length, '长度');
            const malPosition = this.xMalPositions[parseInt(Math.random() * this.xMalPositions.length)];
            const newX = this.fontSize * i + malPosition;
            const word = this._generateWord(i, newX);
            this.wordPool.push(word);
          }
          this.drawWord(curWord);
        }
      });
    }
    // 初始化 词条池
    _initWordPool() {
      const poolLen = parseInt(this.canvasWidth / this.fontSize);
      for(let i = 0; i < poolLen; i++) {
        const oneWord = this._generateWord(i);
        this.wordPool.push(oneWord);
      }
    }
    // 生成词条
    _generateWord(index, newX) {
      const text = this.getRandomWord();
      const textHeight = text.length * this.fontSize;
      const malPosition = this.xMalPositions[parseInt(Math.random() * this.xMalPositions.length)];
      const startY = 0 - textHeight;
      const word = {
        x: newX || (this.fontSize * index + malPosition),
        y: startY,
        recordY: startY,
        fontSize: this.fontSize,
        text,
        textHeight,
        speed: [5, 6, 7][parseInt(Math.random() * 3)], // 随机速度
      }
      return word;
    }
    // 设置计时器
    setTimer(cb) {
      return setInterval(() => {
        cb()
      }, 30);
    }
    // 设置canvas大小
    setCanvasSize(){
      const { clientWidth, clientHeight } = this.bodyEle;
      this.canvasWidth = clientWidth;
      this.canvasHeight = clientHeight;
      this.canvasEle.width = clientWidth;
      this.canvasEle.height = clientHeight;
    }
    // 随机生成句子
    getRandomWord() {
      let count = parseInt(Math.random() * 20 + 10); // [20, 30)
      let word = '';
      while(count >= 0) {
        word += this.words[parseInt(Math.random() * (this.words.length + 1))];
        count -= 1;
      }
      return word;
    };
    // 绘制词条
    drawWord(oneWord) {
      const len = oneWord.text.length;
      this.ctx.clearRect(oneWord.x - 2, oneWord.recordY, oneWord.fontSize + 2, oneWord.textHeight);
      if(oneWord.y >= this.canvasHeight) return;
      oneWord.recordY = oneWord.y;
      this.ctx.textBaseline = "top";
      this.ctx.font = `${oneWord.fontSize}px Microsoft YaHei`;
      
      let startY = oneWord.y;
      for(let i = 0; i < len; i ++) {
        const opacity = i / (len - 1);
        this.ctx.fillStyle = `rgba(49, 117, 51, ${opacity})`;
        this.ctx.fillText(oneWord.text[i], oneWord.x, startY);
        startY += oneWord.fontSize;
      }
    }
  }
  const matrix = new Matrix();

</script>
</body>
</html>