canvas(二)绘制图形

使用canvas 绘制图形


上一篇 canvas基本用法
在学习了canvas基本用法 我们开始着手在 canvas 上绘制矩形、三角形、直线、圆弧和曲线。

栅格

画布栅格
画布栅格

在开始在 canvas 上面绘制图形之前需要了解一下画布栅格以及坐标空间。如上图的,canvas 元素默认被网格所覆盖,每一个单元格代表一个像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。

绘制矩形

不同于SVG,HTML中的元素 canvas 只支持一种原生的图形绘制:矩形。其他的图形绘制都至少需要生成一条路径。

canvas 提供了三种方法绘制矩形:

fillRect(x,y,width,height)
绘制一个填充的矩形

strokeRect(x,y,width,height)
绘制一个矩形的边框

clearRect(x,y,width,height)
清除指定矩形区域,让清除部分完全透明

上面提供的方法中包含相同的参数,x/y指定 所绘制的图形相对于原点的坐标,width/height设置了矩形的宽高

绘制矩形 例子

function draw(){
    let canvas = document.getElementById('canvas')
    
    if(canvas.getContext()){
        let ctx = canvas.getContext('2d')
        ctx.fillRect(25,25,100,100)
        ctx.clearRect(45,45,60,60)
        ctx.strokeRect(50,50,50,50)
    }
}

输出如下图:

矩形例子
矩形例子

fillRect()函数绘制了一个边长为100px的黑色正方形。clearRect()函数从中正方形的中心位置擦出了一个60x60的正方形,接着 strokeRect()在清除区内生成一个50*50的正方形边框

绘制路径

canvas 只支持一种原生的图形绘制:矩形。其他的图形绘制都至少需要生成一条路径。 图形的基本元素是路径,路径是通过不同的颜色和宽度的线段或者曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的,使用路径绘制图形需要一些额外的步骤。

  1. 首先,需要创建路径的起点
  2. 然后使用画图命令去画出路径
  3. 把路径封闭
  4. 一旦路径生成,就能通过描边或者填充路径区域来渲染图形。

以下是所用到的函数:

beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。

moveTo()
将笔触移动到指定的坐标上面,通常当canvas初始化或者beginPath()调用后,需要使用moveTo()函数设置起点。

closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。

stroke()
通过线条来绘制图形轮廓

fill()
通过填充路径的内容区域生成实心的图形。

生成路径的第一步叫做 beginPath()。本质上,路径是有很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧线)构成图形。而每次这个方法调用之后,列表清空重置,然后就可以重新绘制新的图形。

第二部就是调用函数指定绘制路径。

第三步就是闭合路径closePath() 不是必须的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形已经是闭合的,即当前点为开始点,该函数什么也不做。

注意:当调用fill()函数时,所有没有闭合的形状都会自动闭合,所以不再需要调用closePath()函数,但是调用stroke()时不会自动闭合

绘制一个三角形

function draw(){
    let canvas = document.getElementById('canvas')
    if(canvas.getContext()){
        let ctx = canvas.getContext('2d')
        //开始一个路径
        ctx.brginPath()
        ctx.moveTo(75,50)
        ctx.lineTo(100,75)
        ctx.lineTo(100,25)
        ctx.fill()
    }
}

输出图形:

canvas三角形
canvas三角形

移动触笔

一个非常有效的函数,而这个函数实际上并不能画出任何东西,这个函数就是`moveTo()`。是一个点到另一个点的移动过程。

moveTo(x,y)

将笔触移动到指定的x,y的坐标上面。

当canvas初始化或者beginPath()调用后,通常会使用moveTo()函数设置起点。或者也可以用moveTo()绘制一些不连续的路径。

function draw(){
    let canvas = document.getElementById('canvas')
    if(canvas.getContext()){
        let ctx = canvas.getContext('2d')
        //开始绘制
        ctx.beginPath()
        ctx.arc(75,75,50,0,Math.PI*2,true)
        ctx.moveTo(110,75)
        ctx.arc(75,75,35,0,Math.PI,false);   // 口(顺时针)
        ctx.moveTo(65,65);
        ctx.arc(60,65,5,0,Math.PI*2,true);  // 左眼
        ctx.moveTo(95,65);
        ctx.arc(90,65,5,0,Math.PI*2,true);  // 右眼
        ctx.stroke();
    }
}

效果图:


效果图
效果图

线

绘制直线,需要用到的方法lineTo()

lineTo()

绘制一条从当前位置到指定坐标(x,y)的直线。

该方法有两个参数,x,y代表坐标系中直线结束的点。开始点是上个路径的结束点,或者使用moveTo()来设置开始点。

function draw(){
    let canvas = document.getElementById('canvas')
    if(canvas.getContext()){
        let ctx = canvas.getContext('2d')
        //填充三角形
        ctx.beinPath();
        ctx.moveTo(25,25)
        ctx.lineTo(105,25)
        ctx.lineTo(25,105)
        ctx.fill();
        //描边三角形
        ctx.beginPath()
        ctx.moveTo(125,125)
        ctx.lineTo(125,45)
        ctx.lineTo(45,125)
        ctx.closePath()
        ctx.stroke()
    }
}
此处输入图片的描述
此处输入图片的描述

这里从调用beginPath()函数准备绘制一个新的路径,然后使用moveTo()函数移动到目标位置上。

填充与描边不厚有所不同。路径使用fill()填充时,路径会自动闭合,而使用stroke()描边时,路径则不会自动闭合,需要使用closePath()来闭合线路。

圆弧

绘制圆弧或者圆,使用arc()方法。也可以使用arcTo()

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

