【译】用一天入门 canvas 和 JavaScript

在最近的 JavaScript 30天挑战中,我有机会了解 HTML 内置 canvas的特性。相对适宜的等级和学习曲线,使我写下整个过程。javascript

HTML canvas 用最简单的方式,使web 开发者可以经过JavaScript在网页上绘制图形。这样,HTML 元素变得更加有趣。css

<canvas> 元素只是个容器,你老是须要使用 JavaScript 来准确绘制图形。有人可能会说,咱们老是能够添加这些点,也能够添加SVG,但这又会多么有趣?html

回到 <canvas> 元素:canvas 在 HTML 页面上是一个矩形。canvas 是默认没有边框和内容的。java

写法像这样:git

<canvas id="canvas" width="200" height="100"></canvas>复制代码

开始

已经作了那么多介绍,让咱们专一于使用简单的原生 JavaScript(不是很旧——ES6)作些有趣的东西。首先,咱们看下初始的文件。github

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>HTML5 Canvas</title>
    <link rel="stylesheeet" href="style.css" />
</head>
<body>
<canvas id="canvas" width="800" height="800"></canvas>
<script src="app.js"></script>
</body>
</html>复制代码


让咱们慢慢讲。咱们有个叫 style.css 的样式层叠表。而后咱们定义一个宽800和高800的 canvas 。最后,咱们在 script 标签里引用了 app.js ,全部魔法都在这里。咱们开始使用 app.js 作一些事情。web

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

canvas.width = window.innnerWidth;
canvas.height = window.innerHeight;

ctx.strokeStyle = "#BADA55"
ctx.lineWidth = 1;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';复制代码


  • 咱们一开始,在第一行选择 canvas 元素保存 到 叫 canvas 的常量里。
  • 而后,咱们在同一个 canvas 中获取2D上下文,并保存到指定的变量中。
  • 设置 canvas 的宽度和高度,分别等于 窗口的宽度和高度。

如今,咱们终于有了画布,继续定义画布的最基本属性。canvas

  • ctx.strokeStyle 设置或返回用于描绘的颜色,渐变色或图案。是的,你理解的对:默认颜色是 #BADASS
  • ctx.lineWidth 设置或返回 当前线条的宽度。咱们把它设置为 1,稍后咱们会讲到这个。
  • ctx.lineJoin 设置或返回 两条线相汇时所建立的边角的类型。咱们设置当两条线交汇时的边角为圆角。
  • ctx.lineCap 设置或返回线的端点的样式。咱们将它设置为圆形,这样当咱们不遇到其余线时,咱们仍然会获得相同的整齐的圆形,具体取决于以前定义的线的宽度。

线咱们已经完成了全部这些工做,让咱们看看如何在画布上绘图。数组

首先,咱们须要为画布上的鼠标移动添加事件侦听器,而后触发一个绘制内容的函数。咱们来看看 app.js 文件中可能添加的内容。浏览器

let isDrawing = fasle;

function draw(e){
    if(!isDrawing) return;
    console.log(e)
}

canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown', () => isDrawing = true);
canvas.addEventListener('mouseup', () => isDrawing = fasle);
canvas.addEventListener('mousedown', () => isDrawing = fasle);复制代码

咱们来说解下:

  • 咱们开始定义一个叫 isDrawing 的变量,来判断用户是否要在画布上画图。咱们会在后面再讲到这个。
  • 如今,咱们有一个叫 draw 的函数,它会被触发,并负责完成整个绘制动做。
  • 最后,咱们增长一个约束条件,确保咱们捕获正确的事件,而且只在须要的时候执行 draw 函数。

经过声明 isDrawing 变量,并将其值设置为 false,咱们在 canvas 元素被加载后设置 canvas 的初始状态,但还不绘制。而后在每一个后续事件侦听器中,咱们使用内嵌函数,并每次根据触发的事件类型更改 变量 isDrawing 变量的值。

