likes
comments
collection

黑客帝国 - 矩阵雨

作者站长头像
站长
· 阅读数 15

上次编写的文章 韩式浪漫 - 落雪唯美,我们整了个雪花的效果。

咦,那么我们可不可以扩展下,整一个 黑客帝国矩阵雨的效果 呢?

答案是肯定的,下面来整活。

该效果主要完成的功能有两点:

  • 矩阵雨绘制
  • 红蓝药丸编写

矩阵雨绘制

我们同样使用 canvas 来实现。基本思路如下:

  • 初始化画布,画笔
  • 初始化矩阵雨有多少列
  • 01 的初始化绘制的字符串
  • 在画布上绘制,以随机的 01 填充,计算绘制的 xy 轴的距离,并重复绘制
  • 监听视图窗口的更改,更新画布的大小和矩阵雨多少列的数据

实现的代码不多,这里贴上 JavaScript 文件的代码。代码即文档,若难以理解,请结合代码中的注释去学习。

(function() {
  let canvas = document.getElementById('canvas'); // 画布
  let ctx = canvas.getContext('2d'); // 画笔
  let width = canvas.width = window.innerWidth; // 设置 canvas 的大小,然后赋值给 width,方便后面的计算
  let height = canvas.height = window.innerHeight;
  let font = 12;
  let cols = 0;
  let dys = []; // 垂直距离的移动距离数组

  let matrix = '';
  let matrixSize = 20; // 矩阵雨的随机数量
  for(let i = 0; i < matrixSize; i += 1) {
    matrix += Math.floor(Math.random() * 2);
  }

  initData();
  // 初始化关键数据
  function initData() {
    // 列数据
    cols = width / font;
    // 随机生成 dy 垂直移动的距离
    for(let i = 0; i < cols; i += 1) {
      dys[i] = Math.floor(Math.random() * cols);
    }
  }

  

  function draw(){
    // ctx.clearRect(0, 0, width, height); 
    ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
    ctx.fillRect(0, 0, width, height); // 填充画布
    ctx.fillStyle = "#00ff00";
    ctx.font = `${font}px`;
    for(let i = 0; i < cols; i += 1) {
      let txt = matrix[Math.floor(Math.random() * matrixSize)];
      ctx.fillText(txt, i * font, dys[i] * font);
      if(dys[i] * font > height) {
        dys[i] = Math.floor(Math.random() * cols);
      }
      dys[i] += 1; // 垂直距离的设置
    }
    requestAnimationFrame(draw); // 重新绘制
  }

  draw();
  
  // 监听窗口更改
  window.addEventListener('resize', () => {
    // 重置画布的大小
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
    // 重新初始化关键数据
    initData();
  });
})()

需要注意的是,draw() 函数中,不可添加 ctx.clearRect(0, 0, width, height); 代码,不然就是雪花飘落的效果了。读者可以放开该代码,自行验证。

clearRect 是清空画布的指定区域

疑问点🤔️?细心的读者可能会提问:怎么效果图看起来有种类似拖尾的渐变效果?

黑客帝国 - 矩阵雨

这就很有意思了。但是实现挺简单的。你可以想象一下,写上文字的多个半透明的纸张依次叠加在一起的场景。也就是下面代码实现的功能:

// 透明度是 0.05 的黑色颜料
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, width, height); // 填充画布

初始化绘制是 0.05 的透明度,第二次绘画的时候,初始化的透明度视觉效果增加到了 0.1,依次类推 0.150.2 .... 一直到 1.0 或以上的时候,你就对初始化的绘制文字看不出了。

红蓝药丸编写

黑客帝国 - 矩阵雨

我们通过 css 来实现 -- box-shadowlinear-gradient 并结合 ::before 或者 ::after 伪元素。

// 这里使用 less 预处理器编写
.pill{
  position: relative;
  width: @pillWidth;
  height: @pillHeight;
  border-radius: @pillRadius;
  // 高亮
  box-shadow: 0 1px 10px rgba(255,255,255,.1),
    0 15px 10px rgba(255,255,255,.1),
    0 0 10px rgba(255,255,255,.1),
    0 -5px 5px rgba(255,255,255,.4);
}
// 红色药丸
.red {
  background: linear-gradient(180deg,#ff4213 0%,#ab4225 100%) , 
    linear-gradient(180deg,#ff4213 0%,#ab4225 100%);
}
// 蓝色药丸
.blue {
  background: linear-gradient(180deg,#0000ff 0%,#add8e6 100%), 
    linear-gradient(180deg,#0000ff 0%,#add8e6 100%);
}
.pill:after{
  content: '';
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: @pillWidth;
  height: @pillHeight;
  border-radius: @pillRadius;
  // 阴影
  box-shadow: 0 0 15px rgba(56,56,56,.7) inset, // inset 表明内阴影
    0 0 5px rgba(23,23,23,.5) inset,
    0 -7.5px 5px rgba(23,23,23,.5) inset,
    0 0 1.5px rgba(	0, 255, 0,.3), 
    0 9px 20px rgba(	0, 255, 0,.4), 
    -1.5px 0 3px rgba(0,0,0,.4) inset,
    1.5px 0 1.5px rgba(0,0,0,.4) inset,
    0 5px 5px rgba(	0, 255, 0, .1), 
    0 6px 5px rgba(	0, 255, 0, .2), 
    0 12px 10px rgba(	0, 255, 0,.4);
}

整体效果

实现的体验效果如下,读者可以进入阅读全部的代码👇

推荐阅读

本文正在参加「金石计划 . 瓜分6万现金大奖」