paperJS框架学习破冰01-鼠标跟随式三D球状动画效果

目录

1.引言

2.基本描述

3.开始使用

3.1.入门

3.2.基础构建

4.案例:鼠标跟随式3d球状动画

4.1.尝试构建

4.2.多个path

4.3.动画

4.4.长度变化速率曲线

4.5.颜色渐变

4.6.增大数量

4.7.鼠标跟随

4.8.裸眼3D

5.总结


1.引言

    最近工作中对HTML页面特效有一些需求,于是在工作之余就找了一些封装的非常酷眩的JS动画API。其中paperJS就是一个能够让人眼前一亮,紧接着再一亮,最后眼前一黑的“好用”动画API。这里是他的官网:paperJS官网。在官网中paperJS自称为是一把处理矢量图形脚本的“Swiss Army Knife(瑞士军刀)”,足见其功能与信心的强大。在官网实例http://paperjs.org/examples/中提供的特效已经能让我们交出膝盖,但是事实上实例中所提供的展示功能尚不足paperJS全部功能的冰山一角,所以越是深入了解paperJS就越会喜欢这把锋利的刀具。

    然而,注意我对这个非常好用的工具API用了“眼前一黑”的修饰方式。我这里并不是想要表达一种亮瞎眼的程度来夸奖它,而是指这么一个优秀的动画API居然 没有汉化教程!没有汉化教程!没有汉化教程!是的,重要事情说三遍。反正至少我在各种我所已知的途径中我没有找到相对完整权威的教程,只有一些非常零散的使用心得。但是没有中文教程其实这也不是什么问题,毕竟人家官网也都说了这个API对于初学者来说也是非常容易简单的...

...简单个锤子啊!一点都不简单好吗?如果相比于学习2400w行开源内核的谷歌浏览器难度来说,的确paperJS好简单啊!但是初学者没有这个功力啊!难道是我太笨了所以感觉一点都不简单吗?居然除了JavaScript脚本之外还要自己写paperScript脚本好吧啦?!

    OK回归正题。其实客观来讲paperJS的逻辑的确并不复杂,只不过为了完成一些特效有时逻辑并不是最重要的,可能还需要一些空间思维和动画创造力。所以下面我们通过一个最简单的官方案例剖析(整个案例跟随学习过程大约需要1~2个小时左右),来一起感受体验一下paperJS。后续我也会继续深入学习paperJS,争取能够尝试让每一个初学者对这个好用的工具都能用的得心应手。毕竟说实话,这样好用的工具不可多得。

2.基本描述

    paper.js框架是一个基于canvas运行的开源图形脚本框架,这意味着我们在使用paper.js的时候并不需要任何额外的框架依赖,直接下载paper.js框架文件就可以进行特效的编辑了。

    另外,通过框架中提供的API我们可以快速的构建出符合我们需求的矢量图形与贝塞尔曲线,并将他们付诸于各种动画效果。幸运的是这些API都是十分语义化的。

3.开始使用

    相信大部分人看到这个简洁的首页时候都会懵逼一下的,我也不例外(懵逼原因是感觉画风不对)。但是仔细看就会发现左侧边的菜单其实还是蛮明确的。对于初学者可以先看看【Examples(实例)】来感受一下paper.js的犀利和各种特效,然后我们在开始着手准备构建我们自己的案例。相信我,这很有必要!因为看过之后你才能更好的感受他的酷炫。

    OK差不多了,我们进入正题吧。

3.1.入门

    首先点击【Tutorials(教程)】进入paper.js的官方教程,然后在页面右边的【Getting Started】下方点击【Working with paper.js】进入基本说明位置。

    在进入页面后会有一些文字描述,感兴趣可以去看一下详细内容(推荐自己翻译,毕竟有时候机翻不准)。不过如果你不想花大把时间,那看我总结的也可以:

  • paper.js框架引入方式和普通js脚本引入方式没什么区别,script标签走起即可
  • paper.js脚本却必须在<script type="text/paperscript" canvas="页面canvas标签id属性值"></script>样式的脚本内部编辑
  • paper.js框架的导出对象为paper,并且在paperScript内部编辑的时候并不需要考虑全局作用域污染的问题
  • paper.js框架内提供的API与系统API并不冲突,并且在paperScript内部可以随意使用像window、document这类系统API

基本就这四点了解一下就可以。然后继续看下面提供给我们的具体实例。

具体案例脚本代码如下,注释我都尽量给翻译过来了。

