Just Do It - 开启canvas之旅

致懂得努力的我们

夫君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。夫学须静也,才须学也,非学无以广才,非志无以成学。

淫慢则不能励精,险躁则不能治性。年与时驰,意与日去,遂成枯落,多不接世,悲守穷庐,将复何及!

2018.12.12晚上 带大家一起实现你画我猜的画板

画板

主要内容

  • Canvas api讲解
  • 绘画
  • 橡皮擦
  • 图片生成
  • 本地保存
  • 画笔切换

公开课时间:
12月12日 晚上 20:00~21:30

在线地址:
https://ke.qq.com/course/58689

教室地址:
北京昌平区回龙观东大街地铁A口出,向西走100米(紧邻同仁堂)二楼201教室

Canvas简述

Canvas 是HTML5新增的元素,让我们通过JavaScript脚本来绘制图像, 可以用JavaScript在上面绘制各种图表、动画等。

基于Canvas的一些库:

Canvas基本用法

创建一个画布

<canvas id="canvas" width="300" height="300">
  <p>你的浏览器不支持Canvas</p>
</canvas>

如果你的浏览器不支持canvas标签,则标签内的文字会被显示出来

canvas跟img元素很相像,唯一不同的是它没有src和alt属性,canvas标签(除了选择器)只有width和height两个属性,canvas 如果不设置宽高,默认 300*150。

获取渲染上下文

var canvas = document.querySelector('#canvas')

// 获取2D画布的渲染上下文
var ctx = canvas.getContext('2d')

context.getContext() 获取渲染上下文,该对象提供了用于在画布上绘图的方法和属性

提示:在未来,如果 canvas 标签扩展到支持 3D 绘图,getContext() 方法可能允许传递一个 "3d" 字符串参数。

console.log(ctx) // 2D渲染上下文对象
image

从画一条直线开始

JS

// 通过canvas元素设置宽高
canvas.width = 400
canvas.height = 400

// 设置线条宽度
ctx.lineWidth = 10

// 设置画笔开始绘制坐标(落笔点)
ctx.moveTo(100, 100)
// 连接点
ctx.lineTo(300, 300)

// 设置绘制颜色
ctx.strokeStyle = 'red'
// 根据moveTo和lineTo定制的路径 进行绘制
ctx.stroke()

页面效果

image

绘制路径

canvas绘图是基于路径的,肉眼看不到,先进行路径绘制 然后再通过 fill(填充)或 stroke(描边)等方法进行填充或绘制。

涉及api讲解

  • lineWidth
    设置当前绘制线条的宽度。默认值:1像素

  • moveTo(x, y)
    相当于画笔落笔点 开始一条路径 相对于画布左上角 x轴坐标 y轴坐标

  • lineTo(x, y)
    指定连接点坐标,自动和上一个坐标点进行连接 绘制路径 相对于画布左上角 x轴坐标 y轴坐标

  • strokeStyle
    设置通过stroke 绘制描边的颜色。默认值:#000

  • stroke
    根据moveTo和lineTo定制的路径 进行绘制描边

绘制几何图形

绘制三角形

HTML

<canvas id="canvas" width="600" height="600"></canvas>

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

// 设置绘制线条宽度
ctx.lineWidth = 10
// 设置绘制线条颜色
ctx.strokeStyle = 'red'
// 设置填充颜色
ctx.fillStyle = 'pink'

// 开始一条全新的绘制路径
ctx.beginPath()

// 制定路径
ctx.moveTo(300, 50)
ctx.lineTo(550, 500)
ctx.lineTo(50, 500)

// 闭合路径
ctx.closePath()
// 进行填充
ctx.fill()
// 进行绘制
ctx.stroke()

页面效果

image

涉及api讲解

  • fillStyle
    设置填充色 默认值:#000

  • fill
    根据fillStyle 对当前路径进行填充

  • beginPath
    开始一条全新的绘制路径

  • closePath
    闭合当前路径,创建从当前点到开始点的路径

路径相关方法讲解

1.beginPath
开始一个全新绘制的路径

假如我要绘制两条颜色不同的平行线条

HTML

<canvas id="canvas" width="600" height="600"></canvas>

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

ctx.moveTo(100, 100)
ctx.lineTo(500, 100)
ctx.strokeStyle = 'yellow'
ctx.stroke()

ctx.moveTo(100, 200)
ctx.lineTo(500, 200)
ctx.strokeStyle = 'red'
ctx.stroke()

