Canvas从入门到小猪头

开发 前端
Canvas(画布)是在HTML5中新增的标签用于在网页实时生成图像,可以操作图像内容,是一个可以用JavaScript操作的位图(bitmap)。

[[382744]]

通过本文你将了解canvas简介及其比较常用的方法,并利用canvas实现一个小猪头。

 

一、Canvas简介

1.1 什么是canvas

Canvas(画布)是在HTML5中新增的标签用于在网页实时生成图像,可以操作图像内容,是一个可以用JavaScript操作的位图(bitmap)。

1.2 canvas的坐标系统

canvas的坐标系统如下图所示,其具有如下特点:

  • x轴正方向向右、y轴正方向向下
  • 画布的原点在左上角
  • 横纵坐标单位为像素
  • 每个轴的最小单元为一个像素(栅格)

 

1.3 canvas的绘制流程

  1. 创建一个标签
  2. 获取canvas元素对应的DOM对象,这是一个Canvas对象
  3. 调用Canvas对象的getContext()方法,该方法返回一个CanvasRenderingContext2D对象,该对象即可绘制图形
  4. 调用CanvasRenderingContext2D对象的方法绘图

1.4 canvas的应用领域

canvas这个神奇的东西有很多领域可以得到应用,下面我们一起唠一唠。

  1. 游戏:canvas 在基于 Web 的图像显示方面比 Flash 更加立体、更加精巧,canvas 游戏在流畅度和跨平台方面更优秀,例如这25款canvas游戏
  2. 可视化的库:Echart
  3. banner广告:Canvas 实现动态的广告效果非常合适
  4. 图形编辑器:后续Photoshop能够100%基于Web实现
  5. 微信读书、腾讯文档均是通过canvas实现

二、基础功能

通过第一章对canvas有了初步的认识,本章就按照一个人绘制一幅画的思路进行演化,逐步了解canvas的基本功能,从而更好的使用它实现一些酷炫的效果。

2.1 坐标系选择

当要绘制一幅画时首先要确定坐标系,只有选择好了坐标系之后才好动笔,在canvas中默认坐标系在左上角(X轴正方向向右、Y轴正方向向下),可是有的时候需要变换坐标系才能更方便的实现所需的效果,此时需要变换坐标系,canvas提供了以下几种变换坐标系的方式:

  1. translate(dx,dy):平移坐标系。相当于把原来位于(0,0)位置的坐标原点平移到(dx、dy)点。
  2. rotate(angle):旋转坐标系。该方法控制坐标系统顺时针旋转angle弧度。
  3. scale(sx,sy):缩放坐标系。该方法控制坐标系统水平方向上缩放sx,垂直方向上缩放sy。
  4. transform(a,b,c,d,e,f):允许缩放、旋转、移动并倾斜当前的环境坐标系,其中a表示水平缩放、b表示水平切斜、c表示垂直切斜、d表示垂直缩放、e表示水平移动、f表示垂直移动。

