Day08 - HTML5 Canvas 实现彩虹画笔绘画板指南-阿里云开发者社区

开发者社区> 开发与运维> 正文

Day08 - HTML5 Canvas 实现彩虹画笔绘画板指南

简介:

Day08 - HTML5 Canvas 实现彩虹画笔绘画板指南

作者:©liyuechun
简介:JavaScript30Wes Bos 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 8 篇。完整中文版指南及视频教程在 从零到壹全栈部落

项目效果

用 HTML5 中的 Canvas 的路径绘制实现一个绘画板,可供鼠标画画,颜色呈彩虹色渐变,画笔大小同样呈渐变效果。这部分不涉及 CSS 内容,全部由 JS 来实现。

涉及特性

Canvas:

  • 模板骨架
  • 基本属性

    • getContext()
    • strokeStyle
    • fillStyle
    • fillRect
    • lineCap
    • lineJoin
  • 路径绘制

    • beginPath()
    • lineTo()
    • moveTo()

    鼠标事件处理:

  • mousemove
  • mousedown
  • mouseup
  • mouseout

过程指南

  1. 获取 HTML 中的 <canvas> 元素,并设定宽度和高度
  2. .getContext('2d') 获取上下文,下面以 ctx 表示
  3. 设定 ctx 基本属性

    • 描边和线条颜色
    • 线条宽度
    • 线条末端形状
  4. 绘画效果

    1. 设定一个用于标记绘画状态的变量
    2. 鼠标事件监听,不同类型的事件将标记变量设为不同值
    3. 编写发生绘制时触发的函数,设定绘制路径起点、终点
  5. 线条彩虹渐变效果(运用 hsl 的 h 值的变化,累加)
  6. 线条粗细渐变效果(设定一个范围,当超出这个范围时,线条粗细进行逆向改变

Canvas相关知识

Canvas_API

HelloWorld简单介绍

一、 模板骨架

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>HTML5 Canvas 实现彩虹画笔绘画板</title>
  <script type="text/javascript">
    function draw() {
      var canvas = document.getElementById('tutorial');
      if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
      }
    }
  </script>
  <style type="text/css">
    canvas {
      border: 1px solid black;
    }
  </style>
</head>

<body>
  <canvas id="tutorial" width="150" height="150"></canvas>
</body>

</html>
  • canvas> 元素
<canvas id="tutorial" width="150" height="150"></canvas>

canvas 看起来和 img 元素很相像,唯一的不同就是它并没有 srcalt 属性。实际上,canvas 标签只有两个属性——widthheight。这些都是可选的,并且同样利用 DOM properties 来设置。当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲。

  • 渲染上下文(The rendering context)
var canvas = document.getElementById('tutorial');
var ctx = canvas.getContext('2d');

canvas元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。

canvas起初是空白的。为了展示,首先脚本需要找到渲染上下文,然后在它的上面绘制。canvas元素有一个叫做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。getContext()只有一个参数,上下文的格式。对于2D图像而言,基本教程,你可以使用CanvasRenderingContext2D

  • 检查支持性

替换内容是用于在不支持 canvas 标签的浏览器中展示的。通过简单的测试getContext()方法的存在,脚本可以检查编程支持性。

var canvas = document.getElementById('tutorial');

if (canvas.getContext){
    //支持
  var ctx = canvas.getContext('2d');
  // drawing code here
} else {
   //不支持
  // canvas-unsupported code here
}

二、一个简单例子

一开始,让我们来看个简单的例子,我们绘制了两个有趣的长方形,其中的一个有着alpha透明度。我们将在接下来的例子里深入探索一下这是如何工作的。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>HTML5 Canvas 实现彩虹画笔绘画板</title>
  <script type="text/javascript">
    function draw() {
      var canvas = document.getElementById('tutorial');
      if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect (10, 10, 55, 50);

        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
        ctx.fillRect (30, 30, 55, 50);
      }
    }
  </script>
  <style type="text/css">
    canvas {
      border: 1px solid black;
    }
  </style>
</head>

<body onload="draw();">
  <canvas id="tutorial" width="300" height="300"></canvas>
</body>

</html>

效果图:

项目源码分析

源码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>HTML5 Canvas</title>
  <style>
    html,
    body {
      margin: 0;
      overflow: hidden;
    }

    canvas {
      overflow: hidden;
    }
  </style>
</head>

<body>
  <canvas id="draw" width="800" height="800" style="overflow:auto;"></canvas>
  <script>

    // 1.获取canvas节点
    const canvas = document.querySelector('#draw');

    if (canvas.getContext) {
      //支持
      var ctx = canvas.getContext('2d');
      // drawing code here
    } else {
      //不支持
      // canvas-unsupported code here
      console.log("canvas-unsupported code here");
    }

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

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

    ctx.lineWidth = 90;
    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.strokeStyle = "#f00";
    ctx.fillStyle = "#f00";

    let hue = 0;
    let direction = true;
    let x = 0;
    let y = 0;

    function draw(e) {
      if (!isDrawing) return;


      x = e.offsetX;
      y = e.offsetY;


      //        彩虹效
      ctx.strokeStyle = `hsl(${ hue }, 90%, 50%)`;
      if (hue >= 360) hue = 0;
      hue++;


      //        控制笔触大小
      if (ctx.lineWidth > 120 || ctx.lineWidth < 10) {
        direction = !direction;
      }
      if (direction) {
        ctx.lineWidth++;
      } else {
        ctx.lineWidth--;
      }

      //        控制绘制路径
      ctx.beginPath();

      ctx.moveTo(lastX, lastY);
      ctx.lineTo(x, y);
      ctx.stroke();

      [lastX, lastY] = [x, y];

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

    canvas.addEventListener('mousemove', draw);
    canvas.addEventListener('mouseup', () => isDrawing = false);
    canvas.addEventListener('mouseout', () => isDrawing = false);
  </script>

</body>

</html>

源码分析

Canvas

canvas宽高设置

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

属性

  • lineCap:笔触的形状,有 round | butt | square 圆、平、方三种。
  • lineJoin:线条相交的样式,有 round | bevel | miter 圆交、斜交、斜接三种。
  • lineWidth:线条的宽度
  • strokeStyle:线条描边的颜色
  • fillStyle:填充的颜色

方法

  • beginPath():新建一条路径
  • stroke():绘制轮廓
  • moveTo():(此次)绘制操作的起点
  • lineTo():路径的终点

彩虹渐变颜色——HSL

在这个挑战中,涉及到改变线条的颜色,如何实现彩虹的渐变效果?我们需要利用 HSL 色彩模式,首先可以去这个网站 http://mothereffinghsl.com 感受一下 HSL 不同色彩值对应的效果。

  • H(hue) 代表色调,取值为 0~360,专业术语叫色相
  • S 是饱和度,可以理解为掺杂进去的灰度值,取值为 0~1
  • L 则是亮度,取值也是 0~1,或者百分比。

    这之中 H 值从 0 到 360 的变化代表了色相的角度的值域变化,利用这一点就可以实现绘制时线条颜色的渐变了,只需要在它的值超过 360 时恢复到 0 重新累加即可。

let hue = 0;

ctx.strokeStyle = `hsl(${ hue }, 100%, 50%)`;    
if(hue >= 360) hue = 0;
hue++;

除此之外,如果想实现黑白水墨的颜色,可以将颜色设置为黑色,通过透明度的改变来实现深浅不一的颜色。

控制笔触大小

 //        控制笔触大小
 if (ctx.lineWidth > 120 || ctx.lineWidth < 10) {
   direction = !direction;
 }
 if (direction) {
   ctx.lineWidth++;
 } else {
   ctx.lineWidth--;
 }

上面的代码中,根据线条的宽度的变化来控制direction的值,根据direction的值来控制线宽是增加还是减少。

控制线条路径

 //        控制绘制路径
 ctx.beginPath();

 ctx.moveTo(lastX, lastY);
 ctx.lineTo(x, y);
 ctx.stroke();

// 坐标重置
 [lastX, lastY] = [x, y];

事件监听代码逻辑分析

canvas.addEventListener('mousedown', (e) => {
<!--开始绘图-->
 isDrawing = true;
 <!--绘图起始坐标初始化-->
 [lastX, lastY] = [e.offsetX, e.offsetY];
});

<!--鼠标移动时,调用draw方法-->
canvas.addEventListener('mousemove', draw);
<!--鼠标抬起时,将isDrawing置为false-->
canvas.addEventListener('mouseup', () => isDrawing = false);
<!--当鼠标不在可绘图区域范围内时,将isDrawing置为fals-->
canvas.addEventListener('mouseout', () => isDrawing = false);

源码下载

Github Source Code

春哥简介

简介: 资深讲师,全栈工程师;区块链、高可用架构技术爱好者。
个人博客:http://liyuechun.org
新浪微博:黎跃春-追时间的人
github:http://github.com/liyuechun

技术交流

  • 区块链技术交流QQ群:348924182
  • 「区块链部落」官方公众号

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章