HTML5 也出来一段时间了,带来了一些新的技术标准,例如 HTML5 里的 Canvas 标签,<canvas></canvas>,允许我们使用 Javascript 在 canvas 标签里绘制 2D 或 3D 图形和动画。下面就谈谈对 canvas 绘图的理解以及介绍其中一些使用过的方法,用一个条形进度条和一个仿科幻文字做展示例子。

<canvas> 元素不被一些老的浏览器所支持,但是所有的主流浏览器的新近版本都支持,兼容性问题尚好。使用 canvas 标签来绘制图形或动画需要掌握一些基本的 HTML 和 JavaScript 知识,通过 canvas 的 api 和了解 canvas 绘图的原理即可。其实 canvas 上的动作的原理和显示器中动作原理是一样的,都是一帧一帧绘制静态图形,绘制刷新的速度超过了我们的人眼识别速度,所以看上去就像真的在动一样。不同点是显示器的刷新是实时的,而 canvas 的刷新需要我们通过 js 控制来手动进行触发,如果只是绘制简单的静态图形,那么这一步就不需要我们手动控制进行刷新了。比如说我们想要一个矩形从左边移到右边,开始时可以让矩形在最左边,然后下一秒时再往右一些绘制相同的矩形,但是前提是先把原来的矩形给去掉,再重新绘制。通过不断的擦出和重绘,就会出现动作效果,通过加快速度不被人眼识破。

canvas 在 html 文档中通过标签创建后是空白的,是一个固定大小的画布,可以通过 width 和 height 控制它的宽高,默认大小为300像素×150像素(px)。为了展示,首先需要找到渲染上下文,然后通过 js 在 canvas 上进行绘制。<canvas> 元素有一个叫做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。getContext()只有一个参数,上下文的格式,对于 2D 图像而言,填入 '2d' 字符串。

var canvas = document.getElementByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
// 清除画布
context.clearRect(0, 0, canvas.width, canvas.height);

通过 document.getElementByTagName() 方法来为 <canvas> 元素得到 DOM 对象。通过 DOM 元素上的getContext() 方法来访问绘画上下文。看一个条形进度条例子 https://codepen.io/zgfrank/pe...

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    centerX = canvas.width/2,
    centerY = canvas.height/2,
    speed = 0.1;

function text(n){
  context.save();
  context.fillStyle = "#F47C7C";
  context.font = "40px Arial";
  context.textAlign = "center";
  context.textBaseline = "middle";
  context.fillText(n.toFixed(0)+"%", centerX, centerY);
  context.restore();
}

function rectfixed(){
  context.beginPath();
  context.rect(50, 140, 400, 20);
  
  context.lineWidth = 1;
  context.strokeStyle = 'white';
  context.fillStyle = 'white';

  context.fill();
  context.stroke();
}

function rectmove(speed){
  context.beginPath();
  context.rect(50, 140, speed*4, 20);

  context.lineWidth = 1;
  context.fillStyle = '#F47C7C';

  context.fill();
  context.stroke();
}


(function drawFrame(){
  window.requestAnimationFrame(drawFrame, canvas);
  context.clearRect(0, 0, canvas.width, canvas.height);

  text(speed);
  rectfixed()
  rectmove(speed)
  if(speed > 100) speed = 0;
  speed += 0.2;
}());

从代码上看,首先绘制矩形的 api 是 context.rect(x, y, width, height),分别是x/y坐标,宽和高。第一个条形是固定不动的,第二个条形的宽随着speed的变动而变动,通过window.requestAnimationFrame循环调动drawFrame函数达到动画的效果。requestAnimationFrame是一个新的API,作用与setTimeInterval一样,不同的是它会根据浏览器的刷新频率自动调整动画的时间间隔。也别忘了要对画布调用clearRect方法清除上一帧的图案才能产生正确效果。绘制文本的 api 是,给图形上色有两个重要的属性可以做到,分别是 fillStyle 和 strokeStyle。fillStyle 是设置图形的填充颜色,strokestyle 是设置图形的轮廓颜色,通过 fill() 和stroke() 方法,还有更多样式设置可以参考 MDN 中的 canvas API。

第二个例子是仿科幻文字效果,https://codepen.io/zgfrank/pe...

var canvas = document.querySelector('#test'),
    ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth,
    canvas.height = window.innerHeight;

var shadowColor = 'rgba(0, 0, 0, 0.08)',  //用于覆盖文字的渐变阴影
    textColor = "#33ff33",                //文字颜色
    words = "0123456789qwertyuiopasdfghjklzxcvbnm,./;'\[]QWERTYUIOP{}ASDFGHJHJKL:ZXCVBBNM<>?",      //随机文字
    wordsArray = words.split(''),         //将文字拆分进一个数组
    fontSize = 18,                        //字体大小
    columns = canvas.width / fontSize,    //文字降落的列数
    drops = [];                           //文字行数

for(var i = 0; i < columns; i++){
  //从第一行开始
  drops[i] = 1;
}

function drawText(){
  ctx.save();
  ctx.fillStyle = textColor;
  ctx.font = fontSize + "px arial";
  
  for (var i = 0; i < drops.length; i++){
    // 随机取文字
    var text = wordsArray[Math.floor(Math.random() * wordsArray.length)];
    // 绘制第n行文字
    ctx.fillText(text, i * fontSize, drops[i] * fontSize);
    // 差异,数字越小差异越小
    if (drops[i] * fontSize > canvas.height && Math.random() > 0.99){
      drops[i] = 0;
    }
    drops[i]++;
  }
  ctx.restore();
}

(function drawFrame(){
  window.requestAnimationFrame(drawFrame, canvas);
  // 阴影覆盖文字
  ctx.fillStyle = shadowColor;
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  drawText();
}())

上面的代码就是实现该效果的关键:通过 fillText() 方法来绘制随机的字符,通过 fillRect() 方法覆盖一个带有透明度的黑色矩形实现文字被覆盖形成阴影的效果。绘制列数是 canvas 宽度 / 字体大小。在绘制函数中,最开始是一行行绘制,所以创建drop数组一开始都为1,为了从第一行开始绘制,因此刚刷新的时候是从第一行绘制到最后一行。

context.fillText(text, i * fontSize, drops[i] * fontSize);

fillText() 有三个参数,第一个是绘制的随机文字,第二是文字的 X 起始坐标,三是文字的 Y 起始坐标。所以第一行每个文字的 X 轴坐标是 0, 18, 36...,而 Y 轴坐标为 18, 18, 18, ...。绘制第二行则将 drops 数组的值都加 1 ,即第二行的 Y 轴坐标为(32,32,32...)。依次类推,从第一行到最后一行就绘制了满屏的文字,通过 fillRect() 加上覆盖的黑色透明矩形,就有点类似实现了渐变阴影的文字向下运动动画。为了显示重复绘制的效果,需要设置当绘制到最后一行了,需要从第一行开始绘制:

if (drops[i] * fontSize > canvas.height ){
    drops[i] = 0;
}

但是这样我们会发现,每一行文字都是同时绘制的,没有差异性,这样不能达到不同列错落有致的效果。为了显示出每一行文字的差异性,就必须让一些列先从第一行开始绘制,一些列后面再从第一行开始绘制,怎么达到呢,可以增加随机数,同时判断随机数和是否绘制到了最后一行,同时满足的列才开始从第一行绘制:

if (drops[i] * fontSize > canvas.height && Math.random() > 0.99){
    drops[i] = 0;
}

ZG
2 声望0 粉丝