文章目录
  1. 1. Canvas特点
  2. 2. Canvas基本用法及注意点
  3. 3. 有趣的demo
  4. 4. 引用文献

Canvas API 可以用来动态生成和展示图形、图表、图像以及动画。最近用到了canvas,整理一下canvas的知识点。

Canvas特点

Canvas 本质上是一个位图画布,其上绘制的图形是不可缩放的,不能像SVG图像那样可以被放大缩小。此外,用canvas绘制出来的对象不属于页面DOM结构或者任何命名空间。SVG图像却可以在不同的分辨率下流畅地缩放,并且支持单击检测(能检测到鼠标点击了图像上的哪个点)。但是Cavas有两个优势: 首先,不需要将所有绘制图像中的每个图元当做对象存储,所以执行性能非常好;其次,在其他编程语言现有的优秀二维绘图API的基础上实现的Canvas API 比较简单。但是如果你的图像显示需要显著的交互行为,那么可以考虑使用SVG代替Canvas API。SVG也能用于绘制,而且它整合了浏览器的DOM。

Canvas基本用法及注意点

  1. 在使用canvas 时要先设置 width 和height属相,默认是宽为300,高为150。然后取得绘图上下文,即取得绘图上下文对象的引用。并且要进行检测getContext()是否存在。

    1
    2
    3
    4
    var canvas = document.getElementById("canvas");
    if(canvas.getContext){
    }

    最好在canvas 标签上直接设置width和height,若是使用css设置宽高属性,在某些情况下会导致图像扭曲。

  2. 在获取context之后,即可以进行绘制矩形、绘制路径、绘制文本、变换、绘制图像、阴影、渐变等功能。在绘制这些图形时用到最多的是context的fillStyle 和 strokeStyle属性。 这两个属性的值可以是字符串、渐变对象或模式对象。

  3. 渐变对象 context.createLinearGradient()、模式对象 context.createPattern() 可以赋值给 stroke、fill 这两个属性。

  4. 只有当对路径应用context 的stroke() 或者fill() 方法时,结果才能显现出来,否则在只有在显示图像、显示文本或者绘制、填充和清除矩形框的时候,canvas才会马上更新。

  5. 若是先stroke后fill , 填充会覆盖一部分描边路径。若设置路径宽度是4px,这个宽度是沿路径线居中对齐的,而填充是把路径轮廓内部所有像素全部填充,所以会覆盖描边路径的一半。若是想看到完整的路径,则可以先填充再描边。

  6. 不论开始绘制何种图形,第一个需要调用的就是context的beginPath(),用来通知canvas将要绘制一个新的图形了。对于canvas来说,beginPath()函数最大的用处是canvas需要据此来计算图形的内部和外部的范围,以便完成后续的stroke和fill。

  7. 在canvas 时插入图片时,必须等到图片完全加载后才能对其操作,浏览器通常会在页面脚本执行的同时异步加载图片。如果视图在图片未完全加载之前就将其呈现在canvas上,那么canvas将不会显示任何图片。

    1
    2
    3
    4
    5
    var img = new Image();
    img.src ="x.png";
    img.onload = function(){
    }

    使用canvas上的toDataURL()方法,可以导出在canvas元素上绘制的图像,参数表示图像的MIME类型格式。如果绘制到画布上的图像源自不同的域,toDataURL()会抛出错误。

  8. 关于可重用代码: 一般绘制都应从原点(坐标系的0,0点)开始,应用变换(缩放、平移、旋转),然后不断修改代码直到达到希望的效果。所以经常会用到 translate()、save()、 restore() 函数。

  9. 使用图像数据 可以通过context的getImageData()取得原始图像数据,返回的是ImageData 的实例。每个ImageData对象都有三个属性:width、height 和data。其中data属性是一个数组,保存着图像中每一个像素的数据。在data数组中,每一个像素用4个元素来保存,分别表示红、绿、蓝和透明度值。因此,第一个像素的数据就保存在数组的第0到第3个元素中,我们可以在直接访问到这些元素图像数据,并且能以各种方式来修改这些数据。修改之后使用context.putImageData()将图像数据绘制到画布上。

