Canvas

标签

  • 通过 HTML 创建 canvas 元素,其 width、height 属性指定其宽、高。
<canvas id="canvas" width="800" height="800"></canvas>
  • 通过 JS 获取 canvas 元素,可以通过获取后元素的 width、height 属性拿到其宽高。
var canvas = document.getElementById('canvas'); // 获取 canvas 元素
var context = canvas.getContext('2d'); // 获取 canvas 元素中上下文

// 获取 canvas 元素的宽高
var canvas_width = canvas.width;
var canvas_height = canvas.height;
console.log(canvas_width,canvas_height); // 800 800

canvas 元素的实际方法并不多,绘制使用的是其 getContext('2d') 返回的上下文中的方法,以下我们用 context 代表 canvas 所在的上下文。

直线

绘制方法

  • context.moveTo
  • context.lineTo
  • context.lineWidth
  • context.strokeStyle
  • context.fillStyle
  • context.stroke
  • context.beginPath
  • context.closePath

线条属性

  • lineWidth
  • lineCap 用于设置一个线条两端的形状
    • butt(default)
    • round
    • square
  • lineJoin 线条和线条香蕉皮的时候所展现的形态
    • miter(default)
    • bevel
    • round
  • miterLimit 描述lineJoin为miter的时候内角和外角之间距离的最大值,当超过miterLimit将自动转换成bevel属性

线条

把笔尖放在坐标轴(100,100)的位置上,笔从(100,100)走到了(200,200)。以上只是绘制意图,具体的绘制需要调用 stroke 方法。

context.moveTo(100,100);  // 把笔尖放在坐标轴(100,100)的位置上
context.lineTo(200,200);  // 把笔从(100,100)走到了(200,200)
context.lineWidth = 2;    // 设置笔的宽度
context.strokeStyle = '#35b558'; //给绘制设置样式
context.stroke();         // 具体的绘制需要调用 stroke 方法

多边形

通过不断的 lineTo 方法可以绘制出一个多边形。

context.moveTo(100,100);
context.lineTo(200,200);
context.lineTo(100,200);
context.lineTo(100,100);

context.stroke();  

不同路径

对于,多个路径,我们需要分开处理其样式的话,需要在路径定义的首尾通过 beginPath 和 closePath 方法。

// 绘制一个红色的三角形
context.beginPath();
context.moveTo(100,100);
context.lineTo(200,200);
context.lineTo(100,200);
context.lineTo(100,100);
context.closePath();
context.lineWidth = 5;
context.strokeStyle = 'red';
context.stroke();
// 再绘制另一条绿色的线段
context.beginPath();
context.moveTo(150,100);
context.lineTo(250,200);
context.closePath();
context.strokeStyle = '#35b558';
context.stroke(); 

七巧板版示例

<canvas id="canvas" width="800" height="800"></canvas>
var canvas = document.getElementById('canvas'); 
var context = canvas.getContext('2d');
var tangram = [
    {p:[{x:0,y:0},{x:800,y:0},{x:400,y:400}],color:'#caff67'},
    {p:[{x:0,y:0},{x:400,y:400},{x:0,y:800}],color:'#67becf'},
    {p:[{x:800,y:0},{x:800,y:400},{x:600,y:600},{600:200}],color:'#ef3d61'},
    {p:[{x:600,y:200},{x:600,y:600},{x:400,y:400}],color:'#f9f51a'},
    {p:[{x:400,y:400},{x:600,y:600},{x:400,y:800},{x:200,y:600}],color:'#a594c0'},
    {p:[{x:200,y:600},{x:400,y:800},{x:0,y:800}],color:'#fa8ecc'},
    {p:[{x:800,y:400},{x:800,y:800},{x:400,y:800}],color:'#f6ca29'},
]

tangram.forEach(function(data,index){
    context.beginPath();
    context.moveTo(data.p[0].x,data.p[0].y);
    data.p.forEach(function(pos,p_index){
        context.lineTo(pos.x,pos.y)
    })
    context.closePath();
    // 填充颜色
    context.fillStyle = data.color;
    context.fill();
    // 描边
    context.strokeStyle = "black";
    context.lineWidth = 2;
    context.stroke();
})

弧线

context.arc(cx,cy,radius,startingAngle,endingAngle,anticlockwise = false) 无论是逆时针或者顺时针的方向绘制,坐标轴中的弧度坐标是不会变发送变化的。

  • cx、cy 圆心坐标
  • radius 半径
  • startingAngle,endingAngle 开始的弧度到终止的弧度
  • 是否逆时针的方向来绘制

其他曲线绘制方法

  • context.arcTo(x1,y1,x2,y2,radius)
  • context.quadraticCurveTo(x1,y1,x2,y2)
  • context.bezeirCurveTo(x1,y1,x2,y2,x3,y3)
context.lineWidth = 2;
context.strokeStyle = "#35b558";
context.arc(300,300,200,0,1.5 * Math.PI);
context.stroke();

在我们绘制的路径不是封闭的情况下,使用 closePath 之后会自动把首位两端连接起来形成一个封闭的路径。

