【基础系列】Canvas专题

1 Canvas接口元素定义

1.1 getContext()方法

        为了在canvas上绘制,你必须先得到一个画布上下文对象的引用,用本方法即可完成这一操作,格式如下:

context = canvas . getContext(contextId)

        方法返回一个指定contextId的上下文对象,如果指定的id不被支持,则返回null,当前唯一被强制必须支持的是“2d”,也许在将来会有“3d”,注意,指定的id是大小写敏感的。

1.2 toDataURL()方法

        返回一张使用canvas绘制的图片,返回值符合data:URL格式,格式如下:

url = canvas . toDataURL( [ type, ... ])

        规范规定,在未指定返回图片类型时,返回的图片格式必须为PNG格式,如果canvas没有任何像素,则返回值为:“data:,”,这是最短的data:URL,在text/plain资源中表现为空字符串。type的可以在image/png,image/jpeg,image/svg+xml等 MIME类型中选择。如果是image/jpeg,可以有第二个参数,如果第二个参数的值在0-1之间,则表示JPEG的质量等级,否则使用浏览器内置默认质量等级。

        下面的代码可以从ID为codeex的canvas中取得绘制内容,并作为DataURL传递给img元素,并显示。

var canvas =  document.getElementById('codeex');

var url = canvas.toDataURL();

//idmyimg的图片元素

myimg.src = url;

2 二维绘图上下文

        当使用一个canvas元素的getContext(“2d”)方法时,返回的是CanvasRenderingContext2D对象,其内部表现为笛卡尔平面坐标,并且左上角坐标为(0,0),在本平面中往右则x坐标增加和往下方y坐标增加。每一个canvas元素仅有一个上下文对象。其接口如下:

interface  CanvasRenderingContext2D {

// back-reference to the canvas

    readonly attribute HTMLCanvasElement  canvas;

// state

    void restore(); // pop state stack and  restore state

    void save(); // push state on state stack

// transformations (default transform is the identity matrix)

    void rotate(in float angle);

    void scale(in float x, in float y);

    void setTransform(in float m11, in float  m12, in float m21, in float m22, in float dx, in float dy);

    void transform(in float m11, in float  m12, in float m21, in float m22, in float dx, in float dy);

    void translate(in float x, in float y);

// compositing

    attribute float globalAlpha; // (default  1.0)

    attribute DOMString  globalCompositeOperation; // (default source-over)

// colors and styles

attribute  any fillStyle; // (default black)

    attribute any strokeStyle; // (default  black)

    CanvasGradient createLinearGradient(in float  x0, in float y0, in float x1, in float y1);

    CanvasGradient createRadialGradient(in  float x0, in float y0, in float r0, in float x1, in float y1, in float r1);

    CanvasPattern createPattern(in  HTMLImageElement image, in DOMString repetition);

    CanvasPattern  createPattern(in HTMLCanvasElement image, in DOMString repetition);

    CanvasPattern createPattern(in  HTMLVideoElement image, in DOMString repetition);

// line styles

    attribute DOMString lineCap; //  "butt", "round", "square" (default  "butt")

    attribute DOMString lineJoin; //  "miter", "round", "bevel" (default  "miter")

    attribute float lineWidth; // (default 1)

    attribute float miterLimit; // (default  10)

// shadows

    attribute float shadowBlur; // (default  0)

    attribute DOMString shadowColor; //  (default transparent black)

    attribute float shadowOffsetX; //  (default 0)

    attribute float shadowOffsetY; //  (default 0)

// rects

    void clearRect(in float x, in float y, in  float w, in float h);

    void fillRect(in float x, in float y, in  float w, in float h);

    void strokeRect(in float x, in float y,  in float w, in float h);

// Complex shapes (paths) API

    void arc(in float x, in float y, in float  radius, in float startAngle,      in  float endAngle, in boolean anticlockwise);

    void arcTo(in float x1, in float y1, in  float x2, in float y2, in float radius);

    void beginPath();

    void bezierCurveTo(in float cp1x, in  float cp1y, in float cp2x, in float cp2y, in float x, in float y);

    void clip();

    void closePath();

    void fill();

    void lineTo(in float x, in float y);

    void moveTo(in float x, in float y);

    void quadraticCurveTo(in float cpx, in  float cpy, in float x, infloat y);

    void rect(in float x, in float y, in float  w, in float h);

    void stroke();

    boolean isPointInPath(in float x, in  float y);

// text

    attribute DOMString font; // (default  10px sans-serif)

    attribute DOMString textAlign; //  "start", "end", "left", "right",  "center" (default: "start")

    attribute DOMString textBaseline; //  "top", "hanging", "middle",  "alphabetic", "ideographic", "bottom" (default:  "alphabetic")

    void fillText(in DOMString text, in float  x, in float y, optional in    float  maxWidth);

    TextMetrics measureText(in DOMString  text);

    void strokeText(in DOMString text, in  float x, in float y, optional in float maxWidth);

// drawing images

    void drawImage(in HTMLImageElement image,  in float dx, in float dy, optional in float dw, in float dh);

    void drawImage(in HTMLImageElement image,  in float sx, in float sy, in float sw, in float sh, in float dx, in float dy,  in float dw, in float dh);

    void drawImage(in HTMLCanvasElement  image, in float dx, in float dy, optional in float dw, in float dh);

    void drawImage(in HTMLCanvasElement  image, in float sx, in float sy, in float sw, in float sh, in float dx, in  float dy, in float dw, in float dh);

    void drawImage(in HTMLVideoElement image,  in float dx, in float dy, optional in float dw, in float dh);

    void drawImage(in HTMLVideoElement image,  in float sx, in float sy, in float sw, in float sh, in float dx, in float dy,  in float dw, in float dh);

// pixel manipulation

    ImageData createImageData(in float sw, in  float sh);