ctx.moveTo(100, 300)
ctx.lineTo(500, 300)
ctx.strokeStyle = 'blue'
ctx.stroke()

不加beginPath情况下,后面stroke 会从最初路径开始坐标重新绘制一遍 导致之前路径描边颜色发生叠加

image

在每一次moveTo之前 加上beginPath方法 开始一个全新路径

JS

// 通常习惯在第一次的时候也加上beginPath 让代码看上去一致
ctx.beginPath()
ctx.moveTo(100, 100)
ctx.lineTo(500, 100)
ctx.strokeStyle = 'yellow'
ctx.stroke()

ctx.beginPath()
ctx.moveTo(100, 200)
ctx.lineTo(500, 200)
ctx.strokeStyle = 'red'
ctx.stroke()

ctx.beginPath()
ctx.moveTo(100, 300)
ctx.lineTo(500, 300)
ctx.strokeStyle = 'blue'
ctx.stroke()

添加beginPath后

效果如下:

image

2.closePath

假如我们绘制一个几何图形如上,closePath会创建从当前结束点到开始点的路径 形成闭合路径

JS

ctx.lineWidth = 10
ctx.strokeStyle = 'red'
ctx.fillStyle = 'pink'

ctx.beginPath()

ctx.moveTo(300, 50)
ctx.lineTo(550, 500)
ctx.lineTo(50, 500)

ctx.fill()
ctx.stroke()

不加closePath时 是这样

image

添加上closePath

JS

ctx.lineWidth = 10
ctx.strokeStyle = 'red'
ctx.fillStyle = 'pink'

ctx.beginPath()

ctx.moveTo(300, 50)
ctx.lineTo(550, 500)
ctx.lineTo(50, 500)

// 闭合路径
ctx.closePath()

ctx.fill()
ctx.stroke()

添加closePath

效果如下:

image

closePath例子:

JS

ctx.lineWidth = 10
ctx.strokeStyle = 'red'

ctx.beginPath()
ctx.moveTo(300, 100)
ctx.lineTo(500, 500)

ctx.lineTo(300, 500)
ctx.closePath()

ctx.lineTo(100, 500)
ctx.stroke()

效果如下:

image

绘制矩形

rect(x, y, width, height)

创建一个矩形,需要通过stroke() 进行绘制

JS

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

ctx.strokeStyle = 'red'
ctx.beginPath()
ctx.rect(100, 100, 300, 300)
ctx.stroke()

效果如下:

image

strokeRect(x, y, width, height)

根据strokeStyle指定颜色进行矩形描边,绘制无填充色的矩形。
同 rect+stroke 一样效果

JS

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

ctx.beginPath()
ctx.lineWidth = 10
ctx.strokeStyle = 'red'
ctx.rect(100, 100, 300, 300)
ctx.stroke()

效果如下:

无填充矩形

fillRect(x, y, width, height)

绘制具有填充色的矩形

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

ctx.beginPath()
ctx.fillStyle = 'blue'
ctx.fillRect(100, 100, 300, 300)

效果如下:

填充矩形

完整绘制一个矩形

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

ctx.lineWidth = 10
ctx.fillStyle = 'blue'
ctx.strokeStyle = 'red'

// 方式一
ctx.fillRect(100, 100, 200, 200)
ctx.strokeRect(100, 100, 200, 200)

// 方式二
ctx.rect(320, 100, 200, 200)
ctx.fill()
ctx.stroke()
矩形

绘制带阴影矩形

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

// 阴影模糊度
ctx.shadowBlur = 20
// 阴影颜色
ctx.shadowColor = 'black'

// 绘制带蓝色填充色的矩形
ctx.fillStyle = 'blue'
ctx.fillRect(100, 100, 200, 200)
阴影矩形

线条相关属性

lineCap

设置线条两端线帽的样式 context.lineCap=“butt|round|square”;

lineCap属性值

注意:"round" 和 "square" 值会使线条略微变长。

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

ctx.lineWidth = 20
ctx.beginPath()
ctx.moveTo(100, 100)
ctx.lineTo(500, 100)
// 默认值
ctx.lineCap = 'butt'
ctx.stroke()

ctx.beginPath()
ctx.moveTo(100, 200)
ctx.lineTo(500, 200)
// 两末端圆形线帽
ctx.lineCap = 'round'
ctx.stroke()

ctx.beginPath()
ctx.moveTo(100, 300)
ctx.lineTo(500, 300)
// 两末端方形线帽
ctx.lineCap = 'square'
ctx.stroke()

效果如下:

lineCap属性