function main() { 
    const canvas = document.getElementById('canvasId'); 
    const ctx = canvas.getContext('2d'); 
    ctx.lineWidth = 4; 
    // 默认 
    ctx.save(); 
    ctx.strokeStyle = '#F00'
    drawCoordiante(ctx); 
    ctx.restore(); 
 
    // 平移 
    ctx.save(); 
    ctx.translate(150, 150); 
    ctx.strokeStyle = '#0F0'
    drawCoordiante(ctx); 
    ctx.restore(); 
 
    // 旋转 
    ctx.save(); 
    ctx.translate(300, 300); 
    ctx.rotate(-Math.PI / 2); 
    ctx.strokeStyle = '#00F'
    drawCoordiante(ctx); 
    ctx.restore(); 
 
    // 缩放 
    ctx.save(); 
    ctx.translate(400, 400); 
    ctx.rotate(-Math.PI / 2); 
    ctx.scale(0.5, 0.5); 
    ctx.strokeStyle = '#000'
    drawCoordiante(ctx); 
    ctx.restore(); 

 
function drawCoordiante(ctx) { 
    ctx.beginPath(); 
    ctx.moveTo(0, 0); 
    ctx.lineTo(120, 0); 
    ctx.moveTo(0, 0); 
    ctx.lineTo(0, 80); 
    ctx.closePath(); 
    ctx.stroke(); 

 
main(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.

2.2 图形绘制

坐标系选择好了之后,就要开始动笔创作大作了,那么canvas到底允许绘制哪些内容呢?

直线

function drawLine(ctx, startX, startY, endX, endY) { 
    ctx.moveTo(startX, startY); 
    ctx.lineTo(endX, endY); 
    ctx.stroke(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

圆弧

function drawCircle(ctx, x, y, R, startAngle, endAngle) { 
    ctx.arc(x, y, R, startAngle, endAngle); 
    ctx.stroke(); 

  • 1.
  • 2.
  • 3.
  • 4.

曲线

// 贝济埃曲线 
function drawBezierCurve(ctx, cpX1, cpY1, cpX, cpY2, endX, endY) { 
    ctx.bezierCurveTo(cpX1, cpY1, cpX, cpY2, endX, endY); 
    ctx.stroke(); 

// 二次曲线 
function drawQuadraticCurve(ctx, cpX, cpY, endX, endY) { 
    ctx.quadraticCurveTo(cpX, cpY, endX, endY); 
    ctx.stroke(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

矩形

// 填充矩形 
function drawFillRect(ctx, x, y, width, height) { 
    ctx.fillRect(x, y, width, height); 

// 边框矩形 
function drawStrokeRect(ctx, x, y, width, height) { 
    ctx.strokeRect( x, y, width, height); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

字符串

// 填充字符串 
function drawFillText(ctx, text, x, y) { 
    ctx.fillText(text, x, y); 

// 边框字符串 
function drawStrokeText(ctx, text, x, y) { 
    ctx.strokeText(text, x, y); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

复杂图形绘制——路径

// 利用路径绘制 
function drawFigureByPath(ctx) { 
    ctx.beginPath(); 
    ctx.moveTo(100, 400); 
    ctx.lineTo(200, 450); 
    ctx.lineTo(150, 480); 
    ctx.closePath(); 
    ctx.fill(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

 

 


 

function main() { 
    const canvas = document.getElementById('canvasId'); 
    const ctx = canvas.getContext('2d'); 
    ctx.lineWidth = 2; 
    ctx.strokeStyle = '#F00'
    ctx.fillStyle = '#F00'
    ctx.font = 'normal 50px 宋体'
    drawLine(ctx, 50, 10, 150, 10); 
    ctx.moveTo(150, 100); 
    drawCircle(ctx, 100, 100, 50, 0, Math.PI); 
    ctx.moveTo(300, 100); 
    drawCircle(ctx, 250, 100, 50, 0, Math.PI * 2); 
    ctx.moveTo(350, 150); 
    drawBezierCurve(ctx, 200, 200, 450, 250, 300, 300); 
    ctx.moveTo(50, 250); 
    drawQuadraticCurve(ctx, 50, 400, 80, 400); 
    drawFillRect(ctx, 100, 300, 100, 50); 
    drawStrokeRect(ctx, 300, 300, 100, 50); 
    drawFillText(ctx, 'I', 100, 400); 
    drawStrokeText(ctx, 'I', 300, 400); 
    drawFigureByPath(ctx); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

2.3 填充风格

利用canvas绘制图形时势必要上点颜料,通过设置fillStyle属性即可设置对应的颜料,对于颜料值主要有以下四种:纯颜色、线性渐变颜色、径向渐变颜色、位图。

纯颜色

function useColorFill(ctx) { 
    ctx.save(); 
    ctx.fillStyle = '#F00'
    ctx.fillRect(10, 10, 100, 100); 
    ctx.restore(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

线性渐变颜色

function useLinearGradientFill(ctx) { 
    ctx.save(); 
    const lg = ctx.createLinearGradient(110, 10, 210, 10); 
    lg.addColorStop(0.2, '#F00'); 
    lg.addColorStop(0.5, '#0F0'); 
    lg.addColorStop(0.9, '#00F'); 
    ctx.fillStyle = lg; 
    ctx.fillRect(120, 10, 100, 100); 
    ctx.restore(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

径向渐变颜色

function useRadialGradientFill(ctx) { 
    ctx.save(); 
    const lg = ctx.createRadialGradient(260, 60, 10, 260, 60, 60); 
    lg.addColorStop(0.2, '#F00'); 
    lg.addColorStop(0.5, '#0F0'); 
    lg.addColorStop(0.9, '#00F'); 
    ctx.fillStyle = lg; 
    ctx.fillRect(230, 10, 100, 100); 
    ctx.restore(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

位图填充

function useImageFill(ctx) { 
    ctx.save(); 
    const image = new Image(); 
    image.src = 'https://dss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=442547030,98631113&fm=58'
    image.onload = function () { 
        // 创建位图填充 
        const imgPattern = ctx.createPattern(image, 'repeat'); 
        ctx.fillStyle = imgPattern; 
        ctx.fillRect(340, 10, 100, 100); 
        ctx.restore(); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

 

2.4 临时保存

用一只画笔在画某个美女时,忽然来了灵感需要画另一个帅哥,这个时候又不想放弃这个美女,则就需要将当前画美女的颜料、坐标等状态进行暂存,等到画完帅哥后恢复状态进行美女的绘制。在canvas中可以通过save()和restore()方法实现,调用save()方法后将这一时刻的设置放到一个暂存栈中,然后可以放心的修改上下文,在需要绘制之前的上下文时,可以调用restore()方法。

// 从左往右是依次绘制(中间为用新的样式填充,最后一个是恢复到原来样式填充) 
function main() { 
    const canvas = document.getElementById('canvasId'); 
    const ctx = canvas.getContext('2d'); 
    ctx.fillStyle = '#F00'
    ctx.fillRect(10, 10, 100, 100); 
    ctx.save(); 
    ctx.fillStyle = '#0F0'
    ctx.fillRect(150, 10, 100, 100); 
    ctx.restore(); 
    ctx.fillRect(290, 10, 100, 100); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

2.5 引入外部图像

有的时候需要引入外部图片,然后对外部图片进行像素级别的处理,最后进行保存。

  1. 绘制图像:drawImage
  2. 取得图像数据:getIamgeData
  3. 将修改后的数据重新填充到Canvas中:putImageData
  4. 输出位图:toDataURL

function main() { 
    const canvas = document.getElementById('canvasId'); 
    const ctx = canvas.getContext('2d'); 
    const image = document.getElementById('image'); 
    // 绘制图像 
    ctx.drawImage(image, 0, 0); 
    // 获取图像数据 
    const imageData = ctx.getImageData(0, 0, image.width, image.height); 
    const data = imageData.data; 
    for (let i = 0, len = data.length; i < len; i += 4) { 
        const red = data[i]; 
        const green = data[i + 1]; 
        const blue = data[i + 2]; 
 
        const average = Math.floor((red + green + blue) / 3); 
 
        data[i] = average; 
        data[i + 1] = average; 
        data[i + 2] = average; 
    } 
 
    imageData.data = data; 
    ctx.putImageData(imageData, 0, 0); 
    document.getElementById('result').src = canvas.toDataURL('image/png'); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

三、猪头实战

学习了这么多,就利用canvas绘制一个猪头吧,毕竟每个程序员身边肯定有一个陪伴自己的小胖猪,嘿嘿。


 

[[382751]]

 

 

function main() { 
    const canvas = document.getElementById('canvasId'); 
    const ctx = canvas.getContext('2d'); 
    ctx.lineWidth = 4; 
    ctx.strokeStyle = '#000'
    ctx.fillStyle = '#ffd8e1'
    ctx.translate(260, 20); 
    drawPigEar(ctx); 
    ctx.save(); 
    ctx.rotate(Math.PI / 2); 
    drawPigEar(ctx); 
    ctx.restore(); 
    drawPigFace(ctx); 
    ctx.save(); 
    ctx.translate(-100, -100); 
    drawPigEye(ctx); 
    ctx.restore(); 
    ctx.save(); 
    ctx.translate(100, -100); 
    drawPigEye(ctx); 
    ctx.restore(); 
    ctx.save(); 
    ctx.translate(0, 60); 
    drawPigNose(ctx); 
    ctx.restore(); 

 
function drawPigEar(ctx) { 
    ctx.save(); 
    ctx.beginPath(); 
    ctx.arc(-250, 0, 250, 0, -Math.PI / 2, true); 
    ctx.arc(0, -250, 250, -Math.PI, Math.PI / 2, true); 
    ctx.closePath(); 
    ctx.fill(); 
    ctx.stroke(); 
    ctx.restore(); 

 
function drawPigFace(ctx) { 
    ctx.save(); 
    ctx.beginPath(); 
    ctx.arc(0, 0, 250, 0, Math.PI * 2); 
    ctx.fill(); 
    ctx.stroke(); 
    ctx.closePath(); 
    ctx.restore(); 

 
function drawPigEye(ctx) { 
    ctx.save(); 
    ctx.fillStyle = '#000'
    ctx.beginPath(); 
    ctx.arc(0, 0, 20, 0, Math.PI * 2); 
    ctx.closePath(); 
    ctx.fill(); 
    ctx.restore(); 

 
function drawPigNose(ctx) { 
    ctx.save(); 
    ctx.fillStyle = '#fca7aa'
    ctx.beginPath(); 
    ctx.ellipse(0, 0, 150, 100, 0, 0, Math.PI * 2); 
    ctx.closePath(); 
    ctx.fill(); 
    ctx.stroke(); 
    ctx.save(); 
    ctx.translate(-60, 0); 
    drawPigNostrils(ctx); 
    ctx.restore(); 
    ctx.save(); 
    ctx.translate(60, 0); 
    drawPigNostrils(ctx); 
    ctx.restore(); 
    ctx.restore(); 

 
function drawPigNostrils(ctx) { 
    ctx.save(); 
    ctx.fillStyle = '#b55151'
    ctx.beginPath(); 
    ctx.ellipse(0, 0, 40, 60, 0, 0, Math.PI * 2); 
    ctx.closePath(); 
    ctx.fill(); 
    ctx.stroke(); 
    ctx.restore(); 

 
main(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.

本文转载自微信公众号「执鸢者」,可以通过以下二维码关注。转载本文请联系执鸢者公众号。

 

责任编辑:武晓燕 来源: 执鸢者
相关推荐

2021-09-01 22:58:22

Canvas标签

2017-06-26 09:15:39

SQL数据库基础

2012-02-29 00:49:06

Linux学习

2025-02-24 10:07:10

2013-06-06 13:42:48

OSPF入门配置

2010-02-06 15:31:18

ibmdwAndroid

2009-07-22 14:55:16

ibmdwAndroid

2019-07-02 14:17:18

API网关网关流量

2016-12-08 22:39:40

Android

2017-05-09 08:48:44

机器学习

2022-06-10 08:17:52

HashMap链表红黑树

2022-10-20 08:02:29

ELFRTOSSymbol

2021-11-29 14:18:05

Nuxt3静态Nuxt2

2024-02-26 08:52:20

Python传递函数参数参数传递类型

2021-12-12 18:15:06

Python并发编程

2017-03-25 20:30:15

2010-11-08 10:20:18

2017-01-03 16:57:58

2022-09-02 15:11:18

开发工具

2024-07-03 10:09:29

点赞
收藏

51CTO技术栈公众号