    ImageData createImageData(in ImageData  imagedata);

    ImageData getImageData(in float sx, in  float sy, in float sw, in float sh);

    void putImageData(in ImageData imagedata,  in float dx, in float dy, optional in float dirtyX, in float dirtyY, in float  dirtyWidth, in float dirtyHeight);

};

interface CanvasGradient {

// opaque object

    void addColorStop(in float offset, in  DOMString color);

};

interface CanvasPattern {

// opaque object

};

interface TextMetrics {

    readonly attribute float width;

};

interface ImageData {

    readonly attribute CanvasPixelArray data;

    readonly attribute unsigned long height;

    readonly attribute unsigned long width;

};

interface CanvasPixelArray {

    readonly attribute unsigned long length;

    getter octet (in unsigned long index);

    setter void (in unsigned long index, in  octet value);

};

2.1 canvas的状态

    每个上下文都包含一个绘图状态的堆,绘图状态包含下列内容:

    1、当前的transformation matrix.

    2、当前的clipping region.

    3、当前的属性值:fillStyle, font, globalAlpha, globalCompositeOperation, lineCap, lineJoin, lineWidth, miterLimit, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, strokeStyle, textAlign, textBaseline

        注:当前path和当前bitmap不是绘图状态的一部分,当前path是持久存在的,仅能被beginPath()复位,当前bitmap是canvas的属性,而非绘图上下文。

context . restore() //弹出堆最上面保存的绘图状态

context . save() //在绘图状态堆上保存当前绘图状态

        绘图状态可以看作当前画面应用的所有样式和变形的一个快照。而状态的应用则可以避免绘图代码的过度膨胀。

2.1.1 context . restore()

2.1.2 context . save()

2.2 转换(Transformations)

        当建立形状和路径时,转换矩阵被应用到其坐标上。转换的执行顺序是严格按顺序的(注:原文是反向,经试验应该是按调用顺序的)。

        在做转换/变形之前先保存状态是一个良好的习惯。大多数情况下,调用restore 方法比手动恢复原先的状态要简单得多。

2.2.1 context . rotate(angle)

context . rotate(angle) //按给定的弧度旋转,按顺时针旋转

rotate方法旋转的中心始终是canvas的原点,如果要改变它,需要使用translate方法。

2.2.2 context .scale(x, y)

        context . scale(x, y) //按给定的缩放倍率缩放,1.0,为不变,参数比1.0小表示缩小,否则表示放大。默认情况下,canvas的1 单位就是1 个像素。举例说,如果我们设置缩放因子是0.5,1个单位就变成对应0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为2.0 时,1个单位就对应变成了2 像素,绘制的结果就是图形放大了2倍。

2.2.3 context . setTransform(m11, m12, m21, m22, dx, dy)

context . setTransform(m11, m12, m21, m22, dx, dy) //重设当前的转换

2.2.4 context . transform(m11, m12, m21, m22, dx, dy)

context . transform(m11, m12, m21, m22, dx, dy)

//矩阵变换,结果等于当前的变形矩阵乘上

m11 m21 dx

m12 m22 dy

0      0       1

后的结果

2.2.5 context . translate(x, y)

context . translate(x, y) //可以理解为偏移,向x,y方向偏移指定的量,其用来移动Canvas的原点到一个指定的值

2.2.5.1 示例

        下面是一个利用translate方法进行绘制螺旋图案的例子:

//绘制螺旋图案的函数

function drawSpirograph(ctx,R,r,O){

    var x1 = R-O;

    var y1 = 0;

    var i = 1;

    ctx.beginPath();

    ctx.moveTo(x1,y1);

    do {

        if (i>20000) break;

        var x2 = (R+r)*Math.cos(i*Math.PI/72)  - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))

        var y2 = (R+r)*Math.sin(i*Math.PI/72)  - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))

        ctx.lineTo(x2,y2);

        x1 = x2;

        y1 = y2;

        i++;

    } while (x2 != R-O && y2 != 0 );

    ctx.stroke();

}

//调用部分代码

context.fillRect(0,0,300,300);

for (var i=0;i<3;i++) {

    for (var j=0;j<3;j++) {

        context.save();

        context.strokeStyle =  "#9CFF00";

        context.translate(50+j*100,50+i*100);

        drawSpirograph(context,20*(j+2)/(j+1),-8*(i+3)/(i+1),10);

        context.restore();

    }

}

2.3 合成(Compositing)

2.3.1 context . globalAlpha

context . globalAlpha [ = value ] //0-1.0之间的数据,设定图像的透明度

2.3.2 context . globalCompositeOperation

context . globalCompositeOperation [ = value ]

//设定重叠图像的覆盖方式,可以设定为(注,值大小写敏感):

注意:下面所有图例中,B(蓝色方块)是先绘制的,即“已有的canvas 内容”,A(红色圆形)是后面绘制,即“新图形”。

2.4  颜色和风格

2.4.1 context. fillStyle

context . fillStyle [ = value ] //返回填充形状的当前风格,能被设置以用来改变当前的填充风格,其值可以是CSS颜色字串,也可以是CanvasGradient或者CanvasPattern对象,非法的值将被忽略。

2.4.2 context. strokeStyle

context . strokeStyle [ = value ] //返回当前描绘形状的风格,能被设置,其值同上。

        设置Javascript例子如下:

context.strokeStyle="#99cc33";

context.fillStyle='rgba(50,0,0,0.7)';

context.lineWidth=10;

context.fillRect(20,20,100,100);

context.strokeRect(20,20,100,100);

        绘制的图形如下所示。

2.4.3 gradient . addColorStop(offset, color)

gradient . addColorStop(offset, color) //在给定偏移的地方增加一个渐变颜色点,偏移量取值范围为0-1.0之间,否则产生一个INDEX_SIZE_ERR的异常,color为DOM字符串,如果不能解析,则抛出一个SYNTAX_ERR的异常。

