直接使用 JavaScript

Paper.js 架构

为了理解如何直接从 JavaScript 使用 Paper.js,而不使用 PaperScript 自动机制,我们首先需要解释一下 Paper.js 的架构。使用PaperScript 时,每个脚本都在自己的作用域范围内运行,即 PaperScope 对象。库提供的全局 paper 对象也是 PaperScope 对象。这样有助于我们理解 将作用域视为执行上下文 的概念。

引入作用域是为了让页面上的多个示例具有单独的 PaperScript 上下文,每个示例都有自己的视图和项目,共享同一份库的代码,但是彼此无法访问。它们可以被视为带有共享代码的沙盒“插件”。

每个作用域或上下文都包含一些描述其状态的对象,例如开放的 projects 列表,活动 project 的引用,代表 canvas 元素的 views 列表,当前活动的 view,鼠标 tools 列表,当前活动的 tool 等。

我们来解释一下作用域,项目,视图和工具之间的关系:每个作用域可以包含一个或多个项目,这些项目通过一个或多个视图显示(每个视图代表一个 Paper.js canvas)。视图与具体项目无关,事实上视图渲染了可见区域内具有物体项的所有可见项目。工具可以在任意视图中的任意项目上使用,只要它们属于同一作用域。

设置作用域

直接使用 JavaScript 时,大多数情况下,只需要一个作用域即可。 在这个作用域内,可以通过 new Project()new View(canvas) 构造函数创建多个项目或视图。

最简单的方法是使用现有的 paper 对象,并使用它的 paperScope.setup(canvas) 方法为我们初始化空项目和视图。 我们复用了使用 Paper.js中的示例,以便了解直接使用 JavaScript 时还需要什么:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <!-- 加载 Paper.js 库 -->
  5. <script type="text/javascript" src="js/paper.js"></script>
  6. <!-- 定义内联 JavaScript -->
  7. <script type="text/javascript">
  8. // 当 DOM 渲染完毕时执行代码
  9. window.onload = function() {
  10. // 获取 canvas 对象的引用
  11. var canvas = document.getElementById('myCanvas');
  12. // 为 canvas 创建一个空项目和视图:
  13. paper.setup(canvas);
  14. // 创建一个 Paper.js 路径 在其中绘制线条:
  15. var path = new paper.Path();
  16. // 给画笔上色
  17. path.strokeColor = 'black';
  18. var start = new paper.Point(100, 100);
  19. // 移动到起始点并从那开始画线
  20. path.moveTo(start);
  21. // 注意 Point 对象上的加法运算在 JavaScript 中不再有效
  22. // 我们需要调用 add() 函数:
  23. path.lineTo(start.add([ 200, -50 ]));
  24. // 绘制视图:
  25. paper.view.draw();
  26. }
  27. </script>
  28. </head>
  29. <body>
  30. <canvas id="myCanvas" resize></canvas>
  31. </body>
  32. </html>

到此为止,如果我们将这个示例与 PaperScript 中编写的示例进行比较,我们会发现一些差异。 除了上面示例中的代码之外,我们还需要:

  • 在 DOM 加载完毕时注册处理程序,在此之前我们无法使用 canvas。
  • 告诉 paper 对象为 canvas 设置 Project 和 View。 除了传递 canvas 对象,我们也可以将 canvas 元素的 ID 作为字符串传递给 paper 对象。 在 PaperScript 中,会通过canvas ="ID"属性自动绑定。
  • 通过 paper 对象访问所有 Paper.js 的类和对象,因为它们不再是全局的。
  • 在 Point 和 Size 对象上使用数学函数而不是运算符。
  • 最后绘制视图,因为现在只有在设置了 view.onFrame 处理函数时才会自动绘制。

请注意:

教程引用 中的所有示例都假设你使用的是 PaperScript。 如果你直接使用 JavaScript,需要牢记这些差异。

请注意:

在上面的代码中,我们使用 window.onload = handler 来获取 DOM 加载完毕时的回调。如果你正在使用诸如 jQuery 之类的框架,则可以使用 $(document).ready(handler) 来注册 DOM-Ready 事件,该事件在 onload 事件之前触发。

作用域全局化

通过 paper 对象访问所有的类和对象可能不太方便,因此这里有两种策略来规避它。

最直接的方法是将 paper 对象的所有字段复制到全局范围。我们可以手动完成,如果只有一个 project,view 和 tool,这样很有效。 但是,如果有多个,对活动的(project,view 和 tool)的全局引用将不会保持最新状态。 幸运的是,我们可以在内部执行一些 JavaScript 技巧,以保持引用同步:paper.install(window)。 这样我们可以重写上面例子的代码:

  1. // 通过把 paper 注入到 window,使 paper 作用域全局化:
  2. paper.install(window);
  3. window.onload = function() {
  4. // 直接从 canvas 的 id 设置:
  5. paper.setup('myCanvas');
  6. var path = new Path();
  7. path.strokeColor = 'black';
  8. var start = new Point(100, 100);
  9. path.moveTo(start);
  10. path.lineTo(start.add([ 200, -50 ]));
  11. view.draw();
  12. }

如果觉得污染全局作用域不是很好的选择,那么第二种策略是通过使用可怕的with()语句来绕过它。 这是Paper.js 在内部应用的一个小技巧,用来在它自己的 PaperScope 对象中确定 PaperScript 的作用域:

  1. window.onload = function() {
  2. paper.setup('myCanvas');
  3. with (paper) {
  4. var path = new Path();
  5. path.strokeColor = 'black';
  6. var start = new Point(100, 100);
  7. path.moveTo(start);
  8. path.lineTo(start.add([ 200, -50 ]));
  9. view.draw();
  10. }
  11. }

设置事件处理程序

PaperScript 在声明为全局函数时会辨识几个特殊事件处理程序,而在 JavaScript 中,需要手动将这些处理程序配置在对应的对象上。 这两个处理程序是 onFrameonResize,它们都属于 View 类。 如上例所示,如果我们使用 paperScope.setup(canvas) 函数,就会自动为我们创建 view。 所以我们要做的就是在现有的 view 对象上设置这些处理程序:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script type="text/javascript" src="js/paper.js"></script>
  5. <script type="text/javascript">
  6. paper.install(window);
  7. window.onload = function() {
  8. paper.setup('myCanvas');
  9. var path = new Path.Rectangle([75, 75], [100, 100]);
  10. path.strokeColor = 'black';
  11. view.onFrame = function(event) {
  12. // 每一帧时,路径旋转3度:
  13. path.rotate(3);
  14. }
  15. }
  16. </script>
  17. </head>
  18. <body>
  19. <canvas id="myCanvas" resize></canvas>
  20. </body>
  21. </html>

你知道吗?

你可以在教程创建动画中阅读有关动画的信息。

使用工具

就像使用视图处理程序一样,PaperScript 通过使工具处理程序看起来像是全局的来简化和隐藏对 Tool 对象的处理,并且,如果存在这些处理程序如:onMouseDown,onMouseUp,onMouseDrag,onMouseMove 等等,则会为我们动态创建一个 tool。

在 JavaScript 中,我们需要自己创建工具,手动给它们设置处理程序。 这种方法的优点是更加清晰,多个 tool 的处理程序也再正常不过。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script type="text/javascript" src="js/paper.js"></script>
  5. <script type="text/javascript">
  6. paper.install(window);
  7. window.onload = function() {
  8. paper.setup('myCanvas');
  9. // 创建一个简单的绘画工具:
  10. var tool = new Tool();
  11. var path;
  12. // 定义一个 mousedown 和 mousedrag 处理程序
  13. tool.onMouseDown = function(event) {
  14. path = new Path();
  15. path.strokeColor = 'black';
  16. path.add(event.point);
  17. }
  18. tool.onMouseDrag = function(event) {
  19. path.add(event.point);
  20. }
  21. }
  22. </script>
  23. </head>
  24. <body>
  25. <canvas id="myCanvas" resize></canvas>
  26. </body>
  27. </html>

你知道吗?

你可以在教程创建鼠标工具中阅读有关鼠标工具的信息。

多工具

下面这个示例说明了创建多个绘图工具并使用一些简单的 UI 来在它们之间切换是多么简单,这里有两个 HTML 链接,分别用来激活这两种工具:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script type="text/javascript" src="js/paper.js"></script>
  5. <script type="text/javascript">
  6. paper.install(window);
  7. // 保持两个 tool 的全局引用,
  8. // 以此让下面的 html 链接可以直接访问。
  9. var tool1, tool2;
  10. window.onload = function() {
  11. paper.setup('myCanvas');
  12. // 创建两个绘画工具
  13. // tool1 画直线
  14. // tool2 画云
  15. // 共享同一个mouseDown 事件:
  16. var path;
  17. function onMouseDown(event) {
  18. path = new Path();
  19. path.strokeColor = 'black';
  20. path.add(event.point);
  21. }
  22. tool1 = new Tool();
  23. tool1.onMouseDown = onMouseDown;
  24. tool1.onMouseDrag = function(event) {
  25. path.add(event.point);
  26. }
  27. tool2 = new Tool();
  28. tool2.minDistance = 20;
  29. tool2.onMouseDown = onMouseDown;
  30. tool2.onMouseDrag = function(event) {
  31. // 使用 arcTo 命令画云的线
  32. path.arcTo(event.point);
  33. }
  34. }
  35. </script>
  36. </head>
  37. <body>
  38. <a href="#" onclick="tool1.activate();">Lines</a>
  39. <a href="#" onclick="tool2.activate();">Clouds</a>
  40. <canvas id="myCanvas" resize></canvas>
  41. </body>
  42. </html>