画出一个以(x,y)为圆心,radius为半径的圆弧,从startAngle开始到endAngle结束,按照anticlockwise的方向(默认为true顺时针,false为逆时针)。

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

根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。

startAngle和endAngle参数定义了开始以及结束的弧度,并且以x轴为基准。一个正圆的弧度为Math.PI*2,半圆的弧度为Math.PI

function draw(){
    let canvas = document.getElementById('canvas')
    if(canvas.getContext()){
        let ctx = canvas.getContext('2d')
        
        for(let i = 0;i<4;i++){
           for(let j = 0;j<4;j++){
                ctx.beginPath();
                var x              = 25+j*50;               // x 坐标值
                var y              = 25+i*50;               // y 坐标值
                var radius         = 20;                    // 圆弧半径
                var startAngle     = 0;                     // 开始点
                var endAngle       = Math.PI+(Math.PI*j)/2; // 结束点
                var anticlockwise  = i%2==0 ? false : true; // 顺时针或逆时针
                ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
                
                if (i>1){
                  ctx.fill();
                } else {
                  ctx.stroke();
                }
            } 
        }
    }
}
圆弧
圆弧

二次贝赛尔曲线以及三次贝赛尔曲线

一般用于绘制复杂有规律的圆弧

quadraticCurveTo(cp1x,cp1y,x,y)

绘制二次贝赛尔曲线,cpx1,cpy1为一个控制点,x,y为结束点。

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

绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。

贝赛尔曲线
贝赛尔曲线

上图描述了二次贝赛尔曲线和三次贝赛尔曲线的关系。二次贝赛尔曲线有一个结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线有两个控制点,一个结束点;

参数x,y为结束点的坐标,cp1x,cp1y为第一个控制点的坐标,cp2x,cp2y为第二个控制点的坐标。

二次贝赛尔曲线 图例

用二次贝赛尔曲线渲染一个对话气泡。

function draw(){
    let canvas = document.getElementById('canvas')
    if(canvas.getContext()){
        let ctx = canvas.getContext('2d')
        
        ctx.beginPath()
        ctx.noveTo(75,75)
        ctx.quadraticCurveTo(25,25,25,62.5)
        ctx.quadraticCurveTo(25,100,50,100);
        ctx.quadraticCurveTo(50,120,30,125);
        ctx.quadraticCurveTo(60,120,65,100);
        ctx.quadraticCurveTo(125,100,125,62.5);
        ctx.quadraticCurveTo(125,25,75,25);
        ctx.stroke()
    }
}
二次贝赛尔曲线
二次贝赛尔曲线

三次贝赛尔曲线 图例

使用三次贝赛尔曲线绘制一个心形的图案

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

        //三次贝塞尔曲线
        ctx.beginPath();
        ctx.moveTo(75,40);
        ctx.bezierCurveTo(75,37,70,25,50,25);
        ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
        ctx.bezierCurveTo(20,80,40,102,75,120);
        ctx.bezierCurveTo(110,102,130,80,130,62.5);
        ctx.bezierCurveTo(130,62.5,130,25,100,25);
        ctx.bezierCurveTo(85,25,75,37,75,40);
        ctx.fill();
    }
}
三次贝赛尔曲线
三次贝赛尔曲线

矩形

直接在画布上绘制矩形除去fillRect(),strokeRect(),clearRect()外,也可以使用rect()方法讲一个矩形路径添加到当前路径上面。

rect(x,y,width,height)

绘制一个左上角为(x,y),高度为width以及height的矩形路径。

Path2D对象

在前面的例子中,使用一系列的路径或者内置的绘画命令来绘制图形。Path2D对象用来缓存或者记录绘画命令,以便快速的回顾路径。

Path2D()

Path2D()会返回一个新的Path2D对象。

new Path2D() //空的Path对象
new Path2D(path) //克隆Path对象
new Path2D(d) //从SVG建立Path对象

Path2D 示例

function draw(){
    let canvas = document.getElementById('canas')
    if(canvas.getContext()){
        let ctx = canvas.getContext()
        
        //生成一个矩形的Path2D对象
        let rectAngle = new Path2D()
        rectAngle.rect(10,10,50,50)
        
        //生成一个圆形的Path2D对象
        let circle  = new Path2D()
        circle.arc(100,35,25,0,Math.PI*2,true)
        
        //将对象画到画布上
        ctx.stroke(rectAngle)
        ctx.fill(circle)
    }
}
Path2D示例
Path2D示例

代码中,先生成了一个渲染上下文的ctx对象,然后又单独生成了一个矩形对象和一个圆形对象,最后使用将对象画在画布上。

使用 SVG paths

Path2D API 有一个强大的功能,可以使用SVG path data 来初始化canvas上的路径。

let p =new Path2D("M10 10 h 80 v 80 h -80 Z")
微信图片_20171101115016.png

这条路径先移动到点(m10 10) 然后再水平移动80个单位(v 80) 然后下移80个单位(v 80) 接着左移80个单位(h -80),再回到起点(z)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,607评论 2 32
  • 一、canvas简介 1.1 什么是canvas?(了解) 是HTML5提供的一种新标签 Canvas是一个矩形区...
    Looog阅读 3,860评论 3 40
  • 书中代码示例效果展示:Core HTML5 Canvas Examples 基础知识 canvas元素 canva...
    szu_bee阅读 2,711评论 2 28
  • 最基本的使用创建一个画布所有的操作都在画布的context上面canvas是基于状态而不是基于对象的,比如说颜色都...
    亲爱的孟良阅读 1,630评论 0 4
  • 今天参加202商学院学习、李向平总讲的组织架构很实用。 体验:七定最实用。 转身用:先定事...
    京心达张新波阅读 149评论 0 1