2.4.4 context . createLinearGradient(x0, y0, x1, y1)

gradient = context . createLinearGradient(x0, y0, x1, y1) //建立一个线性渐变,如果任何一个参数不是有限值,则抛出一个NOT_SUPPORTED_ERR的异常。

2.4.4.1 addColorStop()

    定义和用法

        addColorStop()方法规定 gradient 对象中的颜色和位置。

        addColorStop()方法与 createLinearGradient()createRadialGradient() 一起使用。

        注释:您可以多次调用addColorStop() 方法来改变渐变。如果您不对 gradient 对象使用该方法,那么渐变将不可见。为了获得可见的渐变,您需要创建至少一个色标。

    JavaScript语法:    

gradient.addColorStop(stopcolor);

    参数值

        参数描述

        stop介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置。

        color在结束位置显示的 CSS 颜色值

2.4.4.2 示例1

        设置Javascript例子如下:

var  gradient = context.createLinearGradient(0, 2, 420, 2);

gradient.addColorStop(0, 'rgba(200, 0, 0, 0.8)');

gradient.addColorStop(0.5, 'rgba(0, 200, 0, 0.7)');

gradient.addColorStop(1, 'rgba(200, 0, 200, 0.9)');

context.strokeStyle = "#99cc33";

context.fillStyle= gradient; //copyright codeex.cn

context.lineWidth = 10;

context.fillRect(20, 20, 400, 100);

context.strokeRect(20, 20, 400, 100);

        绘制的图形如下所示。

2.4.4.3 示例——通过多个addColorStop() 方法来定义渐变

        通过多个 addColorStop() 方法来定义渐变:

var c=document.getElementById("myCanvas");

var ctx=c.getContext("2d");

var grd=ctx.createLinearGradient(0,0,170,0);

grd.addColorStop(0,"black");

grd.addColorStop("0.3","magenta");

grd.addColorStop("0.5","blue");

grd.addColorStop("0.6","green");

grd.addColorStop("0.8","yellow");

grd.addColorStop(1,"red");

ctx.fillStyle=grd;

ctx.fillRect(20,20,150,100);

2.4.5 context. createRadialGradient(x0, y0, r0, x1, y1, r1)

gradient = context . createRadialGradient(x0, y0, r0, x1, y1, r1) //建立一个径向渐变,如果任何一个参数不是有限值,则抛出一个NOT_SUPPORTED_ERR的异常。假如r0或r1为负值,则抛出INDEX_SIZE_ERR的异常。

2.4.5.1 绘制过程

        createRadialGradient(x0,y0,r0,x1,y1,r1)方法有六个参数,前三个参数表示开始的圆,其圆点在(x0,y0),半径为r0,后三个表示结束的圆,参数意义同上。其绘制过程如下:

    1. 如果起始圆和结束圆重叠,则不绘制任何东西,并终止步骤;

    2. x(w) = (x1-x0)w + x0 y(w) = (y1-y0)w + y0 r(w) =(r1-r0)w + r0在以(x(w),y(w))为圆点,r(w)为半径的圆周上所有点的颜色均为Color(w)。

    3. 对于任意的w取值(-∞ -- +∞),确保r(w)>0,总是可以知道画布中已知点的颜色。

        言而总之:这个效果就是建立一个圆锥体(手电筒效果)渲染效果,圆锥体的开始圆使用开始颜色偏移量为0,圆锥体的结束圆使用颜色偏移量为1.0,面积外的颜色均使用透明黑。

2.4.5.2 示例

        设置Javascript例子如下:

var gradient  = context.createRadialGradient (100,100, 20, 300,300,80);

gradient.addColorStop(0,'rgba(200,0,0,0.8)');

gradient.addColorStop(1,'rgba(200,0,200,0.9)');

context.strokeStyle="#99cc33";

context.fillStyle= gradient;    //'rgba(50,0,0,0.7)';

context.lineWidth=10;

context.fillRect(10,10,400,400);

context.strokeRect(10,10,400,400);

绘制的图形如下所示。

2.4.6 context . createPattern(image, repetition)

        上面提到可以作为渲染风格还有图案对象:CanvasPattern,其调用格式如下:

pattern = context . createPattern(image, repetition)

        本方法用指定的图像和重复方向建立一个画布图案对象,image参数可以为img,canvas,video元素中的任一个,如果不满足此条件,则抛出TYPE_MISMATCH_ERR异常,如果图片编码未知或没有图像数据,则抛出INVALID_STATE_ERR异常;第二个参数可以是下列值:

    repeat    默认参数,如果为空,则为此参数,表示两个方向重复

    repeat-x    仅水平重复

    repeat-y    仅垂直重复

    no-repeat    不重复

        如果image参数是一个HTMLImageElement对象,但对象的complete属性是false,则执行时抛出INVLAID_STATE_ERR异常;

        如果image参数是一个HTMLVideoElement对象,但其readyState属性是HAVE_NOTHING或HAVE_METADATA,则执行时抛出抛出INVLAID_STATE_ERR异常;

        如果image参数是一个HTMLCanvasElement对象,但其width属性或height属性是0,则执行时抛出抛出INVLAID_STATE_ERR异常。

        图案的绘制时从左上角开始的,根据不同的参数进行重复绘制。如果传递的图片是动画,则选取海报或第一帧作为其绘制图案源,如果使用HTMLVideoElement为对象,则当前播放位置帧被作为图案源。

2.4.6.1 示例

        设置HTML的核心代码如下:

        设置Javascript例子如下:

var imgSrc = document.getElementById('psrc')

var pattern =  context.createPattern(imgSrc,'repeat');

context.strokeStyle="#99cc33";

context.fillStyle= pattern;//by codeex.cn