<!DOCTYPE html>
<html>
<head>
<!-- 加载 Paper.js 库文件 -->
<script type="text/javascript" src="js/paper.js"></script>
<!-- 定义paperscript脚本, 并作用于页面id值是xxx的canvas标签 -->
<script type="text/paperscript" canvas="myCanvas">
	// 创建一个paper.js中的“Path”对象,并准备在Path对象中绘制图形
	var path = new Path();
	// 设置Path对象的画笔颜色
	path.strokeColor = 'black';
	// 设置Path对象的画笔初始落笔点
        var start = new Point(100, 100);
	// 设置Path对象的画笔运动轨迹
	path.moveTo(start);
	// 设置Path对象沿运动轨迹绘制线条
	path.lineTo(start + [ 100, -50 ]);
</script>
</head>
<body>
	<canvas id="myCanvas" resize></canvas>
</body>
</html>

整个案例的目的和作用就是从(100,100)坐标点向(200,50)坐标点绘制一条黑色的线。看起来和canvas标签的基本使用差不多:获取canvas对象、获取上下文、设置起始点、设置画笔色、绘制线条...事实的确就是这样的。不过结合上下文内容,在这个最最基础的案例中我们知道了paper.js在绘制过程中一个重点:paper.js必须先创建一个path,然后才能通过对path操作来完成绘制。path就好比是HTML中的一个标签,cocos中的一个精灵。

3.2.基础构建

    对于paper.js,在官网中于我们初学者来说需要了的内容到这里就足够了!其余的内容暂时不需要考虑,过多的信息反而不利于新内容的学习。如果真的在后续学习中有用到什么,直接过来查询即可。现在让我们开始动手制作第一个paper.js的基础案例吧。

    首先下载paper.js框架文件,直接在侧边栏【Download】内下载即可,当然npm、bower或者直接download都可以。(我这里为了便于初学者理解和使用,选择了最基础的框架文件下载而没有使用npm。)

然后使用IDE创建如下代码(我这里用的是VSCode),这就是我们刚才在paper.js官网中看到的案例。还是比较好理解的不是吗?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        canvas{
            width: 500px;
            height: 300px;
            border: 1px solid black;
        }
    </style>
    <!-- 1.引入脚本 -->
    <script src="./js/paper-full.js"></script>
    <!-- 
        3.构建一个paperScript 
            (1)text/paperscript 表示当前脚本运行的paperscript脚本,与其他脚本互不冲突
            (2)canvas属性表示当前paperScript脚本将要作用于的canvas元素,其属性值为页面canvas元素的id属性值
    -->
    <script type='text/paperscript' canvas="mycanvas"> 
        var path = new Path();
        path.strokeColor = 'black';
        
        var start = new Point(100,100);
        path.moveTo(start);
        path.lineTo(start+[100,-50]);
    
    </script>
</head>
<body>
    <!-- 2.创建canvas画布,并添加id属性给paperScript做关联准备 -->
    <canvas id="mycanvas" resize></canvas>
</body>
</html>

OK!基本部分就这些,你如果看懂了上面的内容,那么paper.js就可以开始着手准备编辑我们的第一个案例了。什么?你说你还什么都不知道?没关系,相信我,即便你看了再多官方文档的内容也会依然是一头雾水,所以就让我们在了解了基本语法构成之后,直接从实战出发来学习它吧!

4.案例:鼠标跟随式3d球状动画

本案例其实就是刚才我们下载框架文件中自带的一个案例,而我们需要通过这个案例来学习其背后的原理和API中的内容。

4.1.尝试构建

    先把骨架搭建出来,引入paper.js脚本,并准备好paperScript脚本,然后和页面canvas完成绑定。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
            html,body {
                margin: 0;
                overflow: hidden;
                height: 100%;
                background: black;
            }
            canvas[resize] {
                width: 100%;
                height: 100%;
            }
        </style>
    <!-- 1.引入脚本 -->
    <script src="./js/paper-full.js"></script>
    <!--3.构建paperScript -->
    <script type='text/paperscript' canvas="mycanvas"> 
       //...
    </script>
</head>
<body>
    <!-- 2.创建canvas,添加id属性 -->
    <canvas id="mycanvas" resize></canvas>
</body>
</html>

    然后功能拆分,案例中的图形实际上就是一个从中心点向外发散的N个三角形。那么我能不能先制作出从中心点向外发散的一个三角形呢?感觉应该不是特别困难,但是我现在无从下手,所以我们此时去看官方案例的源码。从源码中获知了下面的一段内容:

...
    <script type='text/paperscript' canvas="mycanvas"> 
        //4.创建一条绘画轨迹(可以认为就是一个绘画元素)
        var path = new Path({
            //对绘画轨迹进行属性设置,只设置了填充颜色属性
            fillColor: {
                hue: 1,    //色相(只要有就行,多少无所谓,因为色相是在色相环上去颜色的基准)
                saturation: 1,    //饱和度,0为黑白~1为饱满
                brightness: Math.random()    //明暗度,0为不可见~1为明暗交替
            },
            closed: true    //图形关闭,封闭
        });
        //5.在页面上绘制
        var position = view.center;
        path.segments = [
            position,
            position + { angle: 1 / 12 * 360, length: 300 },
            position + { angle: 2 / 12 * 360, length: 300 }
        ];
    </script>
...

    在代码第4点中:我们在canvas上创建了一个path,并对这个path设置了fillcolor(填充色),并确认这个path将会是一个封闭图形。不过说实话这才刚开始就直接给了我一个下马威,new Path()后面的参数到底可以写什么?怎么写?官方文档我找了好久愣是没有找到具体说明!?仅仅只是在【Tutorials->Paths->Using Color and Style】的文章说明里面找到了一点点蛛丝马迹,还得是连蒙带猜的那种,不过万幸我实际测试结果的确符合了我的预期。

    具体解释简单说就是给path对象设置一个JSON类型的参数(这里我们知道path允许写JSON格式参数了),参数中指明了当前path的填充色和关闭样式。(这里我们知道参数中至少允许写fillColor和close两个字段了)

    而第5点则是我灵机一动的产物。根据基础案例中的提示,我当时在想这个path既然被创建出来了就必然需要进行绘制,而案例中图形的初始位置是画布canvas的中心点,所以paper.js是不是有可能提供一种手段来直接获取canvas中心点,并提供绘制方法呢?皇天不负有心人,经过一番逼死强迫症的查找我在这两个位置找到了相关说明

  • view.center表示获取当前paperScript对应canvas画布的中心点坐标(x,y),view是一个paper.js的内置对象。
  • path.segments = [point1, [point2, [point3]]];则可以用来绘制一个封闭的,三个点构成的图形。

      path.segments = [
            position,
            position + { angle: 1 / 12 * 360, length: 300 },
            position + { angle: 2 / 12 * 360, length: 300 }
        ];

所以这句话的意思就是:以position坐标为原点,以300px长度限制。从360度的十二分之一开始到十二分之二结束构图来绘制一个三角形。运行效果不负期待。

4.2.多个path

   万事开头难,有了一个接下来的事情就好办了。一个path是一个三角,多来几个不久构成案例中的样子了?说干就干,这一步没什么难度,直接循环创建就OK。12个path只是我随便规定的,因为刚才制作三角形的时候我用了十二分之一,所以这里也是为了能让path们画出一个完整的圆来(原谅我这个强迫症)

...
    <script type='text/paperscript' canvas="mycanvas"> 
        //6.创建复数个path,意味着复数个“精灵”
        var amount = 12;
        for (var i = 0; i < amount; i++) {
            var path = new Path({
                fillColor: ...
            });
        }
        var position = view.center;
        //7.有复数个path
        for (var i = 0; i < amount; i++) {
            
            //path.segments = [
            //    position,
            //    position + { angle: (i + 1) / 12 * 360, length: length },
            //    position + { angle: (i + 2) / 12 * 360, length: length }
            //];
        }
    </script>
...

然而我写到这里的时候遇到了个问题,导致我写不下去了:

  • path一个的时候,可以直接指定path.segments来进行样式渲染。但是一旦path多了,不知道如何获取一个所谓的【path集合】,从而导致无法对所有的path进行渲染(每个path都需要三个点来渲染)

于是老规矩,官网走起。

在这里找到了一个相关说明:project对象中提供了一个名为activeLayer的对象,该对象表示当前正在处于激活状态的canvas。而通过寻找activeLayer对象的children属性,即可获知当前处于这个canvas上的所有path对象。即:

var paths = project.activeLayer.children;

于是顺利推进代码

...
    <script type='text/paperscript' canvas="mycanvas"> 
        //6.创建复数个path,意味着复数个“精灵”
        var amount = 12;
        for (var i = 0; i < amount; i++) {
            var path = new Path({
                fillColor: {
                    hue: 1,
                    saturation: 1,
                    brightness: Math.random()
                },
                closed: true
            });
        }
        var position = view.center;
        //7.获取页面上的所有path,按序渲染
        var children = project.activeLayer.children;
        for (var i = 0; i < amount; i++) {
            var path = children[i];
            path.segments = [
                position,
                position + { angle: (i + 1) / 12 * 360, length: 300},
                position + { angle: (i + 2) / 12 * 360, length: 300}
            ];
        }
    </script>
