写点什么

canvas 从入门到猪头

用户头像
执鸢者
关注
发布于: 2021 年 02 月 21 日
canvas从入门到猪头

关注公众号“执鸢者”,回复“资料”获取 500G 资料(各“兵种”均有),还有专业交流群等你一起来潇洒。(哈哈)


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

一、Canvas 简介

1.1 什么是 canvas

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

1.2 canvas 的坐标系统

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

  • x 轴正方向向右、y 轴正方向向下

  • 画布的原点在左上角

  • 横纵坐标单位为像素

  • 每个轴的最小单元为一个像素(栅格)

1.3 canvas 的绘制流程

  1. 创建一个<canvas></canvas>标签

  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();
复制代码

2.2 图形绘制

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

  1. 直线

function drawLine(ctx, startX, startY, endX, endY) {    ctx.moveTo(startX, startY);    ctx.lineTo(endX, endY);    ctx.stroke();}
复制代码
  1. 圆弧

function drawCircle(ctx, x, y, R, startAngle, endAngle) {    ctx.arc(x, y, R, startAngle, endAngle);    ctx.stroke();}
复制代码
  1. 曲线

// 贝济埃曲线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. 矩形

// 填充矩形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. 字符串

// 填充字符串function drawFillText(ctx, text, x, y) {    ctx.fillText(text, x, y);}// 边框字符串function drawStrokeText(ctx, text, x, y) {    ctx.strokeText(text, x, y);}
复制代码
  1. 复杂图形绘制——路径

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



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);}
复制代码

2.3 填充风格

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

  1. 纯颜色

function useColorFill(ctx) {    ctx.save();    ctx.fillStyle = '#F00';    ctx.fillRect(10, 10, 100, 100);    ctx.restore();}
复制代码
  1. 线性渐变颜色

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. 径向渐变颜色

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. 位图填充

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();    }}
复制代码


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);}
复制代码

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');}
复制代码

三、猪头实战

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


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.关注公众号执鸢者,领取学习资料(前端“多兵种”资料),定期为你推送原创深度好文


发布于: 2021 年 02 月 21 日阅读数: 71
用户头像

执鸢者

关注

让前端知识变的简单可依赖。 2019.09.05 加入

以脑图分享前端知识,让知识变的简单可依赖。

评论

发布
暂无评论
canvas从入门到猪头