context.lineWidth=10;

context.fillRect(10,10,200,220);

context.strokeRect(10,10,200,220);

        在IE9中的显示效果如图所示:

2.5 线风格

2.5.1 context . lineCap

        操作线风格的方法有4个,格式如下:

context . lineCap [ = value ] //返回或设置线段的箭头样式,仅有三个选项:butt(默认值),round,square;其他值忽略

2.5.2 context . lineJoin

context . lineJoin [ = value ] ///返回或设置线段的连接方式,仅有三个选项:miter(默认值),round,bevel;其他值忽略

2.5.3 context . lineWidth

context . lineWidth [ = value ] //返回或设置线段的线宽,非大于0的值被忽略;默认值为1.0;

2.5.4 context . miterLimit

context . miterLimit [ = value ] //返回或设置线段的连接处的斜率,非大于0的值被忽略;默认值为10.0。本属性翻译不够准确,请参看英文部分

2.5.5 线宽

        线宽是指给定路径的中心到两边的粗细。换句话说就是在路径的两边各绘制线宽的一半。因为画布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。

        想要获得精确的线条,必须对线条是如何描绘出来的有所理解。见下图,用网格来代表canvas 的坐标格,每一格对应屏幕上一个像素点。在第一个图中,填充了 (2,1) 至 (5,5) 的矩形,整个区域的边界刚好落在像素边缘上,这样就可以得到的矩形有着清晰的边缘。

        如果你想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0的线条,你会得到像第二幅图一样的结果。实际填充区域(深蓝色部分)仅仅延伸至路径两旁各一半像素。而这半个像素又会以近似的方式进行渲染,这意味着那些像素只是部分着色,结果就是以实际笔触颜色一半色调的颜色来填充整个区域(浅蓝和深蓝的部分)。

        要解决这个问题,你必须对路径施以更加精确的控制。已知粗 1.0 的线条会在路径两边各延伸半像素,那么像第三幅图那样绘制从(3.5,1) 到 (3.5,5) 的线条,其边缘正好落在像素边界,填充出来就是准确的宽为1.0 的线条。

        对于那些宽度为偶数的线条,每一边的像素数都是整数,那么你想要其路径是落在像素点之间(如那从(3,1) 到 (3,5)) 而不是在像素点的中间。如果不是的话,端点上同样会出现半渲染的像素点。

2.6 阴影(Shadows)

        有关阴影的四个全局属性将影响所有的绘画操作。有关定义如下:

context . shadowBlur [ = value ] //返回或设置阴影模糊等级,非大于等于0的值被忽略;

context . shadowColor [ = value ] //返回或设置阴影颜色

context . shadowOffsetX [ = value ]

context . shadowOffsetY [ = value ] //返回或设置阴影的偏移量

        注意:上面的值均不受坐标转换的影响,可以看做是绝对值。

        在上面的例子中增加下列语句,可以得到设置阴影的图像:

context.shadowBlur=7;

context.shadowColor='rgb(200,0,0)';

context.shadowOffsetX = 3;

context.shadowOffsetY=3;

2.7 简单形状(矩形)

        形状的绘制不影响当前路径(path),形状是剪切区域的主题,也是阴影(Shadow)效果,全局透明(alpha),全局组合(composition)操作等的主题。其由下面三个方法来进行简单的操作:

2.7.1 context. clearRect(x, y, w, h)

context. clearRect(x, y, w, h) \\在给定的矩形内清除所有的像素为透明黑(transparentblack)

2.7.2 context. fillRect(x, y, w, h)

context. fillRect(x, y, w, h) //用当前的填充风格填充给定的区域

2.7.3 context . strokeRect(x, y, w, h)

context. strokeRect(x, y, w, h) //使用当前给定的线风格,绘制一个盒子区域,影响其绘制风格的有:strokeStyle,lineWidth,lineJoin,miterLimit(可能)。

2.8 复杂形状(路径-paths)

        绘图上下文总有一个当前路径,并且是仅此一个,它不是绘图状态的一部分。

        一个路径有0个或多个子路径列表。每个子路径包含一个或多个点列表(这些点组成直的或弯曲的线段),和一个标识子路径是否闭合的标志。少于两个点的子路径在绘图时被忽略。操作这些形状的方法稍微多些,如下所示:

        默认情况下,图形上下文的路径有0个子路径。

2.8.1 路径起始函数

    调用格式:

context . beginPath() //清空子路径

context . closePath() //闭合路径

        方法概述:

2.8.1.1 context. beginPath()

beginPath方法重设绘图上下文的子路径列表,并清空所有的子路径。

2.8.1.2 context. closePath()

        ClosePath方法在绘图上下文如果没有子路径时,什么也不做;否则,它先把最后一个子路径标示为闭合,然后建立一个包含最后子路径的第一个点的子路径,并加入到绘图上下文。有点拗口,其一般可以看为,假如最后一个子路径,我们命名为spN,假设spN有多个点,则用直线连接spN的最后一个点和第一个点,然后关闭此路径和moveTo到第一个点。

2.8.2 绘制函数

        调用格式:

context . stroke()

context . fill()

context . clip()

        方法概述:

2.8.2.1 context. stroke()

        stroke方法使用lineWidth,lineCap,lineJoin,以及strokeStyle对所有的子路径进行填充。

2.8.2.2 context. fill()

        fill方法使用fillStyle方式填充子路径,未闭合的子路径在填充式按照闭合方式填充,但并不影响实际的子路径集合。

2.8.2.3 context. clip()

        clip方法使用计算所有的子路径而建立新的剪切区域,未闭合的子路径在填充式按照闭合方式填充,但并不影响实际的子路径集合,新的剪切区域将替换当前的剪切区域。

2.8.3 剪切(clip)

        裁切路径和普通的 canvas 图形差不多,不同的是它的作用是遮罩,用来隐藏没有遮罩的部分。如下图所示。红边五角星就是裁切路径,所有在路径以外的部分都不会在canvas 上绘制出来。

        如果和上面介绍的 globalCompositeOperation 属性作一比较,它可以实现与source-in 和 source-atop 差不多的效果。最重要的区别是裁切路径不会在canvas 上绘制东西,而且它永远不受新图形的影响。这些特性使得它在特定区域里绘制图形时相当好用。

2.8.3.1 随机星星示例

        效果图:

        首先,我画了一个与 canvas 一样大小的黑色方形作为背景,然后移动原点至中心点。然后用 clip方法创建一个弧形的裁切路径。裁切路径也属于 canvas 状态的一部分,可以被保存起来。如果我们在创建新裁切路径时想保留原来的裁切路径,我们需要做的就是保存一下canvas 的状态。

        裁切路径创建之后所有出现在它里面的东西才会画出来。在画线性渐变时这个就更加明显了。然后在随机位置绘制50 大小不一(经过缩放)的颗,当然也只有在裁切路径里面的星星才会绘制出来。

        代码如下:

function draw()  {

    var ctx = document.getElementById('canvas').getContext('2d');

    ctx.fillRect(0,0,150,150);

    ctx.translate(75,75);

// Create a  circular clipping path

    ctx.beginPath();

    ctx.arc(0,0,60,0,Math.PI*2,true);

    ctx.clip();

// draw  background

    var lingrad = ctx.createLinearGradient(0,-75,0,75);

    lingrad.addColorStop(0, '#232256');

    lingrad.addColorStop(1, '#143778');

    ctx.fillStyle = lingrad;

    ctx.fillRect(-75,-75,150,150);

// draw stars

    for (var j=1;j<50;j++){

        ctx.save();

        ctx.fillStyle = '#fff';

        ctx.translate(75-Math.floor(Math.random()*150),  75-Math.floor (Math. random () *150));

        drawStar(ctx,Math.floor(Math.random()*4)+2);

        ctx.restore();

    }

}

function  drawStar(ctx,r){

    ctx.save();

    ctx.beginPath()

    ctx.moveTo(r,0);

    for (var i=0;i<9;i++){

        ctx.rotate(Math.PI/5);

        if(i%2 == 0) {

            ctx.lineTo((r/0.525731)*0.200811,0);

        } else {

            ctx.lineTo(r,0);

        }

    }

    ctx.closePath();

    ctx.fill();

    ctx.restore();

}

2.8.4 辅助方法—context. isPointInPath(x, y)

        调用格式:

context . isPointInPath(x, y)

        方法概述:

        给定的坐标(x,y)是否在当前路径中,坐标(x,y)为绘图坐标系坐标,并不受转换的影响。

2.8.5 moveTo方法

    调用格式:

context . moveTo(x, y)

    方法概述:

        建立新的子路径,并制定其第一个点为(x,y)。

2.8.6 lineTo方法

    调用格式:

context . lineTo(x, y)

    方法概述:

        如果绘图上下文没有子路径,则其等同于moveTo(x,y),否则,其建立一条在子路径最后一个点到给定点的直线,并增加(x,y)到子路径中。

2.8.7 rect方法

    调用格式:

context . rect(x, y, w, h)

    方法概述:

        本方法建立二个新的子路径,第一个子路径包含四个点:(x,y),(x+w,y),(x+w,y+h),(x,y+h),四个点的连接方式为直线,该子路径被标示为闭合路径;最后再增加一个子路径,其仅有一个点(x,y)。

2.8.8 圆弧context. arc(x, y, radius, startAngle, endAngle, anticlockwise)

    方法调用格式:

context . arc(x, y, radius, startAngle,endAngle, anticlockwise)

    方法概述:

        本方法先增加一条直线到子路径列表,然后增加一个圆弧子路径到子路径列表。直线子路径是由上一个点到圆弧子路径的起始点,而圆弧则为按照给定的开始角度、结束角度和半径描述的按照给定的方向[布尔类型,anticlockwise-逆时针(true)]圆弧上;假如半径为负值,抛出INDEX_SIZE_ERR的异常;

    JS代码:

context.beginPath();

context.moveTo(100,50);

context.arc(250,50,50,1.5708,3.14,true);

context.stroke();

注释掉moveTo语句,则仅仅绘制圆弧:

2.8.9 最短圆弧context. arcTo(x1, y1, x2, y2, radius)

    方法调用格式:

context . arcTo(x1, y1, x2, y2, radius)

    方法概述:

        本方法绘制出子路径最后一个点(x0,y0)和(x1,y1)以及(x1,y1)和(x2,y2)构成的两条直线间半径为radius的最短弧线,并用直线连接(x0,y0);假如半径为负值,抛出INDEX_SIZE_ERR的异常;

        如图所示,绘制曲线由1,开始绘制。

        JS代码如下:

context.beginPath();

context.moveTo(150,50);

context.arcTo(200,50,100,200,20);

context.arcTo(200,50,100,200,40);

context.arcTo(200,50,100,200,80);

context.arcTo(200,50,100,200,120);

context.arcTo(200,50,100,200,160);

context.stroke();

2.8.10 二次方、三次方贝塞尔曲线

        贝塞尔曲线的一般概念:在数学的数值分析领域中,贝赛尔曲线(Bezier curve)是电脑图形学中相当重要的参数曲线。更高维度的贝赛尔曲线被称作贝塞尔曲面。对于n阶贝塞尔曲线可如下推断,给定P0、P1、P2…Pn,其贝赛尔曲线即为

        用平常话说,n阶的贝赛尔曲线就是双n-1阶贝赛尔曲线之间的插值。

        由公式可以得出二次方贝塞尔曲线公式如下:

        TrueType字型就运用了以贝塞尔样条组成的二次方贝赛尔曲线。

    方法调用格式:

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

quadraticCurveTo(cpx, cpy, x, y)

    方法概述:

        上面分别是三次贝赛尔曲线和二次贝赛尔曲线的调用格式。其主要区别在于控制曲线的控制点式不一样的。其起始点均为子路径的最后一个点,结束点均为(x,y);在最后均要把(x,y)点加入到子路径中。

        其绘制图形的例子如下,三次贝赛尔曲线有两个红点作为曲线平滑的控制点,而二次贝塞尔曲线仅有一个控制点。

2.8.10.1 二次贝赛尔曲线quadraticCurveTo(cpx, cpy, x,y)
2.8.10.2 三次贝赛尔曲线bezierCurveTo(cp1x, cp1y, cp2x,cp2y, x, y)

2.9 文字

        绘图上下文提供了得到和设置文字样式的接口方法。

2.9.1 context.font[=value]

        获得和设置文字设置: context.font[=value],可以参考CSS中对font风格的设置。

2.9.2 context.textAlign[=value]

        获取或设置文字对齐方式:context.textAlign[=value],取值如下:

start                默认值,与canvas风格中的direction定义有关

end                 与canvas风格中的direction定义有关

left                  左

right                右

center             居中

2.9.3 context.textBaseline

        获得和设置文字对齐基线:context.textBaseline[=value],value的取值如下:

2.9.4 context.fillText(text, x, y[,maxWidth])

        绘制填充的文字,

2.9.5 context.strokeText(text, x, y[,maxWidth])

        对文字进行描边,不填充内部区域。

2.9.6 context.measureText(text)

        按照当前字体对给定的文字进行测量:

metrics = context.measureText(text),该方法返回一个TextMetrics对象,可以调用对象的width属性得到文字的宽度。

2.10 绘制图片

2.10.1 drawImage方法

        要在绘图上下文中绘制图片,可以使用drawImage方法。该方法有三种不同的参数:

        其中的image参数可以是HTMLImageElement、HTMLCanvasElement和HTMLVideoElement中的任一个对象。

        绘制参数的含义可以参看下图:

    异常:

        如果第一个参数不是指定的元素类型,抛出一个TYPE_MISMATCH_ERR异常,如果图片不能进行解码,则抛出INVALID_STATE_ERR异常,如果第二个参数不是允许的值,则抛出SYNTAX_ERR异常。    

    参数默认值:

        如果没有指定dw和dh,则默认等于sw和sh,如果sx,sy,sw,sh均没有提供,则默认为sx,xy=0,0;sw和sh为图片的像素宽高。

