5

Canvas从入门到小猪头

 3 years ago
source link: http://developer.51cto.com/art/202102/646492.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ZnY7Vbz.jpg!mobile

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

一、Canvas简介

1.1 什么是canvas

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

1.2 canvas的坐标系统

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

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

FFjaEj.jpg!mobile

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表示垂直移动。

uuIvQb7.jpg!mobile

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到底允许绘制哪些内容呢?

直线

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

圆弧

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

曲线

// 贝济埃曲线 
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(); 
} 

矩形

// 填充矩形 
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); 
} 

字符串

// 填充字符串 
function drawFillText(ctx, text, x, y) { 
    ctx.fillText(text, x, y); 
} 
// 边框字符串 
function drawStrokeText(ctx, text, x, y) { 
    ctx.strokeText(text, x, y); 
} 

复杂图形绘制——路径

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

BVzUR3A.jpg!mobile

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属性即可设置对应的颜料,对于颜料值主要有以下四种:纯颜色、线性渐变颜色、径向渐变颜色、位图。

纯颜色

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

线性渐变颜色

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(); 
} 

径向渐变颜色

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(); 
} 

位图填充

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(); 
    } 
} 

yYnERrQ.jpg!mobile

2.4 临时保存

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

UbmAbyr.jpg!mobile

// 从左往右是依次绘制(中间为用新的样式填充,最后一个是恢复到原来样式填充) 
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

myiqYju.jpg!mobile

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绘制一个猪头吧,毕竟每个程序员身边肯定有一个陪伴自己的小胖猪,嘿嘿。

3i2U3eJ.jpg!mobile

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(); 

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

n2auQj6.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK