乐趣区

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

退出移动版