相信作为一个程序猿,大家应该都已经看过一些排序算法的过程教学动画,现在我来总结一下自己使用ES6的generator实现动画的过程,以作为自己对generator的使用实践的一个记录。

为什么用generator实现?

因为对于计算机而言排序是一个十分短暂的过程,然而动画却不是。动画的生成需要获取当前帧的状态,也就是当前数列的状态,若是我们在计算过程中插入动画的播放,也就是在交换时播放交换动画,由于动画播放是异步的,当你完成了这一次的交换两个数的动画播放,排序已经完成了,数列的状态已经是有序,你已经无法在根据数列状态生成后面的动画并播放。有人可能想到,那可以等到动画播放完再进行下一次的比较和交换,然而我并没有想到如何监听交换动画结束的状态的方法,所以我没有实现这种动画播放的同步化。

一个解决方法时,计算过程中发生交换,不播放动画,只生成动画的“帧”并保存,等排序完毕,"帧”也全部生成完毕,然后逐帧显示形成动画就可以了。但是这种方法缺点在于需要保存帧的数据,也就是每一瞬间动画的状态。因此可以每生成一帧便渲染一次,渲染完后再进行后面的比较和交换。但是这样帧与帧之间的时间间隔变得难以控制,因为你在渲染完这帧后渲染下一帧的时间就是继续开始计算并发生下一次交换并生成帧数据的时间,这个时间十分短,几乎接近0,因此这样的动画是不可控并且短暂的。若是想让帧与帧之间固定一个时间间隔,就必须使用setTimeout()或setInterval(),但这样又会产生之前的异步问题了。因此可以使用generator,了解generator的应该明白,它是一个状态机,代表函数内部的执行状态,当执行到某一语句时就会将这个状态“冻结",并且返回该语句的结果,只有调用generator的next()方法,才能继续执行函数内部语句直到下一个"冻结"产生为止。这里我利用了generator的"冻结”特性,当需要交换时,冻结状态,进行两个数交换动画的渲染和播放,这里我可以设置这个交换动画有多少帧,多长时间后进行下一次交换的计算(我用的setInterval,在回调函数中调用next进行下一次交换的计算)。

代码如下(只是部分关键代码):

function* bubbleSort(array, compare, sorter) {
let i = 0, j = 0, l = array.length;
for (i = 0; i < l; ++i) {
for (j = 0; j < l - i - 1; ++j) {
if (!compare(array[j], array[j + 1])) {
yield* swap(j, j + 1, sorter); //嵌套generator函数
[array[j], array[j + 1]] = [array[j + 1], array[j]];
[sorter.states[j], sorter.states[j + 1]] = [sorter.states[j + 1], sorter.states[j]];
}
}
}
} function* swap(a, b, sorter) {
yield* rise(a, b, sorter); //交换上升动画
yield* move(a, b, sorter); //交换平移动画
yield* down(a, b, sorter); //交换下降动画
} function *rise(a, b, sorter) {
let stateA = sorter.states[a],
stateB = sorter.states[b]; //一次循环更新一次状态,形成一个新帧的信息
for (let i = 1; i <= FRAME_NUMBER; ++i) {
stateA.y -= sorter.divideLineHeight / FRAME_NUMBER;
stateB.y -= sorter.divideLineHeight / FRAME_NUMBER;
yield; //“冻结”状态,进行渲染
}
} function *down(a, b, sorter) {
let stateA = sorter.states[a],
stateB = sorter.states[b];
for (let i = 1; i <= FRAME_NUMBER; ++i) {
stateA.y += sorter.divideLineHeight / FRAME_NUMBER;
stateB.y += sorter.divideLineHeight / FRAME_NUMBER;
yield;
}
} function *move(a, b, sorter) {
let stateA = sorter.states[a],
stateB = sorter.states[b],
p = sorter.numbers[a] < sorter.numbers[b],
dist = Math.abs(stateA.x - stateB.x);
for (let i = 0; i < FRAME_NUMBER; ++i) {
if (!p) {
stateA.x += dist / FRAME_NUMBER;
stateB.x -= dist / FRAME_NUMBER;
} else {
stateA.x += dist / FRAME_NUMBER;
stateB.x -= dist / FRAME_NUMBER;
}
yield;
}
} sort(type) {
if (type == sortType.bubble) {
this.sort = bubbleSort(this.numbers, (a, b) => {
return a - b < 0;
}, this);
} this.animation = setInterval((() => {
//此语句是进行generator更新,若generator到达最后一个状态,停止动画
if (this.sort.next().done) {
clearInterval(this.animation);
} //更新后渲染
this.render();
}).bind(this), 0);
}

