canvas学习

准备

定义 canvas 元素,以及 css 样式,使用 javascript 进行 canvas 绘制。

canvas 的绘制步骤:先描述状态,再使用绘制函数绘制。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>canvas学习</title>
  </head>
  <style type="text/css">
  	#canvas{
  		display: block;
  		margin: 0 auto;
  		border: 1px solid #aaa;
  	}
  </style>
  <body>
    <canvas id="canvas" width="1000" height="800">
    	您的浏览器不支持canvas
    </canvas>
  </body>
  <script type="text/javascript">
  	// 使用context绘制
  	var canvas=document.getElementById("canvas");
  	var context=canvas.getContext("2d");

  </script>
</html>

绘制基础

context设置

// 使用beginPath()以及closePath()来隔离两段状态
context.beginPath();
// 使用closePath()函数时,会自动将最终坐标与最初坐标用直线连接,可以不使用closePath()函数;
// 如果使用了fill()填充了颜色,closePath()不起作用
context.closePath();


// 设置了context的一些形状操作后,相关值会有叠加;可以保存和恢复context来重置,save()以及restore()
context.save();
context.restore();

// 清除canvas的内容
context.clearRect(x,y,w,h)

设置背景

简单设置

context.fillStyle="";
context.strokeStyle="";
// 设置以下值
1. #ffffff
2. #642
3. rgb(255,255,0)
4. rgba(100,100,100,0.6)
5. hsl(20,60%,60%)
6. hsla(20,60%,80%,0.6)
7. red

渐变设置

  • 线性渐变

    // 从(xtart,ystart)坐标到(xend,yend)的线性渐变
    // grd=context.createLinearGradient(xstart,ystart,xend,yend);
    // 在stop处设置color,stop取值 0~1 的浮点值
    // grd.addColorStop(stop,color);
    
    var grd=context.createLinearGradient(0,0,800,0);
    grd.addColorStop(0.0,"red");
    grd.addColorStop(1.0,"yellow");
    context.fillStyle=grd;
    
  • 径向渐变

    // 从圆心坐标(x0,y0)半径为r0的圆到圆心坐标(x1,y1)半径为r1的圆坐标之间的径向渐变
    // grd=context.createRadialGradient(x0,y0,r0,x1,y1,r1);
    // 在stop处设置color,stop取值 0~1 的浮点值
    // grd.addColorStop(stop,color);
    
    var grd=context.createRadialGradient(100,100,0,100,100,100);
    grd.addColorStop(0.0,"red");
    grd.addColorStop(1.0,"yellow");
    context.fillStyle=grd;
    

设置图片背景

context.createPattern(img,repeat-style)

repeat-style 可选值:no-repeat、repeat-x、repeat-y、repeat

var context=canvas.getContext("2d");
//将一个图像绘制到canvas中,从(dx,dy)开始绘制 image 图像
var image=new Image();
image.src="img/UI-bc-2.png";
image.onload=function(){
    // 必须在图像加载完之后再绘制
    var pattern = context.createPattern(image,"no-repeat");
    context.fillStyle=pattern;
    context.fillRec(0,0,800,800);
}

createPattern() 既可以将 image 作为填充,也可以将 canvasvideo 作为填充

线条属性

  • context.lineWidth 线条宽度

  • context.lineCap 设置线条尾部的突出一部分的形状

    可取值:butt(default---不突出)、round(圆形)、square(方形)

  • context.lineJoin 设置两条线的相交点的形状

    可取值:miter(default---形成尖角)、bevel(不会有尖角)、round(形成圆角)

  • context.miterLimit

    只有当 lineJoin 设置为 miter 时有效,默认是10,代表两条线的相交产生的尖角长度超过10个像素时,会使用 bevel 属性相交特点。

位移、旋转、缩放操作

  • context.translate(x,y) 将图形 x 轴移动 x , y 轴移动 y

  • context.rotate(deg) 将图形旋转

  • context.scale(sx,sy) 将图形 x 方向缩放 sx 倍,y 方向缩放 sy 倍,会应用到一个图形的所有属性上,例如坐标值,线条宽度等。。。

  • context.transform(a,b,c,d,e,f) 以矩阵方式进行设置

    a :水平缩放 1

    b :水平倾斜 0

    c :垂直倾斜 0

    d :垂直缩放 1

    e :水平位移 0

    f :垂直位移 0

    相当于以下矩阵:

    a c e

    b d f

    0 0 1

如果进行多次以上操作,这些值会叠加,如果需要清除之前的操作,可以使用以下方法:

// 保存和恢复context,save()以及restore()
context.save();
context.translate(100,100);
context.restore();
// 重设transform,可以使用setTransform()
context.setTransform(1,0,0,1,100,100);

绘制

直线绘制

// 使用beginPath()以及closePath()来隔离两段状态
context.beginPath();