由于安全问题,在getImageData() 调用时,如果canvas中的图像来自其他域,就会抛出安全异常。因为在没有canvas API之前,无法使用编程的方式获取下载图片的像素信息,来自其他网站的私有图片可以显示在本地,但无法被读取或者复制。

  1. 两个会应用到context中所有绘制操作的属性:globalAlpha 和 globalCompositionOperation 。

有趣的demo

  1. 模拟热图的实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    canvas.onmousemove = function(e) {
    x = e.clientX - e.target.offsetLeft;
    y = e.clientY - e.target.offsetTop;
    addToPoint(x, y)
    }
    function getColor(intensity) {
    var colors = ["#072933", "#2E4045", "#8C593B", "#B2814E", "#FAC268", "#FAD237"];
    return colors[Math.floor(intensity / 2)];
    }
    function drawPoint(x, y, radius) {
    context.fillStyle = getColor(radius);
    radius = Math.sqrt(radius) * 6;
    context.beginPath();
    context.arc(x, y, radius, 0, Math.PI * 2, true)
    context.closePath();
    context.fill();
    }
    function addToPoint(x, y) {
    x = Math.floor(x / SCALE);
    y = Math.floor(y / SCALE);
    if (!points[[x, y]]) {
    points[[x, y]] = 1;
    } else if (points[[x, y]] == 10) {
    return
    } else {
    points[[x, y]]++;
    }
    drawPoint(x * SCALE, y * SCALE, points[[x, y]]);
    }

    用points对应的值去获取对应的颜色和绘制相应大小的图形。

  2. 模拟阴天闪电

    1
    2
    3
    4
    5
    6
    if (Math.random() > .01) {
    context.globalAlpha = 0.65; // 不透明度 越大越不透明
    context.fillStyle = '#000000';
    context.fillRect(0, 0, 400, 600);
    context.globalAlpha = 1.0;
    }

    大部分情况下天气较暗,用来模拟阴天,配合requestAnimationFrame 实现闪电

  3. 模拟下雨

    1
    2
    3
    4
    5
    6
    7
    context.fillStyle = context.createPattern(rain, 'repeat');
    var now = Date.now();
    context.save();
    context.translate(-256 + (0.1 * now) % 256, -256 + (0.5 * now) % 256);
    context.fillRect(0, 0, 400 + 256, 600 + 256);
    context.restore();

    用一张下雨的图片铺满,然后根据时间 来设置原点位置,并且使得画的矩形比下雨所需的矩形要大,以保证覆盖。配合requestAnimationFrame 实现下雨

  4. 模拟下雨与下雪

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    context.fillStyle = context.createPattern(rain, 'repeat');
    var now = Date.now();
    context.save();
    context.translate(-256 + (0.1 * now) % 256, -256 + (0.5 * now) % 256);
    context.fillRect(0, 0, 400 + 256, 600 + 256);
    context.restore();
    context.save();
    context.translate(-256 + (0.08 * now) % 256, -256 + (0.2 * now) % 256);
    context.fillRect(0, 0, 400 + 256, 600 + 256);
    context.restore();

下雨的同时下雪与只下雨的区别是下雪的速率比较小,下雨的速率比较大,所以设置两个不同的原点,同时绘制两个不同的图片,相互叠加,这样就能有雨雪的效果。
上面这几个例子都要配合requestAnimationFrame来实现动画。

引用文献

  1. 《HTML5高级程序设计》 第二章
  2. 《javascript 高级程序设计》
文章目录
  1. 1. Canvas特点
  2. 2. Canvas基本用法及注意点
  3. 3. 有趣的demo
  4. 4. 引用文献