要在网页里绘图的话,有两个选择:
1. SVG (Scalable Vector Graphics)
2. HTML5 Canvas API
===
SVG,顾名思义,是矢量图。它在网页里存在的方式和HTML里的页面元素很相似,是以XML一样一层层嵌套的tags组成的。比如,一个红底黑边的圆圈,用SVG来表示是这样的:
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
更多例子:
W3Schools SVG ExamplesSVG的好处:
1. 每一个SVG对象同时也是一个HTML的DOM对象,这意味着你可以在它们上面侦听各种鼠标交互事件,也可以对它加上各种css效果
2. 矢量图,放大缩小都很平滑
SVG本身缺少javascript API,需要第三方库才能简化绘制,比如
Raphael.js和
Bonsai.js。另外数据可视化库
D3.js也是基于SVG的。
===
HTML5 Canvas API则是基于bitmap的绘图,大家所熟悉的processing.js也是基于Canvas API,只是提供了一个和p5一样的语法接口而已。
Canvas API本身的语法其实和processing很相似,只是需要先进行一些准备工作。
首先,在html文档的body里面加入一个canvas标签:
<canvas id="my-canvas" width="500" height="500"></canvas>
<script>
// 接下去的js代码都写在这里
</script>
有了这个,我们就可以在javascript里面获得这个canvas的绘图环境:
var canvas = document.getElementById('my-canvas');
var context = canvas.getContext('2d');
上面的代码获得的context是一个2d绘图环境。如果想要进行有硬件加速的3d绘图,则需要获得webgl绘图环境,但是webgl绘图的api比2d绘图的api要复杂很多,所以留着以后再说。接下来让我们先画个方块:
context.fillRect(10, 10, 100, 100);
是不是和p5的rect()如出一辙?那么换个颜色呢?
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里面的钢笔/路径工具,下面的代码就会容易理解的多:
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()方法:
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上写字也很简单。这里最大的好处是,你可以使用任何用户的浏览器里能显示的字体!
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库。当然了,一家之言,仅供参考。