canvas 线段两端的样式
●canvas 中, 是可以设置线段两端的样子的
●我们先来画三个平行线
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
ctx.stroke()
ctx.beginPath()
// 第二个线段
ctx.moveTo(100, 150)
ctx.lineTo(200, 150)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
ctx.stroke()
ctx.beginPath()
// 第三个线段
ctx.moveTo(100, 200)
ctx.lineTo(200, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
ctx.stroke()
ctx.beginPath()
复制代码
●接下来, 我们开始设置两端样式
语法: 工具箱.lineCap = '值'
值 :
=> butt 无
=> round 圆
=> square 方
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
// 两端样式设置为 butt
ctx.lineCap = 'butt'
ctx.stroke()
ctx.beginPath()
// 第二个线段
ctx.moveTo(100, 150)
ctx.lineTo(200, 150)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
// 两端样式设置为 round
ctx.lineCap = 'round'
ctx.stroke()
ctx.beginPath()
// 第三个线段
ctx.moveTo(100, 200)
ctx.lineTo(200, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
// 两端样式设置为 'square'
ctx.lineCap = 'square'
ctx.stroke()
ctx.beginPath()
复制代码
●square 和 round 会让线段稍稍变长
●线段端点样式的颜色会和线段颜色保持一致
canvas 线段拐点的样式
●canvas 在绘制线段拐角的时候, 会自动进行闭合拐角
●我们也可以通过设置, 来设置一下拐角的样子
●先来绘制三个带有拐角的线段
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 150)
ctx.lineTo(100, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
ctx.stroke()
ctx.beginPath()
// 第二个线段
ctx.moveTo(200, 100)
ctx.lineTo(300, 150)
ctx.lineTo(200, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
ctx.stroke()
ctx.beginPath()
// 第三个线段
ctx.moveTo(300, 100)
ctx.lineTo(400, 150)
ctx.lineTo(300, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
ctx.stroke()
ctx.beginPath()
复制代码
●canvas 对于线段拐点默认的样式就是尖角拐点
●我们可以进行一些设置来改变
语法: 工具箱.lineJoin = '值'
值:
=> miter 默认尖角拐点
=> round 创建圆角拐点
=> bevel 创建斜角拐点
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 150)
ctx.lineTo(100, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
// 默认拐点
ctx.lineJoin = 'miter'
ctx.stroke()
ctx.beginPath()
// 第二个线段
ctx.moveTo(200, 100)
ctx.lineTo(300, 150)
ctx.lineTo(200, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
// 圆角拐点
ctx.lineJoin = 'round'
ctx.stroke()
ctx.beginPath()
// 第三个线段
ctx.moveTo(300, 100)
ctx.lineTo(400, 150)
ctx.lineTo(300, 200)
ctx.lineWidth = 10
ctx.strokeStyle = '#000'
// 斜角拐点
ctx.lineJoin = 'bevel'
ctx.stroke()
ctx.beginPath()
复制代码
canvas 填充
●在 canvas 中, 一旦你画出封闭图形以后
●我们不光可以描边, 也可以进行填充, 也就是填满颜色
●先来画一个矩形吧
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineTo(200, 200)
ctx.lineTo(100, 200)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.closePath()
ctx.stroke()
复制代码
●我们这里用的是描边( 工具箱.stroke() )
●是按照痕迹把路线描绘下来
●在 canvas 内, 除了描边, 还有一个叫做填充
●咱们再来一个矩形, 这次我们不进行描边, 而是进行填充
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineTo(200, 200)
ctx.lineTo(100, 200)
// 3. 填充
// 语法: 工具箱.fill()
ctx.fill()
复制代码
●这样, 就会按照我们绘制的路线, 以填充的形式出现一个封闭图形
●注意 : 填充的时候可以不进行图形闭合, 会自动闭合图形以后进行填充
●填充的时候也可以设置填充颜色的设置
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineTo(200, 200)
ctx.lineTo(100, 200)
// 3. 填充
// 3-1. 填充颜色设置
// 语法: 工具箱.fillStyle = '值'
ctx.fillStyle = 'skyblue'
// 3-2. 填充
ctx.fill()
复制代码
●填充是可以和描边一起使用的
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制第一个线段
// 第一个线段
ctx.moveTo(100, 100)
ctx.lineTo(200, 100)
ctx.lineTo(200, 200)
ctx.lineTo(100, 200)
// 自动闭合图形
ctx.closePath()
// 3. 描边
// 3-1. 设置描边样式
ctx.lineWidth = 4
ctx.strokeStyle = 'orange'
// 3-2. 描边
ctx.stroke()
// 4. 填充
// 4-1. 填充颜色设置
ctx.fillStyle = 'skyblue'
// 4-2. 填充
ctx.fill()
复制代码
canvas 的填充规则
●我们发现, 到现在为止, canvas 的绘制, 描边, 填充都很简单
●但是接下来的内容可能稍微复杂一些了
●我们要说一下 canvas 的填充规则
●我们管 canvas 的填充规则叫做 非零填充
例子
●一下子可能说不明白, 我们先来看一个例子
●绘制一个 "回" 形
●注意一个细节 :
○我们绘制的过程
○里面的小正方形我们会按照 顺时针 的方向绘制
○外面的大正方形我们也会按照 顺时针 的方向绘制
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100)
ctx.lineTo(300, 100)
ctx.lineTo(300, 200)
ctx.lineTo(200, 200)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.stroke()
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50)
ctx.lineTo(350, 50)
ctx.lineTo(350, 250)
ctx.lineTo(150, 250)
ctx.stroke()
复制代码
●两个正方形都是这个方向绘制的, 我们接下来把描边的线去掉
●我们来进行一下填充看看
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100)
ctx.lineTo(300, 100)
ctx.lineTo(300, 200)
ctx.lineTo(200, 200)
// ctx.lineWidth = 2
// ctx.strokeStyle = '#000'
// ctx.stroke()
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50)
ctx.lineTo(350, 50)
ctx.lineTo(350, 250)
ctx.lineTo(150, 250)
// ctx.stroke()
// 4. 填充
ctx.fill()
复制代码
●我们发现, 两个都被填充了
●这是因为, 在填充的时候, 就是会一次性把所有的内容都会填充好
●注意 :
○和是否闭合路径 ( 工具箱.closePath() ) 没有关系
○和里外正方形的绘制先后顺序没有关系
●那是怎么回事呢 ?
例子
●先不管是怎么回事, 我们再来看一个例子
●还是绘制一个 "回" 形
●注意一个细节 :
○我们绘制的过程
○里面的小正方形我们会按照 逆时针 的方向绘制
○外面的大正方形我们也会按照 顺时针 的方向绘制
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('#canvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100)
ctx.lineTo(200, 200)
ctx.lineTo(300, 200)
ctx.lineTo(300, 100)
ctx.lineWidth = 2
ctx.strokeStyle = '#000'
ctx.stroke()
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50)
ctx.lineTo(350, 50)
ctx.lineTo(350, 250)
ctx.lineTo(150, 250)
ctx.stroke()
复制代码
●这回两个矩形绘制的时候, 方向不一样了
●我们再来填充一次试试看
●我们会发现, 和刚才填充出来的结果不一样了
●这又是怎么回事呢 ?
●难道和顺时针逆时针有关系吗 ?
非零填充
●其实我们的填充和顺时针逆时针有关系, 但是不是简单的顺逆时针的问题
●概念 :
○从任何一个区域向画布最外层移动
○按照经历最短边计算
○其中经历的顺时针的边记录为 +1
○经历逆时针的边记录为 -1
○只要最终总和不为 零, 那么该区域填充
○如果最终总和为 零, 那么该区域不填充
●听起来很麻烦, 咱们来画布上看一下效果就好了
●这次我们绘制一个稍微复杂一些的图形
●这是两个矩形对接在一起, 一个是顺时针绘制, 一个是逆时针绘制
●我们来分析一下看看
●首先, 最左侧封闭图形
○如果走最短的路线出来的话, 会经历一条顺时针的边
○记录为 +1
○最终为 +1
○所以该区域填充
●然后, 最右侧封闭图形
○经历最短路线出来的话, 会经历一条逆时针的边
○记录为 -1
○最终为 -1
○所以该区域填充
●最后, 中间的封闭图形
○经历最短路线出来的会, 必然会经历一条顺时针的边和一条逆时针的边
○顺时针记录为 +1
○逆时针记录为 -1
○最终为 0
○所以该区域不填充
●此时我们对当前图形进行填充后观察
●这就是最后填充好的样子