Author Topic: HTML5 2D绘图简介 (1)  (Read 3427 times)

evan

  • Moderator 版主
  • Newbie
  • *****
  • Posts: 28
  • HTML5/JS @ Google Creative Lab
    • personal site
HTML5 2D绘图简介 (1)
« on: November 26, 2012, 10:46:30 AM »
要在网页里绘图的话,有两个选择:

1. SVG (Scalable Vector Graphics)
2. HTML5 Canvas API

===

SVG,顾名思义,是矢量图。它在网页里存在的方式和HTML里的页面元素很相似,是以XML一样一层层嵌套的tags组成的。比如,一个红底黑边的圆圈,用SVG来表示是这样的:
Code
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
更多例子:W3Schools SVG Examples

SVG的好处:
1. 每一个SVG对象同时也是一个HTML的DOM对象,这意味着你可以在它们上面侦听各种鼠标交互事件,也可以对它加上各种css效果
2. 矢量图,放大缩小都很平滑

SVG本身缺少javascript API,需要第三方库才能简化绘制,比如Raphael.jsBonsai.js。另外数据可视化库D3.js也是基于SVG的。

===

HTML5 Canvas API则是基于bitmap的绘图,大家所熟悉的processing.js也是基于Canvas API,只是提供了一个和p5一样的语法接口而已。
Canvas API本身的语法其实和processing很相似,只是需要先进行一些准备工作。

首先,在html文档的body里面加入一个canvas标签:
Code
<canvas id="my-canvas" width="500" height="500"></canvas>
<script>
    // 接下去的js代码都写在这里
</script>

有了这个,我们就可以在javascript里面获得这个canvas的绘图环境:
Code
var canvas = document.getElementById('my-canvas');
var context = canvas.getContext('2d');

上面的代码获得的context是一个2d绘图环境。如果想要进行有硬件加速的3d绘图,则需要获得webgl绘图环境,但是webgl绘图的api比2d绘图的api要复杂很多,所以留着以后再说。接下来让我们先画个方块:
Code
context.fillRect(10, 10, 100, 100);

是不是和p5的rect()如出一辙?那么换个颜色呢?
Code
context.fillStyle = 'rgb(255, 0, 0)'; //这里可以用任意合法的css颜色, 比如 '#FF0000' 或者 'red',还可以用rgba()
context.fillRect(10, 10, 100, 100);

和fillRect()类似的还有clearRect()和strokeRect()两个方法。其中clearRect()通常用来抹掉画布上已经存在的内容。
相比之下,画一条线会稍微复杂一点。
和p5不同的是,canvas里的路径的创建和着色是分开进行的。比如canvas的rect(),创建的是一个长方形的路径,但是在你使用context.fill()或者context.stroke()之前,这个路径是未上色的。
如果你熟悉Illustrator或者Photoshop里面的钢笔/路径工具,下面的代码就会容易理解的多:
Code
context.beginPath(); //开启一条新的路径
context.strokeStyle = 'rgb(0, 255, 0)';  //设置路径颜色为绿色
context.lineWidth = 3; //设置线条粗细
context.lineCap = 'round'; //设置线条端点的形状
context.moveTo(10, 10); //把画笔头移动到x=10,y=10,此时还没有开始绘制任何路径
context.lineTo(110, 110); //从(10,10)创建一条直线路径到(110,110),此时的路径是尚未着色的
context.stroke(); //给当前路径着色

接下来再画个圆。和p5直接不同的是,canvas画圆圈需要先创建一个圆圈的路径,然后再填充它。
这里我们使用canvas context的arc()方法:
Code
context.fillStyle = 'rgb(255, 255, 255)';
context.beginPath();
context.arc(60, 60, 30, 0, Math.PI * 2); //参数依次是:x, y, 半径,起始角度,结束角度。同样的,此时的路径没有着色
context.closePath();
context.fill(); //填充路径

和lineTo(), arc(), rect()等类似的路径方法还有arcTo(), quadraticCurveTo(), bezierCurveTo()等,可以画出更复杂的曲线。

