相信作为一个程序猿,大家应该都已经看过一些排序算法的过程教学动画,现在我来总结一下自己使用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. 044. asp.net主题之二为主题添加CSS样式和动态加载主题

    1. 新建任意一个网站, 默认主页为Default.aspx, 增加一个App_Themes目录, 用于存储主题, 添加一个MyTheme的主题, 在MyTheme主题下添加一个样式表文件, 默认名称 ...

  2. 获取字符串中img标签的url集合(转载)

    /// <summary> /// 获取字符串中img的url集合 /// </summary> /// <param name="content"& ...

  3. 【linux】linux shell 日期格式化

      获得当天的日期 date +%Y-%m-%d 输出: 2011-07-28 将当前日期赋值给DATE变量DATE=$(date +%Y%m%d) 有时候我们需要使用今天之前或者往后的日期,这时可以 ...

  4. hosts立即生效的方法

     做web开发和测试的,难免需要经常修改hosts来指定某个域名访问某个特定的IP:在windows中,每次修改了C:\windows\system32\drivers\etc\hosts,都需要重启 ...

  5. Today I learnt

    2015-May-22 In Oracle database, dropping a table don't free up the space directly. You'll need to di ...

  6. 获取与Url链接相关的信息

    以下结果的值以此示例为基础:http://www.x2y2.com:80/fisker/post/0703/window.location.html?ver=1.0&id=6#imhere j ...

  7. Android SQLite 通配符查询找不到参数问题

    使用Android SQLite中SQLiteDatabase类的query方法查询时,如果where中包含通配符,则参数会无法设置,如类似下面的方法查询时 SQLiteDatabase db = d ...

  8. rsync命令详解

    介绍 rsync命令是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件.rsync使用所谓的“rsync算法”来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部 ...

  9. 【转】 java web开发之安全事项

    从事java web开发也有几年了,可是开发中的安全问题却越来越不以为然.直到不久遇到一黑软,瞬间sql注入,少时攻破网站数据库.还好,我还没有用root级的用户连接数据库.不过也没有什么用了,因为我 ...

  10. debian下安装zendframework

    第一步,打开apache的rewrite模块,因为在debian下使用apache必须执行这一步 a2enmod rewrite #激活rewrite模块 /etc/init.d/apache2 re ...