2.10.2 图片调用方式

        下面给出图片的几种调用方式:

    1.引用页面内图片:

        我们可以通过 document.images 集合、document.getElementsByTagName方法又或者document.getElementById 方法来获取页面内的图片(如果已知图片元素的 ID。

    2.引用canvas元素

        用 document.getElementsByTagName 或document.getElementById 方法来获取其它 canvas 元素。但你引入的应该是已经准备好的canvas。一个常用的应用就是为另一个大的 canvas 做缩略图。

    3.创建图像

        我们可以用脚本创建一个新的 Image对象,但这种方法的主要缺点是如果不希望脚本因为等待图片装置而暂停,还得需要突破预装载。

var img = new Image(); // Create new Image

img.src = 'myImage.png'; // Set source path

        当脚本执行后,图片开始装载。若调用 drawImage 时,图片没装载完,脚本会等待直至装载完毕。如果不希望这样,可以使用onload 事件:

var img = new Image(); // Create new Image

img.onload = function(){

    // 执行drawImage 语句

}

img.src = 'myImage.png'; // Set source path

        如果你只用到一张图片的话,这已经够了。但一旦需要不止一张图片,那就需要更加复杂的处理方法,但图片预装载策略超出本规范的范围。

    4.通过 data: url 方式嵌入图像

        我们还可以通过 data: url 方式来引用图像。Data urls 允许用一串Base64 编码的字符串的方式来定义一个图片。其优点就是图片内容即时可用,无须再到服务器兜一圈。(还有一个优点是,可以将 CSS,JavaScript,HTML 和 图片全部封装在一起,迁移起来十分方便。)缺点就是图像没法缓存,图片大的话内嵌的url数据会相当的长,例如:

var img_src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAyCAYAAADLLVz8AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAALESURBVGhD7ZUxSFVhFMfvezi8wcFBSMjhRYNBQUEOQQ4ODYFBQmOLUVtCDQ1By4OGBoeKgkYTh4gGl6CCIByaLDIqKAo0KhIUEmoUX7//49zHh/Tgil4x+F/4c8533nfPPd/Pcz6zzI8JmIAJmIAJmIAJmIAJmIAJmIAJmIAJmIAJmIAJmIAJmIAJmIAJmEC5BCrlps+yZtas842FSlbp+K31bH0yraOaVc+VXdd25a9uV6Kt5AHuGO9/wM5KW8m10+9uqgPppl6kw+7loFNonvUwOoP/g/hd7B8dQvvQYdyXxB7lHUjsBBph/Rbdj71N7D7Wi7HuxY6jW8RW6dDL+M/pzPc5IHJ0Ry37iT1kXw3bozh2AE2x/0vZQAt3IIWpuDkKOo4+4a9xsJPYB6xnBQvdUcHEx/EncAX1YnJowb4X718KAK2f8bVuKAaMFexBdA2N8vN5Yh+TPLWoZUS5Iq7c0/hH0SB6Uja8TeUXFPQ5fYn1DBKo1l2H/xtbw77BXo34EL46TGAn0LQg6d5Dgq93m/g3c4Cxt5/YMlpAx9LvxvvL2K4EaoN97yJfn3IidXKpT+EOpIo9qN0FUZUKVJfpWaJLND4HkMYrH5/vyQkO4evQdfZ+Rc/y3/Bvo0Y+1oyf3tPIdqfdF/v7sIvE1zbQWdGa+FLEVU+pT2GAFPWaSnR/1ZOK5vFPq+vQKbQakJ9iz6pDFE/2C1hXgGrD+tcJk/F+jH9De7BH6DL9EV6RYxBfo/r/PDFmGjeN1QWNCHYOfUO/UAtWHPRnxCbzEd6wX2M+Fvs1bqmGYnwFSaOsq2E4rgzdc7oOrsfo69tXNP7YFznNyJf+sXcH6Ljj+tNqOt01xHs6dJf+m7fvr6Ini05P7z11/u6DVPRA3mcCJmACJmACJmACJmACJmACJmACJmACJmACJmACJmACJrBTBP4C/mNz5egh3WQAAAAASUVORK5CYII=';

        有兴趣的朋友可以使用<img src=’上述变量值’>的方法显示出上面的图片。

2.11 像素级操作(good)

        2D Context API 提供了三个方法用于像素级操作:createImageData, getImageData, 和putImageData。

        ImageData对象保存了图像像素值。每个对象有三个属性: width, height 和data。data 属性类型为CanvasPixelArray,用于储存width*height*4个像素值。每一个像素有RGB值和透明度alpha值(其值为 0 至255,包括alpha在内)。像素的顺序从左至右,从上到下,按行存储。

        Canvas提供像素级数据,为很多算法的应用提供了平台,实现photoshop中的众多神奇图像效果在前端都已成为可能。甚至于我们还能实现电影的蓝幕效果——针对video的帧,通过drawImage绘进canvas,再做rgba处理,将规定颜色的像素的alpha值设为0,就能使特定部分变成透明,进而实现视频合成。查看演示

canvas的像素级操作——1.引子

http://cssass.com/blog/2012/1140.html

canvas的像素级操作——2.RGBA通道调色

http://cssass.com/blog/2012/1158.html

canvas的像素级操作——3.使用卷积矩阵

http://cssass.com/blog/2012/1165.html

canvas的像素级操作——4.关注性能

http://cssass.com/blog/2012/1179.html

[Color]彩色转灰度算法彻底学习

http://www.cnblogs.com/zyl910/archive/2006/05/22/2186658.html

在HTML5 的CANVAS 中应用卷积矩阵对图像处理

http://shawphy.com/2011/08/convolution-matrix-in-canvas.html

2.11.1 createImageData方法

imagedata = context . createImageData(sw, sh)

imagedata = context . createImageData(imagedata)

    方法概述:

        createImageData方法根据给定的CSS像素宽高或指定的imagedata具有的宽高建立一个ImageData对象,该对象为透明黑。该方法具体实例化一个新的空ImageData对象。

2.11.2 getImageData方法

imagedata = context . getImageData(sx,sy,sw, sh)

    方法概述:

        getImageData方法根据给定的绘图画布矩形面积(sx,sy,sw,sh),生成画布上该矩形面积的图形内容,并综合为ImageData对象返回。画布外的像素作为透明黑返回。

2.11.3 putImageData方法

imagedata = context . putImageData(imagedata, dx, dy [, dirtyX, dirtyY, dirtyWidth, dirtyHeight ])

    方法概述:

        在绘图画布上绘制给定的ImageData对象。假如脏矩形被提供,则只有在脏矩形上面的像素被绘制。本方法对全局透明、阴影和全局组合属性均忽略。

        异常:假如第一个参数不是ImageData对象,抛出TYPE_MISMATCH_ERR异常,假如任一数字参数是无穷或非数字,则抛出NOT_SUPPORTED_ERR错误。

        putImageData参数有(ImageData,dx, dy [, DirtyX] [, DirtyX] [, DirtyWidth] [, DirtyHeight])

    imageData:包含了图像的width,height,还有一个CanvasPixelArray,前面已经提了。

    dx, dy:表示绘图起始位置。相对于canvas区域左上角。

        后面四个可选参数:表示可见区范围。相对于起绘点,即上面的参数dx,dy表示的点。缺省为0,0,ImageData.width,ImageData.height。

2.11.3.1 具体用法

        通过将源canvas中像素数据ImageData,输出(putImageData)到新的canvas中,达到复制作用。

<script type="text/javascript">

function draw(){

            /*在canvas中绘制image*/

            var canvas1 =document.getElementById("MyCanvas");

            var ctx1 =canvas1.getContext("2d");

                        ctx1.drawImage(imgObj,0,0);

}

function clone(){

            /*在canvas2中绘制canvas1——普通复制*/

            var origin =document.getElementById("MyCanvas");

            var canvas2 =document.getElementById("YourCanvas");

            var ctx2 =canvas2.getContext("2d");

            ctx2.drawImage(origin,0,0);//drawImage不仅可以绘image,也可以绘canvas对象,甚至还可以绘video的帧

}

function cloneData(canvasObj){

            /*获取canvas1中的ImageData,在canvas3中输出 ——像素级复制*/

            var origin =document.getElementById("MyCanvas");

            var canvas3 =document.getElementById("GodCanvas");

            var ctx3 =canvas3.getContext("2d");

            var canvasCtx = origin.getContext("2d");

            var imagePix = canvasCtx.getImageData(0,0,origin.width,origin.height); //获取canvas1绘图环境下的参数范围内的imageData。

            ctx3.putImageData(imagePix,0,0);    //putImageData输出图像

}

/* 以下与示例代码无关*/

function Load(canvas){

            /* canvas Loading效果*/

            var backCtx =canvas.getContext('2d');

                        backWidth= canvas.width;

                        backHeight= canvas.height;

            var drawIntervalID,

                        spokes= 7;

            var      drawPad =document.createElement('canvas');

                        drawPad.width= 30;

                        drawPad.height= 30;

            var      drawCtx = drawPad.getContext('2d');

                        drawCtx.translate(drawPad.width/2,drawPad.height/2);

                        drawCtx.lineWidth= 5;

                        drawCtx.lineCap= "round";

                        drawCtx.strokeStyle= "rgba(0,0,0,0.1)";

                        drawCtx.fillStyle= "#fff";

            var draw =function(){

                        drawCtx.fillRect(0,0,drawPad.width ,drawPad.height);

                        drawCtx.rotate(Math.PI*2/spokes);

                        for(var i=0; i

                                    drawCtx.rotate(Math.PI*2/spokes);

                                    drawCtx.beginPath();

                                    drawCtx.moveTo(0,8);

                                    drawCtx.lineTo(0,10);

                                    drawCtx.stroke();

                        }

                        backCtx.drawImage(drawPad,(backWidth- drawPad.width)/2, (backHeight - drawPad.height)/2);

            }

            this.loading =function(){

                         drawIntervalID = setInterval(draw,200);

            }

            this.loaded =function(){

                        clearInterval(drawIntervalID);

                        backCtx.clearRect((backWidth- drawPad.width)/2, (backHeight - drawPad.height)/2, drawPad.width,drawPad.height);

            }

}

var imgObj = document.getElementById("imgObj");

var canvas = document.getElementsByTagName('canvas');

for(var i = 0, l = canvas.length; i < l; i++ ){

            var loadObj = newLoad(canvas[i]);

            (function(obj){

                        obj.loading();

                        imgObj.addEventListener('load',obj.loaded,false);

            })(loadObj);

}

imgObj.addEventListener('load',function(){

            draw();

            clone();

            cloneData();

},false);

</script>

2.11.4 示例

        下面展示了对一张图片进行反色、透明的一个例子,从例子中可以看出,有了像素级的控制能力,我们可以很轻易的对原有图片进行各种图像滤镜操作。图示如下:

        JS代码:[当你在word中选择上面的图片,会发现反色滤镜常用在选择操作里]

var imgSrc =  document.getElementById('codeex.cn')

context.drawImage(imgSrc,10,10);

var imgd =  context.getImageData(10,10,100,122);

var pix =  imgd.data;

//反色处理

for(var  i=0,n=pix.length;i

{

    pix[i] = 255 - pix[i]; //红

    pix[i+1] = 255-pix[i+1]; //绿

    pix[i+2] = 255-pix[i+2]; //蓝

    pix[i+3] = pix[i+3]; //alpha

}

context.putImageData(imgd,130,10);

imgd =  context.getImageData(10,10,100,122);

pix =  imgd.data;

//透明处理 透明度0.6

for(var  i=0,n=pix.length;i

{

    pix[i] = pix[i]; //红

    pix[i+1] = pix[i+1]; //绿

    pix[i+2] = pix[i+2]; //蓝

    pix[i+3] = pix[i+3]*0.6; //alpha

}

context.putImageData(imgd,260,10);

2.11.5 性能优化示例

canvas像素级操作

http://blog.sina.com.cn/s/blog_502364000100qwc8.html

2.12 绘图模型

        在本文描述的画布中绘图,浏览器一般按照下面的顺序进行绘制:

    1.准备形状或图片,此时图片假设为A,形状必须被所有属性描述的形状,且经过坐标转换;

    2.当绘制阴影时,准备图片A,并绘制阴影,形成图片B;

    3.当绘制阴影时,为B的每个像素乘上alpha值;

    4.当绘制阴影时,则根据组合参数对B和本画布剪贴区域内的图片进行组合;

    5.在图片A上每个像素乘上alpha值;

    6.在图片A上根据组合参数对A和本画布剪贴区域内的图片进行组合;

3 Canvas动画库——KineticJS

以下教程是根据2012年教程整理的,部分接口有调整,后续注意逐步整理更新

KineticJS中文系列教程

http://iysm.net/?paged=2

How It Works

https://github.com/ericdrowell/KineticJS/wiki

4 Canvas动画库——Collie

Collie——基于 HTML5 的高性能

JavaScript 动画库

http://www.cnblogs.com/lhb25/archive/2012/12/17/create-highly-optimized-animations-using-html5.html

5 Canvas动画库——EaselJS

EaselJS(CreateJS)屏幕适配

http://abellee.github.io/blog/easeljs/2014/07/11/EaselJS(CreateJS)%E5%B1%8F%E5%B9%95%E9%80%82%E9%85%8D.html

CreateJS基础

http://www.jikexueyuan.com/course/276.html

6 特效合集

6.1 星星图形

function drawStar(ctx,r){

    ctx.save();

    ctx.beginPath()

    ctx.moveTo(r,0);

    for (var i=0;i<9;i++){

        ctx.rotate(Math.PI/5);

        if(i%2 == 0) {

           ctx.lineTo((r/0.525731)*0.200811,0);

        } else {

            ctx.lineTo(r,0);

        }

    }

    ctx.closePath();

    ctx.fill();

    ctx.restore();

}

7 参考链接

HTML5 canvas addColorStop() 方法

http://www.w3school.com.cn/tags/canvas_addcolorstop.asp

canvas像素级操作

http://blog.sina.com.cn/s/blog_502364000100qwc8.html

专栏——HTML5 Canvas编程

http://blog.csdn.net/column/details/canvas-programming.html

专栏——html5 Canvas画图系列教程目录

http://jo2.org/html5-canvas-tutorial-list/

[Canvas系列]Canvas绘制圆弧形状_04

http://blog.csdn.net/baihuaxiu123/article/details/53619435

HTML5 Canvas 画圆教程

http://www.108js.com/article/article7/70206.html?id=1036

6.5 径向渐变

http://www.lvyestudy.com/css3/css3_6.5.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江中散人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值