context.lineWidth = 2;
context.strokeStyle = "red";
for (var i = 0; i < 10 ; i++) {
    context.beginPath()
    context.arc(10 + i * 50, 90,20,0,2 * Math.PI * (i+1)/10 )
    // 添加closePath
    context.closePath();
    context.stroke();
}

context.strokeStyle = "#35b558";
for (var i = 0; i < 10 ; i++) {
    context.beginPath()
    context.arc(10 + i * 50, 180,20,0,2 * Math.PI * (i+1)/10 )
    // 没有添加closePath
    context.stroke();
}

context.fillStyle = "blue";
for (var i = 0; i < 10 ; i++) {
    context.beginPath()
    context.arc(10 + i * 50, 270,20,0,2 * Math.PI * (i+1)/10 )
    context.fill();
}

矩形

  • context.rect(x,y,width,height);
  • context.fillRect(x,y,width,height);
  • context.strockRect(x,y,width,height);
context.rect(100,100,100,100);
context.fillStyle = "blue";
context.fill();

context.fillRect(200,200,100,100);
context.strokeRect(300,100,100,100);

填充

fillStyle = color 、gradient、image、canvas、video

线性渐变

通过(x1,y1,x2,y2)参数可以控制渐变背景的大小和方向

var grd = context.createLinearGradient(0,0,200,200);
grd.addColorStop(0,'white');
grd.addColorStop(1,'black');
context.fillStyle = grd;
context.fillRect(0,0,200,200);

径向渐变

通过(x0,y0,r0,x1,y1,r1)定义两个圆的原点坐标和半径

var grd = context.createRadialGradient(100,100,0,100,100,100);
grd.addColorStop(0,'white');
grd.addColorStop(1,'black');035
context.fillStyle = grd;
context.fillRect(0,0,200,200);

其他填充

  • createPattern(img, repeat-style)
  • createPattern(canvas, repeat-style)
  • createPattern(video, repeat-style)
    • repeat-style
      • no-repeat
      • repeat-x
      • repeat-y
      • repeat

文字

基础方法

  • context.font = "bold 40px Arial";
  • context.fillText(string ,x ,y, [maxLen]);
  • context.strokeText(string, x, y, [maxLen]);
context.font = "bold 40px Arial";
context.fillStyle = "#058";
context.fillText('Hello, world!',40,100);

context.lineWidth = 1;
context.strokeStyle = "#058";
context.strokeText('Hello, world!',40,200);

context.fillText('Hello, world!',40,300,100);
context.strokeText('Hello, world!',40,400,100);

字体

context.font = font-style font-variant font-weight font-size font-family

  • font-style
    • normal
    • italic
    • oblique
  • font-variant
    • nromal
    • smal-caps
  • font-weight
    • lighter
    • normal
    • bold
    • bolder
    • 100,200,300,400(normal)
    • 500,600,700(bold)
    • 800,900
  • font-size
    • px
    • em
    • %
    • xx-small、x-small
    • medium
    • large、x-large、xx-large
  • font-family

文本对齐

水平对齐 context.textAlign

  • left
  • center
  • right
context.font = "bold 40px Arial";
context.fillStyle = "#058";
// 400 的位置左对齐
context.textAlign = "left";
context.fillText('Hello, world!',400,100);
// 400 的位置居中
context.textAlign = "center";
context.fillText('Hello, world!',400,200);
// 400 的位置有对齐
context.textAlign = "right";
context.fillText('Hello, world!',400,300);

垂直对齐 context.textBase

  • top
  • middle
  • bottom
  • alphabetic(default)
  • ideographic
  • hanging
function drawBaseline(h){
    var width = context.canvas.width;
    context.save();
    context.strokeStyle = '#e4e4e4';
    context.lineWidth = 2;
    context.moveTo(0,h);
    context.lineTo(width,h);
    context.stroke();
    context.restore();
}
context.font = "bold 40px Arial";
context.fillText('Hello, world!',100,100);
drawBaseline(100)
context.textBaseline = "bottom";
context.fillText('Hello, world!',100,200);
drawBaseline(200)
context.textBaseline = "middle";
context.fillText('Hello, world!',100,300);
drawBaseline(300)
context.textBaseline = "top";
context.fillText('Hello, world!',100,400);
drawBaseline(400)

上下剧中对齐示例

// 绘制画布剧中文字
context.font = "bold 40px Arial";
context.fillStyle = "#058";
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText('CANVAS',canvas.width/2,canvas.height/2);
// 辅助线
context.strokeStyle = '#e4e4e4';
context.moveTo(0,canvas.height/2);
context.lineTo(canvas.width,canvas.height/2);
context.moveTo(canvas.width/2,0);
context.lineTo(canvas.width/2,canvas.height);
context.stroke();

文本度量,返回该文本在 canvas 上绘制所占用的宽高, context.measureText(String).width;

var str_width = context.measureText("HELLO,WORD!").width;
console.log(str_width)

变换

  • translate(x,y) 位移
  • rotate(deg) 旋转
  • scale(sx,sy) 缩放

参考