draw 函数 的开头,若是 isDrawing 的值为 false ,函数会执行 return 语句。若是 isDrawing 的值为 true, 会执行draw 函数。

绘制函数

咱们来扩展下 draw 函数:

let isDrawing = false;
let lastX = 0;
let lastY = 0;

function draw(e){
    if(!isDrawing) return;
    console.log(e);
    ctx.beginPath();
    ctx.moveTo(lastX,lastY);
    ctx.lineTO(e.offsetX,e.offsetY);
    ctx.stroke();
}复制代码

  • 咱们在开头定义两个全局变量:lastXlastY,并设置他们的初始值为 0.
  • 若是你如今进入浏览器的控制台,你会发现你已经记录了全部鼠标移动。这个 MouseEvent 对象有一些很是重要和有用的属性:

                                               鼠标事件对象

咱们只关注对象里的 offsetXoffsetY 属性。

  • 每次执行 ctx.beginPath ,咱们新建一个路径,或者重置当前的路径。每次事件被触发,咱们希都会执行这个动做。
  • ctx.moveTo 移动路径到画布指定的点上,但还没绘制线。在例子中,在函数外面的全局环境里定义 lastXlastY
  • ctx.lineTo 增长新的点,并从上一个点到新的点之间绘制一条线。
  • ctx.stroke() 真正绘制你已经定义的路径 —— 辛苦了,伙计们!

ctx.lineTo 里,咱们利用事件的属性 offsetXoffsetY 得到 在画布上的最新点的 XY ,只用 ctx.lineTo 绘制一条线。

咱们几乎全部的东西都准备好了。在页面上的每一个鼠标事件都会在画布上绘制一条线——但还有些问题,没有太多酷的东西。因此,让咱们加点酷的东西。

酷!

如今,全部线都是从画布中的(0,0)点绘制的。 咱们将其设置为加载画布或执行 draw 函数时开始绘制的初始点。

让咱们解决这个问题,以得到更好的实时体验。 若是考虑一下,答案很是简单:每次执行draw函数时,咱们都但愿初始点始终是 MouseEvent 对象的 offsetXoffsetY 属性。

经过使用 ES6 的数组解构,咱们能够将变量 lastXlastY 分别重置为 鼠标事件对象的属性 offsetXoffsetY,咱们在 draw 函数的最后执行。咱们来看看加了新东西后的 app.js

function draw(e){
    if(!isDrawing) return;
    console.log(e);
    ctx.beginPath();
    ctx.moveTo(lastX,lastY);
    ctx.lineTo(e.offsetX,e.offsetY);
    ctx.stroke();
    [lastX,lastY] = [e.offsetX,e.offsetY];
}

canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown',(e) => {
    isDrawing = true;
    [lastX,lastY] = [e.offsetX,e.offsetY];
};

canvas.addEventListener('mouseup',()=> isDrawing = false);
canvas.addEventListener('mousemove',()=> isDrawing = false);复制代码
  • mousemove 事件发生时,咱们会执行 draw 函数。而后继续执行,在 draw 函数里使用 ES6 解构 设置 变量 lastXlastY
  • mousedown 事件发生时,首先咱们执行嵌套函数,如你所见,咱们再一次将 变量 lastXlastY 设置为当前事件的偏移属性。这是为了确保当咱们在画布上从一个点移动到另外一个点时,咱们能够在画布上看到这条线。

让它变得丰富多彩,并在笔画中添加一些动态元素。

let hue = 0;
let direction = true;

function draw(e){    
    if(!isDrawing) return;
    console.log(e);
    ctx.strokeStyle = `hsl(${hue},100%,50%)`;
    ctx.beginPath();
    ctx.moveTo(lastX,lastY);
    ctx.lineTo(e.offsetX,e.offsetY);
    ctx.stroke();
    [lastX,lastY] = [e.offsetX,e.offsetY];
    hue++;
    if(hue>=360){
        hue = 0;
    }
    if(ctx.lineWidth >= 75 || ctx.lineWidth <= 1){
        direction = !direction;
    }
    if(direction){
        ctx.lineWidth++;
    } else {
        ctx.lineWidth = 1;
    }
}

canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown',(e) => {
    isDrawing = true;
    [lastX,lastY] = [e.offsetX,e.offsetY];
};

canvas.addEventListener('mouseup',()=> isDrawing = false);
canvas.addEventListener('mousemove',()=> isDrawing = false);复制代码

神圣时刻!!

这还有不少事要处理,咱们一一分解:

  • 我定义一个新的变量 hue ,并设置其值为 0
  • 若是你还不知道色调,为何会很棒,那就去谷歌试试吧,或者只需点击这里就能够了。

在其最简单的形式,hsl 让咱们在从0到360范围里使用相同的彩虹的颜色。 每一个数字都有一个亮度和透明度。 定义 hsl 看起来像这样:hsl(173,99%,50%)。 此处 173 表示颜色 , 99% 是亮度,50% 是透明度。

一样,经过使用 ES6 的模板字符串,来修改 hsl 的值,像这样:

ctx.strokeStyle = `hsl( ${hue}, 100%, 50%)`

接下来,咱们增长 hue 变量的值,该变量在每一个mousemove事件中更改笔触的颜色。 一旦色调值增长到360,咱们将在上述要点的第14行上将 hue 的值重置为0。 但即便咱们不这样作,咱们仍然会有一样的结果。 即使如此,咱们仍是作正确的事吧

if(hue > 360){
    hue = 0
}复制代码

下一步,咱们加点动态的东西,使笔画的宽度实时变化,像这样:

if(ctx.linewidth >= 75 || ctx.lineWidth <= 1){
    direction = ! direction;
}

if(direction){
    ctx.lineWidth++
}else {
    ctx.lineWidth = 0
}复制代码

这里咱们所作的就是首先检查当前的线宽是大于75仍是小于1。 若是是,则将初始值为 true 的变量 direction 取反。

接下来,咱们检查变量 direction 的值是否为true。 若是是,则将 lineWidth 的值增长1,不然将 lineWidth 重置为0

没有不少JavaScript。 若是你正确跟着作,你应该有个漂亮的画布了。

让咱们快速地看一下最终的文件结构是什么样子的。 由于咱们只更改了app.js文件,因此我将只向你展现这一点,由于index.html从一开始就几乎没有变化。

canvas.width = window.innerWidth;canvas.height = window.innerHeight;ctx.strokeStyle = '#BADA55';ctx.lineWidth = 1;ctx.lineJoin = 'round';ctx.lineCap = 'round';let isDrawing = false;let lastX = 0;let lastY = 0;let hue = 0;let direction = true;function draw(e) {    if(!isDrawing) {        return; // 鼠标没有按下时不执行.    }    console.log(e);    ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;    ctx.beginPath();    ctx.moveTo(lastX, lastY);    ctx.lineTo(e.offsetX, e.offsetY);    ctx.stroke();    [lastX, lastY] = [e.offsetX, e.offsetY];    hue++;    if(hue>=360){        hue = 0;    }    if(ctx.lineWidth >= 75 || ctx.lineWidth <= 1) {        direction = !direction;    }    if(direction){        ctx.lineWidth++;    } else {        ctx.lineWidth = 1;    }}canvas.addEventListener('mousemove', draw);canvas.addEventListener('mousedown', (e) => {    isDrawing = true;    [lastX, lastY] = [e.offsetX, e.offsetY];});canvas.addEventListener('mouseup', ()=> isDrawing = false);canvas.addEventListener('mouseout', () => isDrawing = false);复制代码

在 canvas 和 JavaScript 的结合中,介绍的只是冰山一角。 我会鼓励你作更多的研究,使画布看起来更好。 也许添加几个按钮来清除屏幕,或者选择一种特定的颜色在画布上绘制。 有不少选择!

相关文章
相关标签/搜索