// 从(100,100)画到(700,700)的直线
context.moveTo(100,100);
context.lineTo(700,700);
// 可以使用多个context.lineTo()来绘制多边形,会按照先后定义的坐标值按照先后顺序画
context.lineTo(100,700);
context.lineTo(100,100);
// 使用strokeStyle来绘制线条颜色
context.strokeStyle="red";
context.lineWidth=5;

context.closePath();
// stroke()函数画线条
context.stroke();
// 填充画出的区域
context.fillStyle="blue";
context.fill();

弧绘制

基于圆绘制

context.arc(centerx,centery,radius,startAngle,endAngle,antclockwise=false)

说明:

  • centerx --- 圆心的x轴坐标

  • centery --- 圆心的y轴坐标

  • radius --- 半径

  • startAngle --- 开始弧度值

  • endAngle --- 结束弧度值

    弧度值从最右边的坐标开始算起,0*Math.PI - 0.5*Math.PI - 1*Math.PI - 2*Math.PI

  • antclockwise --- 弧度绘制方向,false:顺时针;true:逆时针

context.strokeStyle="red";
context.lineWidth=3;
context.arc(300,300,100,0,1.5*Math.PI);
context.stroke();

弧度绘制

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

以上一个绘制出的最后一个点为 (x0,y0) ,必须有上一个绘制的最终点或者moveTo()定点,根据 (x0,y0)、(x1,y1)、(x2,y2) 三个坐标绘制一个三角形,形成一个与 (x0,y0) 和 (x1,y1) 的直线和 (x1,y1) 和 (x2,y2) 的直线相切的半径为 radius 的圆弧。切点不一定是 (x0,y0) 和 (x2,y2)

!!! 以下贝塞尔曲线需要多次试验才好控制 !!!

二次贝塞尔曲线

context.quadraticCurveTo(x1,y1,x2,y2)

必须有上一个绘制的最终点或者moveTo()定点

(x1,y1) 作为控制点,(x2,y2) 作为结束点

三次贝塞尔曲线

context.bezierCurveTo(x1,y1,x2,y2,x3,y3)

必须有上一个绘制的最终点或者moveTo()定点

(x1,y1)、(x2,y2) 作为控制点,(x3,y3) 作为结束点

矩形绘制

var context=canvas.getContext("2d");
//绘制一个红色的方形,左上坐标(100,100),右下坐标(100+400,100+400)
context.fillStyle="red";
// 方法一
context.rect(100,100,400,400);
context.stroke();
// 方法二
context.fillRect(100,100,400,400);
// 方法三
context.strokeRect(100,100,400,400);

context.fill();

图像绘制

简单绘制

context.drawImage(image,dx,dy)

var context=canvas.getContext("2d");
//将一个图像绘制到canvas中,从(dx,dy)开始绘制 image 图像
var image=new Image();
image.src="img/UI-bc-2.png";
image.onload=function(){
    // 必须在图像加载完之后再绘制
    context.drawImage(image,50,50);
}

改变宽高

context.drawImage(image,dx,dy,dw,dh)

var context=canvas.getContext("2d");
//将一个图像绘制到canvas中,从(dx,dy)开始绘制 image 图像,并修改图片的宽高分别为dw,dh
var image=new Image();
image.src="img/UI-bc-2.png";
image.onload=function(){
    // 必须在图像加载完之后再绘制
    context.drawImage(image,50,50,200,200);
}

部分图像绘制

context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)

将 source 图片的 (sx,sy) 坐标之后的宽为sw,高为sh的图片,根据(dx,dy,dw,dh)参数映射到 canvas 中。

var context=canvas.getContext("2d");
//将一个图像绘制到canvas中,从(dx,dy)开始绘制 image 图像,并修改图片的宽高分别为dw,dh
var image=new Image();
image.src="img/UI-bc-2.png";
image.onload=function(){
    // 必须在图像加载完之后再绘制
    // 截取原图片的一部分图片映射到canvas中
  	context.drawImage(image,50,50,500,500,0,0,canvas.width,canvas.height);
}

缩放绘制

// 添加 range 滑杆控件以及样式,并通过滑杆值的改变来缩放图片
// #range{
// 	display: block;
// 	margin: 20px auto;
// 	width: 300px;
// }
//  <input type="range" name="" id="range" min="0.5" max="3.0" step="0.01" value="1.0" />

