zoukankan      html  css  js  c++  java
  • H5canvas基础

    本篇文章开始讲解HTML5的核心功能之一:Canvas

    通过Canvas可以动态生成和展示图形、图表、图像以及动画。

    Canvas API功能非常多,我们将讨论最常用的功能。

    我们先新建一个canvas看看。

    我们给canvas加一个边框,这样比较方便看。

    可以看到, canvas会创建一块矩形区域,默认情况下生成大小是300*150像素。

    在页面中加入canvas后,我们便可以通过js来自由地控制她。

    例如 添加图片、线条以及文字,也可以在里面绘图,甚至加入高级动画。

    Note

    把canvas当作一个普通的标签,可以通过应用CSS的方式来改变样式,而且一些CSS属性还可以被canvas内的元素继承。

    例如字体样式,在canvas内添加文字,其样式默认是同canvas元素本身是一样的。

    文章提纲

    • 要点
    • 理论基础/前置条件
    • 详细步骤
    • 总结

    要点

    掌握使用canvas API的重要流程

    掌握常用的canvas API:例如moveTo, lineTo, beginPath, closePath,stroke,fill等

    充分理解例子

    理论基础 -- canvas坐标

    如下图,canvas中的坐标是从左上角开始,x轴沿着水平方向(按像素)向右延伸,y轴沿垂直方向向下延伸。

    最左上角坐标为 (0,0) 的点为原点。

    详细步骤 -- 使用HTML5 canvas API

    检测浏览器支持情况

    我们做两件事

    1. 我们用一段script判断浏览器支持情况。

      如果不支持可以将提示信息显示在特定的位置。

      如下图,我们用了一个id="support"的div来显示提示升级的信息。

    2. 我们在canvas中写入一段替代内容.

      如下图,如果不支持,canvas会显示替代内容。

    浏览器支持

    把IE调成IE7模式测试下不支持的情况:

    利用canvas画一条对角线

    对上面的例子做一些修改

    在canvas中绘制一条对角线

    根据上面的js代码,归纳出使用canvas API的重要流程

    1. 根据canvas ID值获取canvas对象访问权,接着定义一个context变量,调用canvas对象的getContext方法。
    2. 基于这个context执行动作(这里是画一条对角线)
    3. 通过context.stroke()完成线条的绘制。

    Note

    这里有一个坑。我原来将设置canvas长宽放在了style里面。如下图。

    出现问题的原因:

    canvas的width和height是画布的实际宽度和高度,绘制的图形在这个画布上面。

    canvas的style的width和height是canvas在浏览器中被渲染的高度和宽度。

    因此需要注意设置宽度时要在外面设置。

    使用变换(transformation)画对角线

    下面来看canvas上绘制图像的另外一种方式:使用变换(transformation)。

    transformation是实现复杂canvas操作的最好方式(就单个上面绘制对角线来说看起来是更加复杂了点)

    理解 变换(transformation):

    把它当成是介于开发人员发出的指令和canvas显示结果之间的一个修正层 (modification layer)

    注意 不管在开发中是否使用变换,修正层始终存在。

    每个绘制操作的结果显示在canvas上之前都要经过修正层去修正。

    虽然这么做增加了额外的复杂性,但却为绘制系统添加了更为强大的功能。

    Note

    不在代码中调用变换函数并不意味着可以提升canvas的性能。

    canvas在执行的时候,变换会被呈现引擎隐式调用,这与开发人员是否直接调用无关。

    可重用代码的一条重要建议:

    一般绘制都应从原点开始,应用变换(缩放,平移,旋转等),然后不断修改代码直至达到希望的效果。

    示例

    这个代码的结果和上面是一模一样的。

    大家注意这两种代码的差别:

    对第二种方式, translate(70,140) 代表将原点移到 (70,140) 这个位置。

    也就是说,接下来所有操作都是相对于 (70,140) 这个位置来操作的。

    第一种情况是(70,140)à(140,70),

    第二种情况是(0,0)à(70,140)à(70,-70)

    第二种情况的(70, -70)是相对于新的原点(70,140)点来说的,相对于一开始的原点坐标是(70+70,-70+140),很容易看到这两种情况的结果是等价的,理解了吗?

    大家体会一下。

    我们归纳一下上面的操作:

    1. 根据canvas ID值获取canvas对象访问权,接着定义一个context变量,调用canvas对象的getContext方法。
    2. 保存尚未修改的context, 这样即使进行了绘制和变换操作,也可以恢复到初始状态(通过后面的restore函数)
    3. 通过translate来移动原点,这个上面已经解释过了。
    4. 基于移动过的context执行画线动作
    5. 通过context.stroke()完成线条的绘制。
    6. 最后,恢复context至原始状态,这样后续的canvas操作就不会被刚才的平移操作影响了。

    画树

    现在学习稍微复杂点的图形。

    前面绘制的一条对角线算是一条简单路径。

    实际上路径可以很复杂:多条线、曲线段、甚至是子路径。

    如果想在canvas上绘制任意形状,那么你需要重点关注路径API

    按照惯例,不论开始绘制何种图形,第一个需要调用的就是beginPath, 对于canvas来说,beginPath函数的最大用处是canvas需要据此来计算图形的内部和外部范围,以便完成后续的描边和填充。

    路径会跟踪当前坐标,默认值是原点。

    调用beginPath之后,就可以使用context的各种方法来绘制想要的形状了。

    到目前为止已经使用了几个简单的context路径函数。

    moveTo(x,y)

    lineTo(x,y)

    上面两个函数的区别是:moveTo就像是提起画笔,移动到新位置;

    而lineTo告诉canvas用画笔从纸上的旧坐标画条直线到新坐标。

    注意,不管调用的是哪一个,都不会真正画出图形,因为我们还没有调用stroke或者fill函数。

    目前我们只是定义路径的位置,以便后面绘制时使用。

    另外再介绍一个路径函数closePath, 这个函数和lineTo很像,唯一的差别是会将路径的起始坐标 自动作为 目标坐标。

    clothPath还会通知canvas当前绘制的图形已经完全闭合或者形成了完全封闭的区域,这对将来的填充和描边都非常有用。

    此时,可以在已有的路径中继续创建其他的子路径,或者随时调用beginPath重新绘制新路径并完全清除之前的所有路径。

    1.绘制树冠

    绘制树冠的函数

    为了直观的显示图线的走势,我画了个从开始点到顶点的草图,如下

    在canvas上画树的函数:

    最终结果如下

    下面我们对树冠做一些美化,在stroke之前添加如下代码

    变成了更粗更平滑的棕色线条。

    进一步美化,将闭合路径内部填充为绿色

    注意,右边的边框也变细了。

    当我们采用先描边后填充的方式,会填充一半的边框。

    如果要不填充边框,需要采用先填充后描边的方式,如下。

    2. 利用fillRect画树干(填充矩形区域)

    我们先把translate的数值改一下,让出树干的位置。

    context.translate(130,150);

    通过fillRect(x, y, 宽, 高)来画出树干。

    注意,这段要在context.restore();前面,否则画的位置就错了。

    最终结果:

    Note

    fillRect(x,y,width,height)

    总结

    大家初步可以看到canvas的威力,可以不用借助第三方技术进行绘图。

    当然目前画的东西还比较简单,下篇文章将会在这棵树的基础上加入其他元素和特殊效果,完成一幅雨水动画效果的林荫小道图。

    好了,今天就到这里,欢迎大家多多评论,让下一篇文章更好:)

     http://javascript.ruanyifeng.com/htmlapi/canvas.html

    目录

    概述

    Canvas API(画布)用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图(bitmap)。

    使用前,首先需要新建一个canvas网页元素。

    <canvas id="myCanvas" width="400" height="200">
      您的浏览器不支持canvas!
    </canvas>
    

    上面代码中,如果浏览器不支持这个API,则就会显示canvas标签中间的文字——“您的浏览器不支持canvas!”。

    每个canvas元素都有一个对应的context对象(上下文对象),Canvas API定义在这个context对象上面,所以需要获取这个对象,方法是使用getContext方法。

    var canvas = document.getElementById('myCanvas');
    
    if (canvas.getContext) {
      var ctx = canvas.getContext('2d');
    }
    

    上面代码中,getContext方法指定参数2d,表示该canvas对象用于生成2D图案(即平面图案)。如果参数是webgl,就表示用于生成3D图像(即立体图案),这部分实际上单独叫做WebGL API(本书不涉及)。

    绘图方法

    canvas画布提供了一个用来作图的平面空间,该空间的每个点都有自己的坐标,x表示横坐标,y表示竖坐标。原点(0, 0)位于图像左上角,x轴的正向是原点向右,y轴的正向是原点向下。

    (1)绘制路径

    beginPath方法表示开始绘制路径,moveTo(x, y)方法设置线段的起点,lineTo(x, y)方法设置线段的终点,stroke方法用来给透明的线段着色。

    ctx.beginPath(); // 开始路径绘制
    ctx.moveTo(20, 20); // 设置路径起点,坐标为(20,20)
    ctx.lineTo(200, 20); // 绘制一条到(200,20)的直线
    ctx.lineWidth = 1.0; // 设置线宽
    ctx.strokeStyle = "#CC0000"; // 设置线的颜色
    ctx.stroke(); // 进行线的着色,这时整条线才变得可见

    moveto和lineto方法可以多次使用。最后,还可以使用closePath方法,自动绘制一条当前点到起点的直线,形成一个封闭图形,省却使用一次lineto方法。

    (2)绘制矩形

    fillRect(x, y, width, height)方法用来绘制矩形,它的四个参数分别为矩形左上角顶点的x坐标、y坐标,以及矩形的宽和高。fillStyle属性用来设置矩形的填充色。

    ctx.fillStyle = 'yellow';
    ctx.fillRect(50, 50, 200, 100); 

    strokeRect方法与fillRect类似,用来绘制空心矩形。

    ctx.strokeRect(10,10,200,100); 

    clearRect方法用来清除某个矩形区域的内容。

    ctx.clearRect(100,50,50,50);  

    (3)绘制文本

    fillText(string, x, y) 用来绘制文本,它的三个参数分别为文本内容、起点的x坐标、y坐标。使用之前,需用font设置字体、大小、样式(写法类似与CSS的font属性)。与此类似的还有strokeText方法,用来添加空心字。

    // 设置字体
    ctx.font = "Bold 20px Arial"; 
    // 设置对齐方式
    ctx.textAlign = "left";
    // 设置填充颜色
    ctx.fillStyle = "#008600"; 
    // 设置字体内容,以及在画布上的位置
    ctx.fillText("Hello!", 10, 50); 
    // 绘制空心字
    ctx.strokeText("Hello!", 10, 100); 

    fillText方法不支持文本断行,即所有文本出现在一行内。所以,如果要生成多行文本,只有调用多次fillText方法。

    (4)绘制圆形和扇形

    arc方法用来绘制扇形。

    ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

    arc方法的x和y参数是圆心坐标,radius是半径,startAngle和endAngle则是扇形的起始角度和终止角度(以弧度表示),anticlockwise表示做图时应该逆时针画(true)还是顺时针画(false)。

    下面是如何绘制实心的圆形。

    ctx.beginPath(); 
    ctx.arc(60, 60, 50, 0, Math.PI*2, true); 
    ctx.fillStyle = "#000000"; 
    ctx.fill();

    绘制空心圆形的例子。

    ctx.beginPath(); 
    ctx.arc(60, 60, 50, 0, Math.PI*2, true); 
    ctx.lineWidth = 1.0; 
    ctx.strokeStyle = "#000"; 
    ctx.stroke();

    (5)设置渐变色

    createLinearGradient方法用来设置渐变色。

    var myGradient = ctx.createLinearGradient(0, 0, 0, 160); 
    
    myGradient.addColorStop(0, "#BABABA"); 
    
    myGradient.addColorStop(1, "#636363");

    createLinearGradient方法的参数是(x1, y1, x2, y2),其中x1和y1是起点坐标,x2和y2是终点坐标。通过不同的坐标值,可以生成从上至下、从左到右的渐变等等。

    使用方法如下:

    ctx.fillStyle = myGradient;
    ctx.fillRect(10,10,200,100);

    (6)设置阴影

    一系列与阴影相关的方法,可以用来设置阴影。

    ctx.shadowOffsetX = 10; // 设置水平位移
    ctx.shadowOffsetY = 10; // 设置垂直位移
    ctx.shadowBlur = 5; // 设置模糊度
    ctx.shadowColor = "rgba(0,0,0,0.5)"; // 设置阴影颜色
    
    ctx.fillStyle = "#CC0000"; 
    ctx.fillRect(10,10,200,100);

    图像处理方法

    drawImage方法

    canvas允许将图像文件插入画布,做法是读取图片后,使用drawImage方法在画布内进行重绘。

    var img = new Image();
    img.src = "image.png";
    ctx.drawImage(img, 0, 0); // 设置对应的图像对象,以及它在画布上的位置

    上面代码将一个PNG图像载入canvas。

    由于图像的载入需要时间,drawImage方法只能在图像完全载入后才能调用,因此上面的代码需要改写。

    var image = new Image(); 
    
    image.onload = function() { 
      var canvas = document.createElement("canvas");
    	canvas.width = image.width;
    	canvas.height = image.height;
    	canvas.getContext("2d").drawImage(image, 0, 0);
    
    	return canvas;
    } 
    
    image.src = "image.png";

    drawImage()方法接受三个参数,第一个参数是图像文件的DOM元素(即img标签),第二个和第三个参数是图像左上角在Canvas元素中的坐标,上例中的(0, 0)就表示将图像左上角放置在Canvas元素的左上角。

    getImageData方法,putImageData方法

    getImageData方法可以用来读取Canvas的内容,返回一个对象,包含了每个像素的信息。

    var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

    imageData对象有一个data属性,它的值是一个一维数组。该数组的值,依次是每个像素的红、绿、蓝、alpha通道值,因此该数组的长度等于 图像的像素宽度 x 图像的像素高度 x 4,每个值的范围是0–255。这个数组不仅可读,而且可写,因此通过操作这个数组的值,就可以达到操作图像的目的。修改这个数组以后,使用putImageData方法将数组内容重新绘制在Canvas上。

    context.putImageData(imageData, 0, 0);

    toDataURL方法

    对图像数据做出修改以后,可以使用toDataURL方法,将Canvas数据重新转化成一般的图像文件形式。

    function convertCanvasToImage(canvas) {
      var image = new Image();
      image.src = canvas.toDataURL("image/png");
      return image;
    }

    上面的代码将Canvas数据,转化成PNG data URI。

    save方法,restore方法

    save方法用于保存上下文环境,restore方法用于恢复到上一次保存的上下文环境。

    ctx.save(); 
    
    ctx.shadowOffsetX = 10;
    ctx.shadowOffsetY = 10;
    ctx.shadowBlur = 5;
    ctx.shadowColor = "rgba(0,0,0,0.5)";
    
    ctx.fillStyle = "#CC0000";
    ctx.fillRect(10,10,150,100);
    
    ctx.restore(); 
    
    ctx.fillStyle = "#000000";
    ctx.fillRect(180,10,150,100); 

    上面代码先用save方法,保存了当前设置,然后绘制了一个有阴影的矩形。接着,使用restore方法,恢复了保存前的设置,绘制了一个没有阴影的矩形。

    动画

    利用JavaScript,可以在canvas元素上很容易地产生动画效果。

    
    var posX = 20,
        posY = 100;
    
    setInterval(function() {
    	context.fillStyle = "black";
        context.fillRect(0,0,canvas.width, canvas.height);
    
    	posX += 1;
    	posY += 0.25;
    
    	context.beginPath();
    	context.fillStyle = "white";
    
    	context.arc(posX, posY, 10, 0, Math.PI*2, true); 
    	context.closePath();
    	context.fill();
    }, 30);
    
    

    上面代码会产生一个小圆点,每隔30毫秒就向右下方移动的效果。setInterval函数的一开始,之所以要将画布重新渲染黑色底色,是为了抹去上一步的小圆点。

    通过设置圆心坐标,可以产生各种运动轨迹。

    先上升后下降。

    
    var vx = 10,
        vy = -10,
        gravity = 1;
    
    setInterval(function() {
        posX += vx;
        posY += vy;
        vy += gravity;
    	// ...
    });
    
    

    上面代码中,x坐标始终增大,表示持续向右运动。y坐标先变小,然后在重力作用下,不断增大,表示先上升后下降。

    小球不断反弹后,逐步趋于静止。

    
    var vx = 10,
        vy = -10,
        gravity = 1;
    
    setInterval(function() {
        posX += vx;
        posY += vy;
    
    	if (posY > canvas.height * 0.75) {
              vy *= -0.6;
              vx *= 0.75;
              posY = canvas.height * 0.75;
        }
    	
        vy += gravity;
    	// ...
    });
    
    

    上面代码表示,一旦小球的y坐标处于屏幕下方75%的位置,向x轴移动的速度变为原来的75%,而向y轴反弹上一次反弹高度的40%。

    像素处理

    通过getImageData方法和putImageData方法,可以处理每个像素,进而操作图像内容。

    假定filter是一个处理像素的函数,那么整个对Canvas的处理流程,可以用下面的代码表示。

    if (canvas.width > 0 && canvas.height > 0) {
    
    	var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
    
        filter(imageData);
    
        context.putImageData(imageData, 0, 0);
    
    }

    以下是几种常见的处理方法。

    灰度效果

    灰度图(grayscale)就是取红、绿、蓝三个像素值的算术平均值,这实际上将图像转成了黑白形式。假定d[i]是像素数组中一个象素的红色值,则d[i+1]为绿色值,d[i+2]为蓝色值,d[i+3]就是alpha通道值。转成灰度的算法,就是将红、绿、蓝三个值相加后除以3,再将结果写回数组。

    grayscale = function (pixels) {
    
    	var d = pixels.data;
    
        for (var i = 0; i < d.length; i += 4) {
          var r = d[i];
          var g = d[i + 1];
          var b = d[i + 2];
          d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3;
        }
    
        return pixels;
    
    };

    复古效果

    复古效果(sepia)则是将红、绿、蓝三个像素,分别取这三个值的某种加权平均值,使得图像有一种古旧的效果。

    sepia = function (pixels) {
    
        var d = pixels.data;
    
        for (var i = 0; i < d.length; i += 4) {
          var r = d[i];
          var g = d[i + 1];
          var b = d[i + 2];
          d[i]     = (r * 0.393)+(g * 0.769)+(b * 0.189); // red
          d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green
          d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue
        }
    
        return pixels;
    
    };

    红色蒙版效果

    红色蒙版指的是,让图像呈现一种偏红的效果。算法是将红色通道设为红、绿、蓝三个值的平均值,而将绿色通道和蓝色通道都设为0。

    red = function (pixels) {
    	
        var d = pixels.data;
    
        for (var i = 0; i < d.length; i += 4) {
          var r = d[i];
          var g = d[i + 1];
          var b = d[i + 2];
          d[i] = (r+g+b)/3;        // 红色通道取平均值
          d[i + 1] = d[i + 2] = 0; // 绿色通道和蓝色通道都设为0
        }
    
        return pixels;
    
    };

    亮度效果

    亮度效果(brightness)是指让图像变得更亮或更暗。算法将红色通道、绿色通道、蓝色通道,同时加上一个正值或负值。

    brightness = function (pixels, delta) {
    
        var d = pixels.data;
    
        for (var i = 0; i < d.length; i += 4) {
              d[i] += delta;     // red
              d[i + 1] += delta; // green
              d[i + 2] += delta; // blue   
        }
    
    	return pixels;
    
    };

    反转效果

    反转效果(invert)是指图片呈现一种色彩颠倒的效果。算法为红、绿、蓝通道都取各自的相反值(255-原值)。

    invert = function (pixels) {
    
    	var d = pixels.data;
    
    	for (var i = 0; i < d.length; i += 4) {
    		d[i] = 255 - d[i];
    		d[i+1] = 255 - d[i + 1];
    		d[i+2] = 255 - d[i + 2];
    	}
    
    	return pixels;
    
    };
  • 相关阅读:
    State模式
    Visitor模式,Decorator模式,Extension Object模式
    系统报错undefine not symbol armv7
    decompressedResponseImageOfSize:completionHandler:]_block_invoke
    App Transport Security has blocked a cleartext HTTP
    UIButton 左对齐 省略号最右边
    ActiveAndroid问题no such table解决总结
    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'
    fun下载内容批量收集
    The `brew link` step did not complete successfully
  • 原文地址:https://www.cnblogs.com/coolslider/p/5227544.html
Copyright © 2011-2022 走看看