...

4.3.动画

    canvas动画的特征这里就不多解释了,一句话:每帧图像重新渲染。所以我们如果想让我们的这个图像动起来,那么paper.js必然需要提供一个能够对页面进行“刷新”的方法(也就是canvas动画中我们用settimeInterval来手动强制刷新的这个操作)。而这个方法就是onFrame()函数。

于是,代码继续推进,在onFrame中对每帧动画要渲染的path进行绘制即可。(我对长度进行了Math.random,以此保证看起来有差异)

...
    <script type='text/paperscript' canvas="mycanvas">  
        //6.创建复数个path,意味着复数个“精灵”
        var amount = 12;
        for (var i = 0; i < amount; i++) {
            var path = new Path({
                fillColor: {
                    hue: 1,
                    saturation: 1,
                    brightness: Math.random()
                },
                closed: true
            });
        }
        
        //5.在页面上绘制(不能逐一绘制了,需要在onFrame中进行绘制)
        var position = view.center;
        //9.获取页面上的所有path
        var children = project.activeLayer.children;

        //7.有复数个“精灵”,而渲染只有一个,因此不得不借助onFrame帧渲染函数
        function onFrame() {
            //8.有多少精灵,在一帧中就渲染多少次
            for (var i = 0; i < amount; i++) {
                //10.必须保证绘制时每个path绘制为止有所不同,不然会产生覆盖
                var path = children[i];
                length = Math.random()*300;
                path.segments = [
                    position,
                    position + { angle: (i + 1) / 12 * 360, length: 300 },
                    position + { angle: (i + 2) / 12 * 360, length: 300 }
                ];
            }
        }
    </script>
...

 

瞧,实现了不是么?虽然由于onFrame每秒钟刷新60次导致非常鬼畜,但是毕竟实现了我们的功能不是么?

4.4.长度变化速率曲线

    每一帧中,每一个path的长度渲染都是完全随机的,因此会看到页面中的长度是一个眼花缭乱非常不舒服的效果。这里我思考了很久如何才能实现目标案例中的增长和衰退效果,而最终得到的答案和源码中的答案也不谋而合。那就是:三角函数曲线。简单来说使用正弦函数曲线就足够了。因为正弦函数曲线是一条震荡波,因此如果将长度与正弦函数的y坐标结合,在加以绝对值,就能够得到一条在单位时间内正向起伏的长度变化曲线。

而此时还面临的最后一个问题就是,正弦函数曲线的确y轴表示了长度,但是我们还欠缺一个随时间变化的增量x。也就是说我们必须有一个x变量随时间变化,才能通过sin(x)来获取到正弦函数曲线上的对应y值。而幸运的是这个属性paper.js框架已经考虑到了。在onFrame帧渲染函数中提供了一个名为event的隐式参数(此event和事件对象event完全不是一个东西),该参数如果想要使用则必须显式写出。

event(base类型对象){
  count //累加器
  delta //增量
  time //每帧动画执行时间
}

//base类型对象是paper.js自带的一个onFrame渲染函数的事件对象

其中的base.count会随着onFrame的执行而不断进行累加,相当于一个paperJS自带的天然计数器。因此借助与sin正弦函数与这个计数器,我们就可以制作出随正弦曲线变化的length了。即 length = Math.sin(count) * 300

    别急!虽然此时看起来已经可以控制path进行起伏变化,但是现在还有几个亟待解决的逻辑小问题:

  • 如果path长度真的采用上面的公式,那么太棒了,所有的path长度在每次渲染的时候都一样。换句话说每个path的长度都是一起变大变小的,因为他们的长度都取决于count,而count在一次渲染中是固定值。因此我们可以通过循环变量i来保证每一个path在正弦函数上的y值不同,即 length = Math.sin(i + count) * 300
  • 此时,第二个问题出现了,前面说过正弦函数是存在正负的。而长度这一概念是不存在正负的,因此我们需要“绝对值”一下,即 length = Math.abs(Math.sin(i + count) * 300)
  • 最后一个不是问题的问题,那就是对于正弦函数sin而言,sin的参数每变化1°,对应在正弦曲线上的数值变化都会是一个非常可观的数字,何况这个数字还被放大了300倍(不明白的自己拿计算器按sin45和sin46差多少,再乘以300呢)。说的再直白一点就是我们需要根据count计数器来决定【在正弦函数上的length值】,但是又不希望这个变化十分迅速,所以最好的解决办法就是“降频”,也就是除以一个数字。把count缩小,这样计数器每次变化造成的length改变就变成了原有的多少分之一。即 length = Math.abs(Math.sin(i + count/40) * 300)

特别注意:event参数必须显示声明,这个event和事件对象event没有一毛钱关系。另外/40表示变化减慢40倍,40是随便写的。

    分析完毕,代码推进。

...
    <script type='text/paperscript' canvas="mycanvas"> 
        var amount = 12;
        for (var i = 0; i < amount; i++) {
            var path = new Path({
                fillColor: {
                    hue: 1,
                    saturation: 1,
                    brightness: Math.random()
                },
                closed: true
            });
        }
        var position = view.center;
        var children = project.activeLayer.children;
        function onFrame(event) {
            for (var i = 0; i < amount; i++) {
                var path = children[i];
                //11.正弦曲线取值
                length = Math.abs(Math.sin(i + event.count/40) * 300);
                path.segments = [
                    position,
                    position + { angle: (i + 1) / 12 * 360, length: length },
                    position + { angle: (i + 2) / 12 * 360, length: length }
                ];
            }
        }
    
    </script>
...

4.5.颜色渐变

    颜色改变其实在我们的案例中算是一个比较容易实现的步骤,关键点就是需要你必须知道【色相环】这一概念才能做出这个功能。感谢大神v_xchen_v的博客【shaderforge学习笔记】 Hue节点(色相节点)提供了详尽的说明。在这里我们只需要知道两点即可:

  • 色相环共计360度,重复增长无所谓,正负亦无所谓,不外乎就是361度的色相与1度的色相相同,或者-180度和正180度相同。
  • paper.js中hue用来设置色相,而且设置数字即可表示度

所以只要path的色相在单位时间内发生变化就可以,而前面我们提到event.count是一个天然计数器。因此就有了下面的代码:

path.fillColor.hue = (event.count % 360) - length;

说实话length的负增量有没有都无所谓,但是添加这个负增量能够保证颜色的变化更明显一些,因为length也会伴随事件发生变化。

...
    <script type='text/paperscript' canvas="mycanvas"> 
        ...
        function onFrame(event) {
            for (var i = 0; i < amount; i++) {
                ...
                //12.不断改变当前轨迹的色相,基于计数器和长度来变更。
                path.fillColor.hue = (event.count % 360) - length;
            }
        }
    </script>
...

4.6.增大数量

    没什么好说的,原本12个path就是我们最开始的时候为了方便测试随手写的。现在只需要把12的地方改变成动态数量即可。记得绘制的时候的角度也要动态分配就好。

...
    <script type='text/paperscript' canvas="mycanvas"> 
        //13.数量改变
        var amount = 45;
        for (var i = 0; i < amount; i++) {
            ...
        }
        ...
        function onFrame(event) {
            for (var i = 0; i < amount; i++) {
                ...
                //14.每个path占角度比也要改变
                path.segments = [
                    position,
                    position + { angle: (i + 1) / amount * 360, length: length },
                    position + { angle: (i + 2) / amount * 360, length: length }
                ];
                ...
            }
        }
    </script>
...

4.7.鼠标跟随

    当下案例的所有的一切都是建立在中心点上的,只要中心点发生一点偏移,整个图形都会伴随发生移动。因此想要完成鼠标跟随,就需要获取鼠标坐标,然后重新设置为position即可。而优秀的框架总是能够想人之所想,急人之所急。感谢paperJS,再一次提供了贴心的函数:function onMouseMove(event)  来帮助处理鼠标的移动事件,免去了我们自己封装的困扰。

    这里需要提一句:

  • onFrame函数的event是一个Base类型的Object
  • 而onMouseMove函数的event则是一个toolEvent类型的Object

就参数而言虽然两者都叫event,但是还是需要说明一下,这两个event并没有任何一丁点关系!

    onMouseMove函数中的参数event对象中有一个point参数,而这个正是我们所需要的position鼠标坐标(毕竟你完成的是不是就是【鼠标跟随】?不跟随鼠标坐标还想干啥)

4.8.裸眼3D

    鼠标坐标的即时获取虽然能够做出跟随效果,但是很明显不够酷炫,或者说不够3D。而要想要在2D平面中实现3D效果听起来非常困难但是实际上做法却出乎意料的简单:让大脑自己脑补出3D效果。

    听起来有点扯是吧,别说你不相信,就是我最开始我也是不相信的那个。之后我也是理解了好久才明白,容我换个说法:2D中想要实现3D效果就需要让整体拆分,让物体的一部分先运动,其余部分后运动。只要动作层次分明就足够让人的大脑自己脑补出3D效果。其中比较经典的就是下面这张图(背景位移距离小,部分皮肤位移距离大,宝石不位移)。

    那么换算到我们的的代码里要怎么做呢?OK,我们不再直接将鼠标坐标赋值给position,而是将这“一次”的改变,分成几次执行。

...
    <script type='text/paperscript' canvas="mycanvas"> 
        ...
        var position = view.center;
        var children = project.activeLayer.children;
        
        function onFrame(event) {
            //17.3计算出每次移动鼠标,相较于上一次移动了多少距离
            var delta = mousePostion - position;
            //17.4每次渲染只移动鼠标位移的一部分
            position += delta / 10;
            ...
        }
        function onMouseMove(event){
            //17.0不再全体跟随鼠标移动
            //17.2每次移动都记录下鼠标的坐标
            mousePostion = event.point;
        }
        //17.1创建一个全局变量,持有鼠标坐标
        mousePostion = view.center;
    </script>
...

当我们实现上述代码的时候,延迟移动的效果已经出现了,但是3d效果还没看到。这是因为整个动画的渲染时机仍然还是发生在每一次onFrame中的,也就是说每次渲染的时候全体path依然会同时移动。而我们必须要其中一部分path在鼠标移动的瞬间渲染,剩下的部分则是在onFrame中进行渲染。

    这里的确是熬死了我好多脑细胞,最终想到了一个不是办法的办法:

  1. 干脆把onFrame中的渲染path代码封装为一个函数render
  2. 在鼠标移动的时候进行主动调用render函数来渲染所有path
  3. onFrame帧刷新正常执行render函数来刷新渲染所有path

这样我认为在鼠标移动的瞬间会立即执行render方法渲染所有path,但是onFrame帧刷新的时间间隔非常短暂(1秒钟有60次)。所以很大概率会导致在鼠标移动触发的render渲染没能执行完的时候,onFrame已经开始下一次刷新了。这个时候虽然两个函数执行的刷新是不会互相冲突的,但是在页面中带来的效果就会使鼠标移动触发render中已经渲染完成的path位置发生一次改变,而onFrame帧刷新的时候所有path一起位置发生一次变化。这样就会导致鼠标中心对应的path比距离鼠标中心相对远一些的path“可能”会多渲染一次,而这种可能会带来一种视觉上的误差,简单说,就是让大脑开启脑补了。

...
    <script type='text/paperscript' canvas="mycanvas"> 
        ...
        var position = view.center;
        mousePostion = view.center;
        var children = project.activeLayer.children;
        //18.1 渲染需要的内容只有一个count,其余都是全局变量,把内部所有的event.count直接改成count就可以
        function render(count){
            var delta = mousePostion - position;
            position += delta / 10;
            for (var i = 0; i < amount; i++) {
                var path = children[i];
                length = Math.abs(Math.sin(i + count/40) * 500);
                path.segments = [
                    //18.4 最后,别忘了渲染的时候,如果有位移,所有的path要跟随位移渲染
                    position + delta,
                    position + { angle: (i + 1) / amount * 360, length: length },
                    position + { angle: (i + 2) / amount * 360, length: length }
                ];
                path.fillColor.hue = (count % 360) - length;
            }
        }
        function onFrame(event) {
            //18.2 调用渲染函数render,传入event.count计数器
            render(event.count);
        }
        function onMouseMove(event){
            //18.3 最最最最最最关键的一环!!!!!!!!移动时立即渲染
            render();
            mousePostion = event.point;
        }
    </script>
...

5.总结

    以上就是paper.js动画框架学习破冰的第一篇。简单来说paper.js功能的确十分强大,但是在圈子内知名度不高可能由于是因为官方对于文档的支持不是特别的好的缘故吧。哈哈,当然也有可能是我孤陋寡闻的缘故。好了,不管怎样,今天的内容应该能够让你对paper.js有一定的了解了。相信在后面的内容中,我们能对paper.js有更深刻的认识。

    再次感谢在本文中引用链接的博客作者与文章作者。


版权声明:本文为u013792921原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。