使用generator生成排序动画的更多相关文章

  1. mybatis Generator生成代码及使用方式

    本文原创,转载请注明:http://www.cnblogs.com/fengzheng/p/5889312.html 为什么要有mybatis mybatis 是一个 Java 的 ORM 框架,OR ...

  2. 【WP 8.1开发】如何动态生成Gif动画

    相信如何为gif文件编码,很多朋友都会,而难点在于怎么让GIF文件中的帧动起来,也就是创建gif动画. Gif文件编码方法 先简单介绍一下编码的方法. 1.调用BitmapEncoder.Create ...

  3. tween.js是一款可生成平滑动画效果的js动画库。tween.js允许你以平滑的方式修改元素的属性值。它可以通过设置生成各种类似CSS3的动画效果。

    简要教程 tween.js是一款可生成平滑动画效果的js动画库.相关的动画库插件还有:snabbt.js 强大的jQuery动画库插件和Tweene-超级强大的jQuery动画代理插件. tween. ...

  4. unity工具IGamesTools之批量生成帧动画

    unity工具IGamesTools批量生成帧动画,可批量的将指定文件夹下的帧动画图片自动生成对应的资源文件(Animation,AnimationController,Prefabs) unity工 ...

  5. CSS3鼠标移入移出图片生成随机动画

    今天分享使用html+css3+少量jquery实现鼠标移入移出图片生成随机动画,我们先看最终效果图(截图为静态效果,做出来可是动态的哟) 左右旋转 上下移动 缩放 由于时间关系我就不一步步解析各段代 ...

  6. JAVA 调用Axis2 code generator 生成的webservice

    以下代码为调用 JAVA 调用Axis2 code generator 生成的webservice的代码. package test; import java.rmi.RemoteException; ...

  7. PHP使用JPG生成GIF动画图片,基于php_imagick_st-Q8.dll

    PHP使用php_imagick_st-Q8.dll类库,把JPG图片连接生成GIF动画图片,需要事先下载好php_imagick_st-Q8.dll,文件,并配置php.ini文件,启用php_im ...

  8. python 将png图片格式转换生成gif动画

    先看知乎上面的一个连接 用Python写过哪些[脑洞大开]的小工具? https://www.zhihu.com/question/33646570/answer/157806339 这个哥们通过爬气 ...

  9. spring boot+mybatis+generator生成domain大小写问题

    之前遇到一个问题,用generator生成数据库对应的domain,以前都是好好的,那天突然生成的domain都是小写的,因为我数据库里是大写的,后来找到解决办法, <table tableNa ...

随机推荐

  1. 详细解说Java Spring的JavaConfig注解 【抄】

    抄自: http://www.techweb.com.cn/network/system/2016-01-05/2252188.shtml @RestController spring4为了更方便的支 ...

  2. selenium环境搭建

    开发环境: 1.jdk 2.eclipse 3.selenium,selenium只需要selenium-java-2.45.jar这个就可以 具体搭建步骤可参考:http://www.cnblogs ...

  3. Bulkcopy对应的实现是Oracle的SQL*LOADER,期间造成Index Unusable,并且last_ddl_time上是不体现的

    部分项目反馈系统整体突然变慢,经查询发现一个系统核心的大数据表的索引状态全部是Unusable. 导致索引失效的直接原因:当某些操作导致数据的rowid改变,索引就会完全失效. 那什么时候会导致row ...

  4. Oracle用法集锦

    查询第一条数据 修改表名 ALTER TABLE tablename RENAME TO newtablename 修改列名: ALTER TABLE BD_PRI RENAME COLUMN EU_ ...

  5. meta 标签 详细说明

    meta 标签可提供页面元素信息, 使用键值对的定义方式,可以记录网页上的主要信息,也可以自定义键值对 属性 content(必须),name,http-equiv,scheme,lang 常用 me ...

  6. OAF_文件系列11_实现OAF读写Excel包JXL和POI的区别(概念)

    20150803 Created By BaoXinjian

  7. 也说php从mysql数据库通过服务器端json返回数据出现乱码问题

    我最近需要用js和json与mysql数据库做一个两级联动的下拉菜单,发现当从数据库中返回的是中文时客户端会出现乱码问题,经过在百度上查找终于找到了解决办法如下: while($row=$MySqlc ...

  8. GridLookUpEdit多列模糊查询最简单方式 z

    GridLookUpEdit的知识库是RepositoryItemGridLookUpEdit,切确的说GridLookUpEdit只是RepositoryItemGridLookUpEdit的一个壳 ...

  9. Java 技术文章摘录

    sokcet 编程实例 android bundle类 Android -- Looper.prepare()和Looper.loop() —深入版 Java NIO系列教程 XML操作 Androi ...

  10. Spring AOP不拦截从对象内部调用的方法原因

    拦截器的实现原理很简单,就是动态代理,实现AOP机制.当外部调用被拦截bean的拦截方法时,可以选择在拦截之前或者之后等条件执行拦截方法之外的逻辑,比如特殊权限验证,参数修正等操作. 但是最近在项目中 ...