var slider=document.getElementById("range");
//获取滑杆的值
var scale=slider.value;
//将一个图像绘制到canvas中
var image=new Image();
image.src="img/UI-bc-2.png";
image.onload=function(){
    // 必须在图像加载完之后再绘制
    // 根据缩放比例显示图片
    drawImageByScale(scale,image);
    // 根据滑杆的移动来改变图片的显示比例
    slider.onmousemove=function(){
        scale=slider.value;
        drawImageByScale(scale,image);
    }
}
function drawImageByScale(scale,image){
    //清除 canvas 内容
    context.clearRect(0,0,canvas.width,canvas.height);
    // 计算应显示的图片宽高
    var imageWidth=image.width*scale;
    var imageHeight=image.height*scale;
    // 方法一,裁剪图片来放大图片但是缩小图片时需要另写判断程序,以下程序不完全
    // 计算sx,sy
    var sx=imageWidth/2-canvas.width/2;
    var sy=imageHeight/2-canvas.height/2;
    context.drawImage(image,sx,sy,canvas.width,canvas.height, 0,0,canvas.width,canvas.height);
    
    // 方法二,修改图片的(dx,dy)值来缩小放大图片
    var dx=canvas.width/2-imageWidth/2;
    var dy=canvas.height/2-imageHeight/2;
    context.drawImage(image,dx,dy,imageWidth,imageHeight);
}

双canvas绘制

// 将 canvas_2 的图像绘制在 context 所代表的 canvas 上
context.drawImage(canvas_2,canvas.width-canvas_2.width,canvas.height-canvas_2.height);

像素绘制

  • 获取一个 canvas 的图像

    imageData=context.getImageData(x,y,w,h);

    可以获取到 imageData 的 width、height、data

  • 将获取到的图像放到 canvas 中

    context.putImageData(imageData,x,y,imageDataX,imageDataY,imageDataW,imageDataH)

  • 创建imageDta

    imageData=context.createImageData(w,h)

imageData 的data中存储了所有像素,计算方式如下:

  1. 第i个像素:r=imageData.data[4*i+0];g=imageData.data[4*i+1];b=imageData.data[4*i+2];a=imageData.data[4*i+3]
  2. 第x行第y列的像素:i=x*width+y,r/g/b/a的计算方式仍如上所示。

文字渲染

  • context.font

    可设置font-style(normal、italic---斜体字、oblique---倾斜文字)、font-variant(normal、small-caps---英文小写字母更有效)、font-weight(lighter、normal、bold、bolder、100-900)、font-sizefont-family(支持@font-face),默认"20px sans-serif"

  • context.textAlign 文本水平对齐方式

    left、right、center

  • context.textBaseline 文字垂直对齐方式

    top、middle、bottom

    alphabetic(为拉丁语准备的)、ideographic(为汉字等方块文字准备的)、hanging(为印度语准备的)

  • context.measureText(string).width 文本度量

    获取在canvas中渲染文字所占的宽度

// 设置文字和位置,显示文字线条和位置
// context.fillText(string,x,y,[maxlen]);
// context.strokeText(string,x,y,[maxlen]);
// 也可以使用fillStyle来设置文字的颜色
context.font="bold 40px Arial";
context.fillText("canvas",100,100);
context.fillText("canvas",100,100,200);

阴影绘制

  • context.shadowColor 阴影颜色
  • context.shadowOffsetX 阴影 x 轴的偏移量
  • context.shadowOffsetY 阴影 y 轴的偏移量
  • context.shadowBlur 阴影的模糊程度

其他属性

  • context.globalAlpha 这是一个全局变量的透明度设置,取值:0~1

  • context.globalCompositeOperation 图形重叠时的处理方式

    取值:

    source-over(后绘制的覆盖前绘制的---默认)

    destination-over(前覆盖后)

    source-atop(显示前绘制的图形以及处于前绘制的图形中的后绘制图形的部分)

    source-in(只显示处于前绘制的图形中的后绘制图形的部分)

    source-out(只显示处于前绘制的图形之外的后绘制图形的部分)

    destination-atop(显示后绘制的图形以及处于后绘制的图形中的前绘制图形的部分)

    destination-in(只显示处于后绘制的图形中的前绘制图形的部分)

    destination-out(只显示处于后绘制的图形之外的前绘制图形的部分)

    lighter(重叠部分的颜色会混合重叠双方的颜色)

    copy(只绘制后绘制的)

    xor(重叠部分没有颜色)

  • context.clip() 剪辑区域

    使用之前绘制的图形作为剪辑区域

  • 按照非零环绕原则确认路径方向以及封闭区域的内外

  • context.isPointInPath(x,y) (x,y)是否在之前绘制的区域内

  • canvas.getBoundingClientRect() 获取canvas的边距

    示例使用:canvas.getBoundingClientRect().left

高级拓展

自定义库函数

允许将自定义的画图函数添加到context中。

CanvasRenderingContext2D.prototype.fillStar=function(){
    // ...
}

context.fillStar();

// 记录MoveTo的坐标
var originalMoveTo=CanvasRenderingContext2D.prototype.moveTo;
CanvasRenderingContext2D.prototype.lastMoveToLoc={}
// 重写函数
CanvasRenderingContext2D.prototype.moveTo=function(x,y){
    // 调用原函数
    originalMoveTo.apply(context,[x,y]);
    this.lastMoveToLoc.x=x;
    this.lastMoveToLoc.y=y;
}
posted @ 2019-02-17 14:41  枫子_dan  阅读(238)  评论(0编辑  收藏  举报