绘制圆形

arc

JavaScript 语法:

context.arc(x,y,r,sAngle,eAngle,counterclockwise);

参数

image

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

ctx.lineWidth = 10
ctx.beginPath()

ctx.arc(300, 300, 100, 0, Math.PI * 2, false)

ctx.strokeStyle = 'blue'
ctx.fillStyle = 'red'
ctx.fill()
ctx.stroke()

效果如下:

image

开始角与结束角:

image

绘制半圆

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')

ctx.lineWidth = 10
ctx.beginPath()

// 逆时针绘制
ctx.arc(300, 300, 100, 0.5 * Math.PI, 1.5 * Math.PI, true)

ctx.strokeStyle = 'green'
ctx.fillStyle = 'red'
ctx.fill()
ctx.stroke()

效果如下:

半圆

绘制五角星

JS

var canvas = document.querySelector('#canvas')
var ctx = canvas.getContext('2d')


// 计算x轴坐标值
// X = Math.cos(弧度) * 半径 + 圆心x轴坐标
var cx = function(i, r, x, d) {
  return Math.cos((d + i * 72) / 180 * Math.PI) * r + x
}

// 计算y轴坐标值
// Y = Math.sin(弧度) * 半径 + 圆心y轴坐标
var sy = function (i, r, y, d) {
  return -Math.sin((d + i * 72) / 180 * Math.PI) * r + y
}

/**
 * @desc 绘制五角星
 * @param  {Object} ctx  2d渲染上下文
 * @param  {Number} r  内圆半径
 * @param  {Number} R  外圆半径
 * @param  {Number} x  圆心x轴位置
 * @param  {Number} y  圆心y轴位置
 * @param  {Color} fillColor  填充色
 * @param  {Color} strokeColor  描边色
 *
 */
function drawStart(ctx, r, R, x, y, fillColor, strokeColor) {
  ctx.fillStyle = fillColor
  ctx.strokeStyle = strokeColor

  ctx.beginPath()
  // 绘制五个角
  for (var i = 0; i < 5; i++) {
    // 绘制外圆路径
    ctx.lineTo( cx(i, R, x, 18), sy(i, R, y, 18))
    // 绘制内圆路径
    ctx.lineTo( cx(i, r, x, 54), sy(i, r, y, 54))
  }
  ctx.closePath()
  ctx.fill()
  ctx.stroke()
}
drawStart(ctx, 100, 200, 300, 300, 'red', 'white')

效果如下:

五角星

五角星数学知识补下

image
// 求弧度
// 为L = n(圆心角度数)× π × r(半径)/180(角度制)

// 对边与斜边的比叫做正弦(sine),记作sin
// 邻边与斜边的比叫做余弦(cosine),记作cos
// 对边与邻边的比叫做正切(tangent),记作tan

// JS Math.sin() 与 Math.cos() 用法
// Math.sin(x) x 的正玄值。返回值在 -1.0 到 1.0 之间
// Math.cos(x) x 的余弦值。返回的是 -1.0 到 1.0 之间的数
// 这两个函数中的X 都是指的“弧度”而非“角度”,

// 弧度的计算公式为:2 * PI / 360 * 角度
// 30° 角度 的弧度 = 2 * PI / 360 * 30

// 如何得到圆上每个点的坐标?
// 在圆的坐标系中 半径r为斜边 x轴为邻边 y轴为对边
// 正弦 = y / r
// 余弦 = x / r

// 解决思路:根据三角形的正弦、余弦来得值
// 假设一个圆的圆心坐标是(a, b),半径为r

// X = 圆心x轴坐标 + 弧度的余弦值 * 半径
// 则圆上每个点的X坐标= a + Math.cos(2 * Math.PI / 360) * r

// Y = 圆心y轴坐标 + 弧度的正弦值 * 半径
// Y坐标 = b + Math.cos(2 * Math.PI / 360) * r

// 计算x轴坐标值
// X = Math.cos(弧度) * 半径 + 圆心x轴坐标
var cx = function(i, r, x, d) {
  return Math.cos((d + i * 72) / 180 * Math.PI) * r + x
}

// 计算y轴坐标值
// Y = Math.sin(弧度) * 半径 + 圆心y轴坐标
var sy = function (i, r, y, d) {
  return -Math.sin((d + i * 72) / 180 * Math.PI) * r + y
}

ctx.lineTo(X, Y)
image

该文章配套源码地址

https://github.com/Lwenli1224/canvas_base

获取更多学习资源

珠峰微信号

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