往canvas上写字也很简单。这里最大的好处是,你可以使用任何用户的浏览器里能显示的字体!
Code
context.font = "12px Helvetica, Arial"; // 这里可以用任何合法的css font属性
context.fillStyle = "rgb(0, 0, 0)";
context.textAlign = 'center'; // 相对绘制坐标居中文字
context.textBaseline = 'middle'; // 相对绘制坐标垂直对齐
context.fillText('Awesome!', 60, 60); // 把文字写在正中。没有上面两行的话,默认是对齐在文字的左下角。

这一篇就先讲这些,下一篇会将canvas里的transform,加载绘制外部图片,以及对图片数据进行像素处理。总的来说,原理和p5是差不多的,只是语法略有不同而已。
在熟悉canvas的过程中,可以参考非常实用的canvas API cheat sheet.

===

最后,谈一下我对p5.js的看法。
如果大家只是单纯地想要重复利用已有的p5代码,用p5.js自然是不二的选择;但如果想要真正地用HTML5进行创意编程,我强烈建议还是先系统地学习一下javascript,然后了解一下原生的HTML5 API是怎么运作的。
在我看来,p5.js有两个主要问题:
1. p5.js为了兼容p5的语法,牺牲了不少性能和灵活性。如果真的要做比较复杂的web端项目,还是得了解和使用javascript。
2. p5过度集成化的理念不适合HTML5。java的p5提供了很多方便,因为java是一个门坎略高的语言,比如想要用java弄出一个窗口在里面绘图,从0开始是很复杂的事情。p5提供了一个简化的环境,把互动开发各种常用的功能集成到一起。但是HTML5本身其实就已经是一个以互动为主的平台,比如用javascript画一个圆圈,其实比用p5.js复杂不了多少。另外如音频,另一方面,javascript作为目前开源氛围最浓厚的语言,几乎你想解决的问题都有优质的开源项目可以使用(参见本版概览贴),使用p5.js,很多时候不如使用更专一地解决你的问题的其他js库。当然了,一家之言,仅供参考。
« Last Edit: November 26, 2012, 11:04:08 AM by evan »
新浪微博@尤小右
twitter@youyuxi

evan

  • Moderator 版主
  • Newbie
  • *****
  • Posts: 28
  • HTML5/JS @ Google Creative Lab
    • personal site
Re: HTML5 2D绘图简介 (1)
« Reply #1 on: November 26, 2012, 10:58:49 AM »
附完整代码:

Code
<!DOCTYPE html>
<html>
    <head>
        <title>HTML5 Canvas 入门</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <head>
    <body>
        <canvas id="my-canvas" width="500" height="500"><canvas>
        <script>
            var canvas = document.getElementById('c');
            var context = canvas.getContext('2d');

            context.fillStyle = 'rgb(255, 0, 0)'; //这里可以用任意合法的css颜色, 比如 '#FF0000' 或者 'red',还可以用rgba()
            context.fillRect(10, 10, 100, 100);

            context.beginPath(); // 开启一条新的路径
            context.strokeStyle = '#00FF00';  // 设置路径颜色为绿色
            context.lineWidth = 5; // 设置线条粗细
            context.lineCap = 'round';
            context.moveTo(10, 10); // 把画笔头移动到x=10,y=10
            context.lineTo(110, 110); // 从(10,10)创建一条直线路径到(110,110)
            context.stroke(); // 给当前路径上色

            context.fillStyle = "rgb(255,255,255)";
            context.beginPath();
            context.arc(60, 60, 30, 0, Math.PI * 2); //参数依次是:x, y, 半径,起始角度,结束角度。同样的,此时的路径没有着色
            context.closePath();
            context.fill(); //填充路径

            context.font = "12px Helvetica, Arial"; // 这里可以用任何合法的css font属性
            context.fillStyle = "rgb(0, 0, 0)";
            context.textAlign = 'center'; // 相对绘制坐标居中文字
            context.textBaseline = 'middle'; // 相对绘制坐标垂直对齐
            context.fillText('Awesome!', 60, 60);
        </script>
    </body>
</html>
新浪微博@尤小右
twitter@youyuxi

Tags: