<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_叶落为重生每片落下的叶子都是为了下一次的涅槃...^_^</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/76200/rss</id><updated>2012-05-28T15:01:43Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/76200/rss"/><entry><id>http://www.cnblogs.com/hongru/archive/2012/05/23/2515409.html</id><title type="text">基于【双密度松弛算法】的二维流体粒子模拟</title><summary type="text">以技术预研的心态做的一个东东。基于 【双密度松弛算法】目前来看应用价值不大，更多的是扩展一些思路，扩大前端方向，或者说是js能做的事的范围。大家路过围观一下就好。源码不多，托管在github/hongru/fluid上，感兴趣的可以大致看一看。很早以前自己也做了个类似的，不过那个算法没有理论依据，自己瞎想的。离开了html5，面对“流体力学”，我只能旁观了...。【实现的思路】最关键的还是 【双密度松弛算法】 的实现。具体的算法可以参考文献和资料：http://wenku.baidu.com/view/2d53091b6bd97f192279e95e.htmlhttp://www.iro.um</summary><published>2012-05-23T14:16:00Z</published><updated>2012-05-23T14:16:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/05/23/2515409.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/05/23/2515409.html"/><content type="html">&lt;p&gt;&lt;a href="https://github.com/hongru/fluid"&gt;&lt;img class="alignleft" title="fluid simulation" src="http://hongru.github.com/resource/images/fluid.png" alt="" width="200" height="210" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;以技术预研的心态做的一个东东。基于 【双密度松弛算法】&lt;/p&gt;&lt;p&gt;目前来看应用价值不大，更多的是扩展一些思路，扩大前端方向，或者说是js能做的事的范围。大家路过围观一下就好。&lt;/p&gt;&lt;p&gt;源码不多，托管在&amp;nbsp;&lt;a href="https://github.com/hongru/fluid" target="_blank"&gt;github/hongru/fluid&lt;/a&gt;上，感兴趣的可以大致看一看。&lt;/p&gt;&lt;p&gt;很早以前自己也做了个类似的，不过那个算法没有理论依据，自己瞎想的。&lt;a href="http://www.cnblogs.com/hongru/archive/2010/12/21/1912820.html" target="_blank"&gt;离开了html5，面对&amp;ldquo;流体力学&amp;rdquo;，我只能旁观了...&lt;/a&gt;&lt;span&gt;。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;【实现的思路】&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;最关键的还是 【双密度松弛算法】 的实现。具体的算法可以参考文献和资料：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://wenku.baidu.com/view/2d53091b6bd97f192279e95e.html"&gt;http://wenku.baidu.com/view/2d53091b6bd97f192279e95e.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.iro.umontreal.ca/labs/infographie/papers/Clavet-2005-PVFS/pvfs.pdf"&gt;http://www.iro.umontreal.ca/labs/infographie/papers/Clavet-2005-PVFS/pvfs.pdf&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;另有一个 html5 版本的 粒子 流体模拟；实现思路略有不同，可以参考：&lt;a href="http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas"&gt;http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas&lt;/a&gt;&lt;/p&gt;&lt;p&gt;使用js来做这种高计算量的事情，还是有点吃不住.&lt;/p&gt;&lt;p&gt;本文同步发表在&lt;a href="http://www.alloyteam.com/2012/05/fluid-simulation/"&gt;http://www.alloyteam.com/2012/05/fluid-simulation/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2515409.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/05/23/2515409.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/05/20/2510960.html</id><title type="text">为什么我推荐事件委托而不是批量绑定</title><summary type="text">太长时间没写blog了，最近迷迷糊糊，又到一个周末，为了给自己一个交代，还是尽力记录点东西吧。免得哪天失忆想回去找资料都没地方找了。今天要记录的东西很简单，就是事件委托。我相信但凡一个做前端方向的，甚至不是前端方向的编码者，对于dom元素的事件委托应该都了解了。所以今天不是说“事件委托”是什么？而是说为什么需要它。【基于前端模版的开发】我们先说这个，为什么要先说这个呢，因为事件委托在这种模式下显得比较有价值。前端模版-相信大家也都耳熟能详，玩的很溜了。web的越来越多的工作开始移交到前端来做。其中就包含这一个东西。当然，我们今天也不讨论前端模版的优势。而是要看接下来使用过程中可能遇到的一些问题</summary><published>2012-05-20T14:55:00Z</published><updated>2012-05-20T14:55:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/05/20/2510960.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/05/20/2510960.html"/><content type="html">&lt;p&gt;太长时间没写blog了，最近迷迷糊糊，又到一个周末，为了给自己一个交代，还是尽力记录点东西吧。免得哪天失忆想回去找资料都没地方找了。&lt;/p&gt;&lt;p&gt;今天要记录的东西很简单，就是事件委托。我相信但凡一个做前端方向的，甚至不是前端方向的编码者，对于dom元素的事件委托应该都了解了。所以今天不是说&amp;ldquo;事件委托&amp;rdquo;是什么？而是说为什么需要它。&lt;/p&gt;&lt;p&gt;【基于前端模版的开发】&lt;/p&gt;&lt;p&gt;我们先说这个，为什么要先说这个呢，因为事件委托在这种模式下显得比较有价值。&lt;/p&gt;&lt;p&gt;前端模版-相信大家也都耳熟能详，玩的很溜了。web的越来越多的工作开始移交到前端来做。其中就包含这一个东西。当然，我们今天也不讨论前端模版的优势。而是要看接下来使用过程中可能遇到的一些问题。&lt;/p&gt;&lt;p&gt;比如简单的，如下：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br/&gt;&amp;lt;meta charset="utf-8" /&amp;gt;&lt;br/&gt;&amp;lt;body&amp;gt;&lt;br/&gt;&amp;lt;script&amp;gt;&lt;br/&gt;var $T = new function () {&lt;br/&gt;var $ = this;&lt;br/&gt;// escape&lt;br/&gt;    this.escape = function(string) {&lt;br/&gt;        return (''+string).replace(/&amp;amp;/g, '&amp;amp;')&lt;br/&gt;                          .replace(/&amp;lt;/g, '&amp;lt;')&lt;br/&gt;                          .replace(/&amp;gt;/g, '&amp;gt;')&lt;br/&gt;                          .replace(/"/g, '"')&lt;br/&gt;                          .replace(/'/g, ''')&lt;br/&gt;                          .replace(/\//g,'/');&lt;br/&gt;    };&lt;br/&gt;    // template&lt;br/&gt;    this.templateSettings = {&lt;br/&gt;        evaluate    : /{{([\s\S]+?)}}/g,&lt;br/&gt;        interpolate : /{{=([\s\S]+?)}}/g,&lt;br/&gt;        escape      : /{{-([\s\S]+?)}}/g&lt;br/&gt;    };&lt;br/&gt;    var noMatch = /.^/;&lt;br/&gt;    var unescape = function(code) {&lt;br/&gt;        return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");&lt;br/&gt;    };&lt;br/&gt; &lt;br/&gt;    this.template = function (str, data) {&lt;br/&gt;        var c  = $.templateSettings;&lt;br/&gt;        var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +&lt;br/&gt;            'with(obj||{}){__p.push(\'' +&lt;br/&gt;            str.replace(/\\/g, '\\\\')&lt;br/&gt;                .replace(/'/g, "\\'")&lt;br/&gt;                .replace(c.escape || noMatch, function(match, code) {&lt;br/&gt;                    return "',$.escape(" + unescape(code) + "),'";&lt;br/&gt;                })&lt;br/&gt;                .replace(c.interpolate || noMatch, function(match, code) {&lt;br/&gt;                    return "'," + unescape(code) + ",'";&lt;br/&gt;                })&lt;br/&gt;                .replace(c.evaluate || noMatch, function(match, code) {&lt;br/&gt;                    return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";&lt;br/&gt;                })&lt;br/&gt;                .replace(/\r/g, '\\r')&lt;br/&gt;                .replace(/\n/g, '\\n')&lt;br/&gt;                .replace(/\t/g, '\\t')&lt;br/&gt;                + "');}return __p.join('');";&lt;br/&gt;                &lt;br/&gt;        var func = new Function('obj', '$', tmpl);&lt;br/&gt;        if (data) return func(data, $);&lt;br/&gt;        &lt;br/&gt;        return function (data) {&lt;br/&gt;            return func.call(this, data, $);&lt;br/&gt;        };&lt;br/&gt;    }   &lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;var tpl1 = '&amp;lt;div&amp;gt;我由第一个模版生成 {{= name }} &amp;lt;button onclick="say.call(this)"&amp;gt;one&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;',&lt;br/&gt;tpl2 = '&amp;lt;div&amp;gt;我由第二个模版生成 {{= name }} &amp;lt;button onclick="say.call(this)"&amp;gt;two&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;';&lt;br/&gt;&lt;br/&gt;function say () {&lt;br/&gt;alert(this.value || this.innerHTML)&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;document.body.innerHTML = $T.template(tpl1, {name: 'Horizon'})&lt;br/&gt;&amp;lt;/script&amp;gt;&lt;br/&gt;&amp;lt;/body&amp;gt;&lt;br/&gt;&lt;/div&gt;&lt;p&gt;可能大家看这个：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;var tpl1 = '&amp;lt;div&amp;gt;我由第一个模版生成 {{= name }} &amp;lt;button onclick="say.call(this)"&amp;gt;one&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;',&lt;br/&gt;tpl2 = '&amp;lt;div&amp;gt;我由第二个模版生成 {{= name }} &amp;lt;button onclick="say.call(this)"&amp;gt;two&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;';&lt;br/&gt;&lt;/div&gt;&lt;p&gt;看里面这个onclick总会觉得别扭。但是我们还得绑事件呀，直接用addEventListener或者attachEvent，一旦换模版重置innerHTML， 那之前的addEventListener不就没用了么....&lt;/p&gt;&lt;p&gt;总不能每次都重绑一次吧？？ 而且通常也容易带出内存泄漏等等乱七八糟的东西。&lt;/p&gt;&lt;p&gt;所以，这时候，事件委托就有用了。&lt;/p&gt;&lt;p&gt;【利用委托进行事件派发】&lt;/p&gt;&lt;p&gt;一个简单的委托例子可能像下面的代码：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&amp;lt;ul id="dele-ul"&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&lt;br/&gt;&amp;lt;/ul&amp;gt;&lt;br/&gt;&amp;lt;script&amp;gt;&lt;br /&gt;var $E = {};&lt;br/&gt;$E.on = function (o, e, f) {&lt;br/&gt;return o.addEventListener ? o.addEventListener(e, f, false) : o.attachEvent('on'+e, function () { f.call(o) });&lt;br/&gt;};&lt;br/&gt;&lt;br/&gt;$E.on(document.getElementById('dele-ul'), 'click', function (e) {&lt;br/&gt;var tar = e.target || e.srcElement;&lt;br/&gt;if (tar.nodeName.toLowerCase() == 'li') {&lt;br/&gt;alert(tar.innerHTML);&lt;br/&gt;}&lt;br/&gt;})&lt;br/&gt;&amp;lt;/script&amp;gt;&lt;br/&gt;&lt;/div&gt;&lt;p&gt;可能注意到里面有个过滤指定dom的代码&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;if (tar.nodeName.toLowerCase() == 'li') { ... }&lt;br/&gt;&lt;/div&gt;&lt;p&gt;当元素简单时，当然还好，当dom层级复杂时。我们就需要做一点小手脚。&lt;/p&gt;&lt;p&gt;给我们需要绑事件的元素，加个标志位，然后利用事件冒泡即可。&lt;/p&gt;&lt;p&gt;简单的，比如下面的代码：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;$E = new function () {&lt;br/&gt;    function on (o, e, f) {&lt;br/&gt;return o.addEventListener ? o.addEventListener(e, f, false) : o.attachEvent('on'+e, function () { f.call(o) });&lt;br/&gt;    };&lt;br/&gt;    function bubbleTo (el, endEl, key) {&lt;br/&gt;        if (!el || (el &amp;amp;&amp;amp; el == document)) {&lt;br/&gt;            return null;&lt;br/&gt;        } else if (el == endEl || (el.getAttribute &amp;amp;&amp;amp; el.getAttribute(key))) {&lt;br/&gt;            return el;&lt;br/&gt;        } else if (el.parentNode) {&lt;br/&gt;            return bubbleTo(el.parentNode, endEl, key);&lt;br/&gt;        } else {&lt;br/&gt;            return null;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    function dispatch (el, type, key, distributor) {&lt;br/&gt;        if (typeof key == 'object') {&lt;br/&gt;            distributor = key;&lt;br/&gt;            key = 'data-cmd';&lt;br/&gt;        }&lt;br/&gt;        $E.on(el, type, function (e) {&lt;br/&gt;            var tar = bubbleTo(e.target, el, key); &lt;br/&gt;            if (tar) {&lt;br/&gt;                var cmd = tar.getAttribute(key);&lt;br/&gt;                distributor[cmd] &amp;amp;&amp;amp; distributor[cmd].call &amp;amp;&amp;amp; distributor[cmd].call(tar, e, tar);&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;this.on = on;&lt;br/&gt;this.bubbleTo = bubbleTo;&lt;br/&gt;this.dispatch = dispatch;&lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;原理就是加一个指定标志位，冒泡到有指定属性的，就停下来，为他响应对应的事件。当然前提是有约定（这个约定属性不能乱用 ^.^）&lt;/p&gt;&lt;p&gt;于是我们就可以这样用：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&amp;lt;body&amp;gt;&lt;br/&gt;&amp;lt;section&amp;gt;&lt;br/&gt;...&lt;br/&gt;&amp;lt;button data-custom-cmd="sayHi"&amp;gt;&amp;lt;button&amp;gt;&lt;br/&gt;        &amp;lt;div id="tpl-con"&amp;gt;&lt;br/&gt;            ...&lt;br/&gt;        &amp;lt;/div&amp;gt;&lt;br/&gt;...&lt;br/&gt;&amp;lt;/section&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;script&amp;gt;&lt;br/&gt;    var tpl1 = '&amp;lt;a data-custom-cmd="walk"&amp;gt;&amp;lt;span&amp;gt;{{= name }}&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;';&lt;br/&gt;    // use tpl&lt;br/&gt;    ...&lt;br/&gt;    &lt;br/&gt;    // dispatch Event&lt;br/&gt;$E.dispatch(document.body, 'click', 'data-custom-cmd', {&lt;br/&gt;'sayHi': function (e, tar) {&lt;br/&gt;// todo&lt;br/&gt;},&lt;br/&gt;'walk': function (e, tar) {&lt;br/&gt;// todo&lt;br/&gt;}&lt;br/&gt;...&lt;br/&gt;});&lt;br/&gt;&amp;lt;/script&amp;gt;&lt;br/&gt;&amp;lt;/body&amp;gt;&lt;br/&gt;&lt;/div&gt;&lt;p&gt;于是这样，几乎所有的 click 事件全部放到body上来处理了。　&lt;/p&gt;&lt;p&gt;不用担心 templete 模板化，会把之前的事件弄没了。&lt;/p&gt;&lt;p&gt;同时，也可以大大减少对于内存泄漏的一些担心。&lt;/p&gt;&lt;p&gt;【后记】&lt;/p&gt;&lt;p&gt;当然，这只是一个很小很简单的技巧和经验，同时也不适用于所有情况，比如 resize， mousemove &amp;nbsp;之类的场景，基本不适用。 但是对于常用的类似click，mousedown，mouseup之类。大部分情况还是可以考虑一下的。&lt;/p&gt;&lt;p&gt;说不定可以解决一些意想不到的问题。(^^)&lt;/p&gt;&lt;p&gt;简单的东西不用多说，一个小技巧，仅供参考而已，估计已经有很多同学早就开始这样做了。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2510960.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/05/20/2510960.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/03/31/2427590.html</id><title type="text">关于简单的碰撞检测</title><summary type="text">【前言】这篇博文旨在给自己做个记录和备忘，同时希望也能给有这方面简易碰撞模型需求的同学一点点参考价值。【关于像素级别检测】前一阵有同学问我说能否做到像素级别的碰撞检测，做过类似碰撞检测的同学应该清楚，按照我们最常规的想法，假如要检测一个运动的物体和一条线之间是否有碰撞，最简单的判断条件，就是看当前帧，这个物体的位置，是否超过的我们的界定范围...但这样简单的判定确实是有问题的，我们举一个实际一点的例子。假如 小球 从 a 点向 c 点的方向 落下。外面的黑框为我们所示的边界，那么，我们想要小球在碰到边界的时候反弹... 那么我们该怎么做呢？可能有同学会迫不及待说了，这还不简单，一个 条件语句搞</summary><published>2012-03-31T15:41:00Z</published><updated>2012-03-31T15:41:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/03/31/2427590.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/03/31/2427590.html"/><content type="html">&lt;p&gt;【前言】&lt;/p&gt;&lt;p&gt;这篇博文旨在给自己做个记录和备忘，同时希望也能给有这方面简易碰撞模型需求的同学一点点参考价值。&lt;/p&gt;&lt;p&gt;【关于像素级别检测】&lt;/p&gt;&lt;p&gt;前一阵有同学问我说能否做到像素级别的碰撞检测，做过类似碰撞检测的同学应该清楚，按照我们最常规的想法，假如要检测一个运动的物体和一条线之间是否有碰撞，最简单的判断条件，就是看当前帧，这个物体的位置，是否超过的我们的界定范围...&lt;/p&gt;&lt;p&gt;但这样简单的判定确实是有问题的，我们举一个实际一点的例子。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033120262119.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;假如 小球 从 a 点向 c 点的方向 落下。外面的黑框为我们所示的边界，那么，我们想要小球在碰到边界的时候反弹... 那么我们该怎么做呢？&lt;/p&gt;&lt;p&gt;可能有同学会迫不及待说了，这还不简单，一个 条件语句搞定： 假设 小球的 纵向 速度 为 vy，那么在循环帧里判断 小球当前的 位置， 当发现 位置 低于 边界 的时候 vy = -vy 就可以了。&lt;/p&gt;&lt;p&gt;没错，这的确是最简单的判别 方法。 但是也是漏洞百出的 办法。 在复杂度不高的情况下 勉强可以胜任需求。&lt;/p&gt;&lt;p&gt;如果我们把要求升级一点。小球每反弹一次，它的纵向 速度值 降为原来的 0.5 ，那么会出现什么情况下。我们假设 每个循环帧的间隔为dt，我下面画出 小球 在临界碰撞 前后3帧 位置的示意图， 大家看看就会发现问题了。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033120390095.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;假设a,b,c 三点为小球 1，2，3帧的位置，那么我们按上面说的 检测方法， 小球在b点的时候检测 发现 小球位置 已经超出 界定范围了，那么 vy 反向，同时 ，vy的数值 减半，那么可想而知 ，再下一帧的位置 就会大约是在 c点的位置。&lt;/p&gt;&lt;p&gt;那么按照上面说的检测方法，问题就来了，在 c 点，检测发现，小球位置依然在 界定范围外， 又被判定为 &amp;ldquo;碰撞&amp;rdquo; ，速度反向， 那么可想而知，以此类推， 这个小球 就永远也弹不起来了... 坑爹。&lt;/p&gt;&lt;p&gt;好吧，为了绕过这种问题，肯定有同学都想到了，我们的碰撞检测 再 加上 对于速度方向的判断 不就可以了吗？&lt;/p&gt;&lt;p&gt;没错，对于这个模型，再加上一个速度方向的判断，也可以勉强的解决问题。 不过话说回来，这样不觉得有些别扭吗？ 而且其实反弹的点也不对，正确的应该是在红色交点 o 处反弹的，结果按照上面的思路变成 在 b 点反弹了.... 还是有点坑爹。&lt;/p&gt;&lt;p&gt;&amp;nbsp;那么怎么把 反弹点从b点移到o点呢？&lt;/p&gt;&lt;p&gt;可能又有同学会说了，你这不就回到最开始的问题了吗，要精确的得到 反弹点 o 不就需要所谓的 像素级判断了吗？理论上是不可能的....&lt;/p&gt;&lt;p&gt;确实，如果按照上面的思路是不可能的。但是换个思路，一切就有戏了。&lt;/p&gt;&lt;p&gt;【关于线段相交】&lt;/p&gt;&lt;p&gt;回到正题，上面的例子已经表明 那种 简单的 利用当前位置来判断 碰撞的模型 是不太靠谱的。 那么换个思路，按小球的运行轨迹 与 边界 的相交性 来判断，是否可行呢？&lt;br /&gt;什么意思呢？就是说 我们 把小球 当前帧 和 下一帧 的位置 连接起来， 变成一条线段， 然后跟 需要进行检测的 边界 这条线段 放到一起，碰撞问题 就变成 数学中 简单的 【二维平面中两条线段相交问题】了。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033122354328.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;同样，我们以 a, b, c 三点来表示小球 前后 3 帧的位置，中间那条黑色的实线表示边界N, 那么 在第一帧 到第二帧， 也就是 a -&amp;gt; b 的运行过程中，线段ab 和边界N 明显是没有交点的，那么 自然可以认为 是没有碰撞的。&lt;/p&gt;&lt;p&gt;而由b-&amp;gt;c 的过程中，可以发现，线段bc 和 N 就有 交点 o 了，只要我们有办法 证明 bc 和N 相交，并且求出 这个交点 o 的位置，那么 就可以证明 小球在 b -&amp;gt; c 的运行过程中，必然和边界N 碰撞。同时 交点 o 即为反弹点。&lt;/p&gt;&lt;p&gt;通过这种方式来判断碰撞关系的话，就不会说因为小球的速度 过大 或者 FPS 过小 而造成 碰撞检测失效的情况了，而且还能精确的得到反弹点。甚至反弹角度。&lt;/p&gt;&lt;p&gt;好吧，接下来，咋们有了数学模型，就是所谓的 关于两条线段 的相交问题。这个问题怎么处理呢。&lt;/p&gt;&lt;p&gt;判断已知的两条线段是否相交的办法，我这里提供两个。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;1. 分别求出 两条线段 的 二维 表示公式 如：线段A： y= a1*x + b1;  线段B： y=a2*x+b2; &lt;br/&gt;然后把线段A 的两个端点 分别 带到 线段B 的公式里。&lt;br/&gt;我们知道，把一个点坐标 带到 一条直线 公式中， a2*x + b2 -y ； 如果等于0 ，那么表示点在线上，如果小于0表示点在线的一方， 大于0 表示在另一方。&lt;br/&gt;那么 当 两个端点 带到 公式里面的结果 ，一正一负 就可以表示 这条线段 的两个端点 分别在这条直线的两边。&lt;br/&gt;&lt;/div&gt;&lt;p&gt;如：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033122533916.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;把 a， b点分别 带到 线段N 所在 的直线公式中， 如果 结果 一正一负 就可以 表示 a, b 分布在N 的两边，&lt;/p&gt;&lt;p&gt;但是光把 a, b 戴到N中 去算 是不够的，因为 还有这种情况，&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033122563068.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;因为是线段嘛，所以 还要把另一条线段 的 端点 再带到 自己所在直线 的公式 上，按同样的方式 进行计算， 得到 另一条线段 端点 也分布在 相对应的 线段两边 的时候， 才能保证线段 是相交的。&lt;/p&gt;&lt;p&gt;另外一种判断 两条 线段 是否相交的思路，可以用 向量外积来判断。关于外积，如果已经还给数学老师的同学，可以百度或者google一下。简单的理解，其实 外积本身 也就是带方向的向量， 数值上等于 两条向量组成的三角形的外接矩形面积。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033123060978.png" alt="" /&gt;&lt;br /&gt;我们可以简单的理解为，我们先求 线段ab 的两个端点 相对于 cd 的&amp;ldquo;外积&amp;rdquo; 。 比如， 由 a,c,d 三点组成的三角形的外积，得到的结果是一个带方向的数值，我们假定为 S_acd, 同样的方式 得到 b点 和 线段cd 组成的三角形 外积 S_bcd 。&amp;nbsp;&lt;br /&gt;数学上可以证明，如果a， b 两点分布在 cd 的两侧的话，那么 S_acd 和S_bcd 一定是反向的。即 S_acd*S_bcd &amp;lt; 0;&lt;/p&gt;&lt;p&gt;同理，为了避免类似：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012033123145366.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;这种， 虽然 S_abc * S_abd &amp;lt; 0 , 但是 S_acd*S_bcd &amp;gt; 0;&lt;/p&gt;&lt;p&gt;同样不能判定相交。&lt;/p&gt;&lt;p&gt;所以必须是：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;S_abc * S_abd &amp;lt; 0&lt;br/&gt;S_acd * S_bcd &amp;lt; 0&lt;br/&gt;&lt;/div&gt;&lt;p&gt;同时满足时，才能证明 ab 和 cd 两条线段相交。&lt;/p&gt;&lt;p&gt;关于二维坐标系三角形外积的求法，我这里给出简单的代码：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;/* == Helper == */&lt;br/&gt;// 用于帮助检测 碰撞&lt;br/&gt;/**&lt;br/&gt; * Helper area calculation function. Returns 2 X the area.&lt;br/&gt; * 三角形外积&lt;br/&gt; *&lt;br/&gt; * @param  {Vec2} pointA&lt;br/&gt; * @param  {Vec2} pointB&lt;br/&gt; * @param  {Vec2} pointC&lt;br/&gt; * @return {number}&lt;br/&gt; */&lt;br/&gt;function signed2DTriArea(pointA, pointB, pointC) {&lt;br/&gt; return ((pointA.x - pointC.x) * (pointB.y - pointC.y) - (pointA.y - pointC.y) * (pointB.x - pointC.x));&lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;有了上面的知识，就可以得出两条线段相交 的判定 方法 和交点了：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;/**&lt;br/&gt; * Helper intersection function. Checks if two lines intersect.&lt;br/&gt; *&lt;br/&gt; * @param {LineSegment2} a Line A&lt;br/&gt; * @param {LineSegment2} b Line B&lt;br/&gt; * @return {Object} An object is returned with intersection point and time if they intersect, otherwise null.&lt;br/&gt; * 判断两条线段是否相交，如果是，返回交点，和相交比例， 否则返回null&lt;br/&gt; */&lt;br/&gt;function intersectLineSegments(a, b) {&lt;br/&gt;var a1 = signed2DTriArea(a.a, a.b, b.b);&lt;br/&gt;var a2 = signed2DTriArea(a.a, a.b, b.a);&lt;br/&gt;if (a1 * a2 &amp;lt; 0) {&lt;br/&gt;var a3 = signed2DTriArea(b.a, b.b, a.a);&lt;br/&gt;var a4 = a3 + a2 - a1;&lt;br/&gt;if (a3 * a4 &amp;lt; 0) {&lt;br/&gt;var intersectionTime = a3 / (a3 - a4);&lt;br/&gt;&lt;br/&gt;// intersectionPoint = a.a + intersectionTime * (a.b - a.a);&lt;br/&gt;var intersectionPoint = new Vec2(a.b.x, a.b.y);&lt;br/&gt;intersectionPoint.sub(a.a);&lt;br/&gt;intersectionPoint.mul(intersectionTime);&lt;br/&gt;intersectionPoint.add(a.a);&lt;br/&gt;&lt;br/&gt;return {intersectionPoint: intersectionPoint,intersectionTime : intersectionTime};&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;return null;&lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;【关于线段类】&lt;/p&gt;&lt;p&gt;当然，我们要用到线段的概念，那么最好抽象出一个 线段的 类， 我这里也提供在线段处理中常用的几个方法，比如&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;获取线段上 到指定点p 最短距离的点&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;获取距离p点最短距离的点 的 比例&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;得到 p 点到线段的最短距离&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;类似的，可以看看 这个 demo 就知道 是怎么回事了。&lt;a href="http://hongru.github.com/proj/laro/test/lineSegment.test.html"&gt;http://hongru.github.com/proj/laro/test/lineSegment.test.html&lt;/a&gt;&amp;nbsp;（需canvas支持，里面几个端点都是可以拖动的）。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;【关于 由 多条线段组成的不规则凸起物的碰撞】&lt;/p&gt;&lt;p&gt;有了针对运动物体相对于 一条 线段 碰撞的检测后， 那么 由多条线段组成的 凸起物 也就顺着解决就行，无非就是 将 这种 由多条线段组成的 凸起物 按每条边分解成 单一的 向量，分别检测 就行。&lt;br /&gt;以下是 一个圆 相对于 不规则突起物 的碰撞检测判断代码：&amp;nbsp;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;/**&lt;br/&gt; * Test a sweep-circle against convex shape.&lt;br/&gt; *&lt;br/&gt; * @param {Circle}      circle        Circle used in test&lt;br/&gt; * @param {Vec2}        movement      Vector describing movement&lt;br/&gt; * @param {ConvexShape} shape         Shape to be tested against&lt;br/&gt; * @return {Collisions}               Collision objects that holds both contact points and intersection time.&lt;br/&gt; * 判断一个移动的圆 和 不规则凸起形状的碰撞相交关系，如果相交，返回关联的点和 相交比例&lt;br/&gt; * convex shape 的 points 顶点顺序 需要是逆时针排列的，这样可以避免在内部碰撞的检测，同时movement反向的也可以直接跳出&lt;br/&gt; */&lt;br/&gt;function getCollisionShape(circle, movement, shape) {&lt;br/&gt;var contactPoints = [],&lt;br/&gt;SWEEP_EPSILON = pkg.SWEEP_EPSILON;&lt;br/&gt;&lt;br/&gt;var foundOne = false;&lt;br/&gt;var minIntersectionTime = Number.MAX_VALUE;&lt;br/&gt;var localIntersectionTime = Number.MAX_VALUE;&lt;br/&gt;&lt;br/&gt;var i = 0;      // Used for iteration&lt;br/&gt;var pt;         // Handle during loops&lt;br/&gt;var contact;    // Placeholder for eventual contact point&lt;br/&gt;&lt;br/&gt;if (shape.numOfPoints() &amp;gt; 1) {&lt;br/&gt;pt = shape.points;&lt;br/&gt;for (i = 0; i &amp;lt; pt.length; i++) {&lt;br/&gt;var last = pt[i];&lt;br/&gt;var curr = i+1 === pt.length ? pt[0] : pt[i+1];&lt;br/&gt;&lt;br/&gt;// last &amp;amp; curr is now start and end points of the line.&lt;br/&gt;// 远离&lt;br/&gt;var normal = new Vec2(-(curr.y - last.y), curr.x - last.x);&lt;br/&gt;if (0 &amp;lt; normal.dot(movement)) {&lt;br/&gt;continue; &lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;normal.normalize();&lt;br/&gt;&lt;br/&gt;// last = last + normal * (circle.r + SWEEP_EPSILON)&lt;br/&gt;var _last = new Vec2(normal.x, normal.y);&lt;br/&gt;_last.mul(circle.r + SWEEP_EPSILON);&lt;br/&gt;_last.add(last);&lt;br/&gt;&lt;br/&gt;// curr = curr + normal * (circle.r + SWEEP_EPSILON)&lt;br/&gt;var _curr = new Vec2(normal.x, normal.y);&lt;br/&gt;_curr.mul(circle.r + SWEEP_EPSILON);&lt;br/&gt;_curr.add(curr);&lt;br/&gt;&lt;br/&gt;var endPos = new Vec2(circle.c.x, circle.c.y);&lt;br/&gt;endPos.add(movement);&lt;br/&gt;&lt;br/&gt;var localContact = new Vec2();&lt;br/&gt;&lt;br/&gt;var res = intersectLineSegments(new LineSegment2(circle.c, endPos), new LineSegment2(_last, _curr));&lt;br/&gt;if (res) {&lt;br/&gt;if (res.intersectionTime &amp;lt; minIntersectionTime) {&lt;br/&gt;foundOne = true;&lt;br/&gt;minIntersectionTime = res.intersectionTime;&lt;br/&gt;&lt;br/&gt;// localContact - normal * circle.r&lt;br/&gt;var _lc = new Vec2(normal.x, normal.y);&lt;br/&gt;_lc.mul(circle.r);&lt;br/&gt;&lt;br/&gt;res.intersectionPoint.sub(_lc);&lt;br/&gt;&lt;br/&gt;contact = new CollisionContact(res.intersectionPoint, normal, 0, shape.user, shape.material);&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;if (!foundOne) {&lt;br/&gt;pt = shape.points;&lt;br/&gt;for (i = 0; i &amp;lt; pt.length; i++) {&lt;br/&gt;localIntersectionTime = Number.MAX_VALUE; &lt;br/&gt;&lt;br/&gt;// Multiply the length of the ray to make sure that the spheres won't go&lt;br/&gt;// through each other at extremely low speeds&lt;br/&gt;var _mov = new Vec2(movement.x, movement.y);&lt;br/&gt;_mov.mul(1.1);&lt;br/&gt;if (intersectRaySphere(new Ray2(circle.c, _mov), new Circle(pt[i], circle.r))) {&lt;br/&gt;localIntersectionTime = closure();&lt;br/&gt;if (localIntersectionTime &amp;lt; minIntersectionTime) {&lt;br/&gt;// Got collision.&lt;br/&gt;foundOne = true;&lt;br/&gt;minIntersectionTime = localIntersectionTime;&lt;br/&gt;&lt;br/&gt;// circle.c + localIntersectionTime * movement - pt[i]&lt;br/&gt;var _normal = new Vec2(movement.x, movement.y);&lt;br/&gt;_normal.mul(localIntersectionTime);&lt;br/&gt;_normal.sub(pt[i]);&lt;br/&gt;_normal.add(circle.c);&lt;br/&gt;_normal.normalize();&lt;br/&gt;&lt;br/&gt;// Contact point is the vertex.&lt;br/&gt;// Normal is vector from corner to position of sphere at collision time.&lt;br/&gt;contact = new CollisionContact(pt[i], _normal, 0, shape.user, shape.material);&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;if (foundOne) {&lt;br/&gt;contactPoints.push(contact);&lt;br/&gt;return new Collisions(contactPoints, minIntersectionTime);&lt;br/&gt;}&lt;br/&gt;else {&lt;br/&gt;return null;&lt;br/&gt;}&lt;br/&gt;};&lt;br/&gt;&lt;/div&gt;&lt;p&gt;其他的代码我就不多贴了，相信明白了原理之后，大家都能自己写出类似功能的代码。&lt;br /&gt;我也就不献丑了。&lt;/p&gt;&lt;p&gt;最后把另外两个 用上面的方式 进行碰撞检测 的demo 贴出来：（都需要canvas支持）&lt;br /&gt;&lt;a href="http://hongru.github.com/proj/laro/test/laro.collision.test2.html"&gt;http://hongru.github.com/proj/laro/test/laro.collision.test2.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://hongru.github.com/proj/laro/test/laro.collision.test3.html"&gt;http://hongru.github.com/proj/laro/test/laro.collision.test3.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;【注：test3 demo 引入的弹性碰撞 和 非弹性碰撞的概念， 这个后面再说】&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;【后记】&lt;br /&gt;代码功能越多，复杂度越高，必然导致计算量增加。 更重要的是找一个权衡吧。&lt;br /&gt;PS，吐槽下，所谓的弹性工作时间没了，明天要9点前到公司，坑爹，要早起了。&lt;br /&gt;今天早点睡吧，各位晚安 : )&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2427590.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/03/31/2427590.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/03/28/2420415.html</id><title type="text">事情没有想象中那么难--JX官网首页3D粒子效果</title><summary type="text">上周为AlloyTeam/JX做了个简单的官网http://alloyteam.github.com/JX/，当时文档，demo以及其他的附属工具都还没完善，地址就流了出去...独立的粒子特效demo 可以看这里http://hongru.github.com/proj/laro/examples/jxhome/JX 作为webqq的底层，框架本身怎么样，我这里暂时不作评论，很多同学对JX官网home页的opening动画的实现很感兴趣，我这里就简单说一下实现的思路。应该没有大家想象中麻烦。【关于canvas的使用】home页的粒子效果 其实是受启发于 Google 2011年的I/O 大会的</summary><published>2012-03-27T16:43:00Z</published><updated>2012-03-27T16:43:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/03/28/2420415.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/03/28/2420415.html"/><content type="html">&lt;p&gt;上周为&lt;a href="https://github.com/alloyteam/JX" target="_blank"&gt;AlloyTeam/JX&lt;/a&gt;做了个简单的官网&lt;a href="http://alloyteam.github.com/JX/"&gt;http://alloyteam.github.com/JX/&lt;/a&gt;，当时文档，demo以及其他的附属工具都还没完善，地址就流了出去...&lt;/p&gt;&lt;p&gt;独立的粒子特效demo 可以看这里&amp;nbsp;&lt;a href="http://hongru.github.com/proj/laro/examples/jxhome/"&gt;http://hongru.github.com/proj/laro/examples/jxhome/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;JX 作为webqq的底层，框架本身怎么样，我这里暂时不作评论，很多同学对JX官网home页的opening动画的实现很感兴趣，我这里就简单说一下实现的思路。应该没有大家想象中麻烦。&lt;/p&gt;&lt;p&gt;【关于canvas的使用】&lt;/p&gt;&lt;p&gt;home页的粒子效果 其实是受启发于 Google 2011年的I/O 大会的opening。只是实现思路可能稍有不同。我知道I/O大会上他们的opening用的是Three.js 来做3D渲染，但是具体的细节我也没有深究。我这里只是说一下我自己的实现思路。&lt;/p&gt;&lt;p&gt;目前这个例子效果是基于canvas的，其实用dom也是可以实现的。只是担心大量的dom操作和appendChild，removeChild之类的，对性能和内存的影响蛮大，就没去做兼容。&lt;/p&gt;&lt;p&gt;实现思路就是：基于canvas的 3D 粒子 旋转算法 + 依赖imageData 生成 模型数组。&lt;/p&gt;&lt;p&gt;【3D粒子旋转】&lt;/p&gt;&lt;p&gt;这个部分说麻烦也麻烦，说简单也很简单。其实就是两三个数学公式的事儿。我之前有好几篇随笔都是讲它的，所以我这里也就不重复赘述了。具体可以参考：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2011/09/12/2174187.html" target="_blank"&gt;rotate 3D [初识篇]&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2011/09/16/2178673.html" target="_blank"&gt;rotate 3D 篇二&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://hongru.github.com/share/3D.html" target="_blank"&gt;http://hongru.github.com/share/3D.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;【怎么让粒子组成指定形状？】&lt;/p&gt;&lt;p&gt;最开始想到的思路，是自己拼数组出来，类似现在很多html5小游戏的地图数据 一样，通过数组中特定的值来表示某一个 具体的位置，比如这个demo（&lt;a href="http://hongru.github.com/test/google-clock.html"&gt;http://hongru.github.com/test/google-clock.html&lt;/a&gt;）里面的 由粒子组成的数字，其实最开始就是一组特定的数组，如下：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;    var NUM = [&lt;br/&gt;        '####   ##########  #####################    ',&lt;br/&gt;        '#  #   #   #   ##  ##   #      ##  ##  #    ',&lt;br/&gt;        '#  #   #   #   ##  ##   #      ##  ##  #  # ',&lt;br/&gt;        '#  #   #####################   #########    ',&lt;br/&gt;        '#  #   ##      #   #   ##  #   ##  #   #  # ',&lt;br/&gt;        '#  #   ##      #   #   ##  #   ##  #   #    ',&lt;br/&gt;        '####   #########   #########   #########    '&lt;br/&gt;    ]&lt;br/&gt;&lt;/div&gt;&lt;p&gt;由左向右分别是0-9以及冒号。 通过这种数组， # 就代表一个粒子。 这种人工生成 模型 数组的方式还可以处理一些简单的模型，例如这种 数字，或者少数字母等等。&lt;/p&gt;&lt;p&gt;但是如果想要让粒子组成一些稍微复杂一些的图案，人工排列的方式就变得不太靠谱了。&lt;/p&gt;&lt;p&gt;那有什么办法可以让它自动生成这种类似的模型数组呢？&lt;/p&gt;&lt;p&gt;canvas的imageData就有用了。我们知道canvas支持像素级别的操作，可以把一个canvas画布里面每一个像素点都拿出来独立处理。&lt;br /&gt;假如 ，我们把一个canvas画布上不透明或者有颜色的所有像素点都找出来，把每一个像素点都当成一个粒子的话，那么岂不是我们在canvas上写的字，draw的image等等，都可以由很多粒子组成？&lt;/p&gt;&lt;p&gt;比如这个demo（&lt;a href="http://hongru.github.com/test/my-text-particle.html"&gt;http://hongru.github.com/test/my-text-particle.html&lt;/a&gt;）：&lt;/p&gt;&lt;p&gt;大家注意这一段代码：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;        getImageData: function () {&lt;br/&gt;            var imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);&lt;br/&gt;            for (var x = 0; x &amp;lt; imageData.width; x ++) {&lt;br/&gt;                for (var y = 0; y &amp;lt; imageData.height; y ++) {&lt;br/&gt;                    //var i = 4*(x * imageData.height + y);&lt;br/&gt;                    var i = 4*(y * imageData.width + x);&lt;br/&gt;                    if (imageData.data[i + 3] &amp;gt; 128) {&lt;br/&gt;this.place ++;&lt;br/&gt;                        (this.place%4 == 0) &amp;amp;&amp;amp; this.particles.push(new Particle(x, y, this.canvas));&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        },&lt;br/&gt;&lt;/div&gt;&lt;p&gt;我们遍历canvas的指定区域像素点，把透明度大于 0.5 的点都找出来，当成一个particle来处理。 当然，为了减少并行的粒子数造成的性能问题，我这里 把符合 条件的粒子 又除以了4.&lt;/p&gt;&lt;p&gt;看到这里，我想应该好多同学都明白了怎么让粒子组成想要的图形或者字符了吧。没错，就是借助图片，利用imageData取出 想要的点。 然后 记录下它的位置和颜色，利用这些信息生成自定义的粒子。&lt;/p&gt;&lt;p&gt;当然，这里还有个小技巧，就是关于图片大小的控制，因为imageData是像素级别的，所以太大图片，必然导致过多的粒子，我们做这种效果并不需要多精确的形状，而且太多的粒子在后续渲染上 是个 极大的消耗，所以 通常 控制在 20*20 大小，算比较合适，也就是保持在 400 个粒子左右变化， 在图形组成和 变换性能上 取一个权衡点。&lt;/p&gt;&lt;p&gt;【最后，关于进程的控制】&lt;/p&gt;&lt;p&gt;&amp;nbsp;这也是一个关键点，通常，一个稍有经验的开发者，有了上面说的几点知识后，应该就能大致写出类似的效果了，但是如果涉及到像这种连续几个不同的动画 来 转换的时候， 进程的控制 会让人觉得头疼， 因为从一个动画 进入到下一个动画的时候，为了降低性能消耗 ，需要把上一次 使用的粒子 请出循环数组， 而是用下一个 动画需要的 模型数组来变化。另外 我们还需要在 动画 连续帧变化 的loop 中控制每个动画过程的时间， 以便到了某个节点 可以进入到下一个 动画进程。&lt;/p&gt;&lt;p&gt;为了很好的控制进程，简化编码思路，我这里引入了一个 【有限状态机 FSM】的概念。引入这个概念之后，基本上相似的动画，每个动画进程可以独立成 一个 status来处理，互相基本没有耦合，只用关心自己即可。而且代码都具有相似的结构&lt;/p&gt;&lt;p&gt;进入状态-update更新-重绘draw-状态转换条件transition- 离开状态leave &lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012032800370259.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;这很好的帮助了我们简化思路。便利的控制整个进程。&lt;/p&gt;&lt;p&gt;关于有限状态机 更多的信息和 源码以及使用方法， 可以参照源码：&lt;br /&gt;&lt;a href="https://github.com/hongru/Laro/blob/master/src/game/fsm.js"&gt;https://github.com/hongru/Laro/blob/master/src/game/fsm.js&lt;/a&gt;&lt;br /&gt;&lt;a href="https://github.com/hongru/Laro/blob/master/src/game/state.js"&gt;https://github.com/hongru/Laro/blob/master/src/game/state.js&lt;/a&gt;&lt;/p&gt;&lt;p&gt;【后记】&lt;/p&gt;&lt;p&gt;思路大致就是这样，当然，细节方面还有很多需要注意的地方，如果感兴趣的同学不妨一点点尝试，从3D 旋转算法开始， 完成一个类似的特效。 对编码思路 以及 简易的图形学算法 应该会有些帮助。&lt;/p&gt;&lt;p&gt;大家晚安 : )&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2420415.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/03/28/2420415.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/03/16/2402324.html</id><title type="text">【Laro】- About Game Engine</title><summary type="text">经过断断续续折腾。关于html5 canvas 的一个游戏引擎的底层框架部分大致完成了。现在是version 0.1， 还有很多不完善的地方，或者是bug。亦或设计不合理的地方。我把地址放出来，正好我看院子里也有些同学写html5 的小游戏或者 游戏引擎。 感兴趣的同学可以大家互相交流一下，互帮互助，一起成长。关于Laro ，放在github上https://github.com/hongru/Laro或者https://github.com/AlloyTeam/Laro都可以看到源码。感兴趣的同学，或者有好的意见建议的同学 可以在上面留言。互相交流。如果说是游戏引擎，目前还仅仅完成了30%-</summary><published>2012-03-16T15:13:00Z</published><updated>2012-03-16T15:13:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/03/16/2402324.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/03/16/2402324.html"/><content type="html">&lt;p&gt;经过断断续续折腾。关于html5 canvas 的一个游戏引擎的底层框架部分大致完成了。&lt;/p&gt;&lt;p&gt;现在是version 0.1， 还有很多不完善的地方，或者是bug。亦或设计不合理的地方。&lt;/p&gt;&lt;p&gt;我把地址放出来，正好我看院子里也有些同学写html5 的小游戏或者 游戏引擎。 感兴趣的同学可以大家互相交流一下，互帮互助，一起成长。&lt;/p&gt;&lt;p&gt;关于Laro ，放在github上&amp;nbsp;&lt;a href="https://github.com/hongru/Laro"&gt;https://github.com/hongru/Laro&lt;/a&gt;&amp;nbsp;或者&amp;nbsp;&lt;a href="https://github.com/AlloyTeam/Laro"&gt;https://github.com/AlloyTeam/Laro&lt;/a&gt;&amp;nbsp;都可以看到源码。&lt;/p&gt;&lt;p&gt;感兴趣的同学，或者有好的意见建议的同学 可以在上面留言。互相交流。&lt;/p&gt;&lt;p&gt;如果说是游戏引擎，目前还仅仅完成了30%-40%的工作，因为最终目的是要以底层框架为基础，搭建一个可视化的游戏编辑器。方便快捷的制作html5 小游戏。&lt;/p&gt;&lt;p&gt;所以今后还会有漫漫长路。不停的优化，继续。&lt;/p&gt;&lt;p&gt;里面也放了利用这个底层框架 和 网络上的一些素材资源 制作的 简易的演示demo。可以稍微体验一下，可能会有些bug。&lt;/p&gt;&lt;p&gt;&lt;a href="http://hongru.github.com/proj/laro/examples/emberwind/"&gt;http://hongru.github.com/proj/laro/examples/emberwind/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://hongru.github.com/proj/laro/examples/typeshot/index.html"&gt;http://hongru.github.com/proj/laro/examples/typeshot/index.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;希望能和大家多交流。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;另外，这里再附上 JX 框架的 地址，也已经开源到github。&lt;a href="https://github.com/AlloyTeam/JX"&gt;https://github.com/AlloyTeam/JX&lt;/a&gt;&amp;nbsp;关于Jx，是webQQ&lt;a href="http://web.qq.com/"&gt;http://web.qq.com/&lt;/a&gt;&amp;nbsp;2.0~4.0 一直在使用的一个底层js框架。目前正在写一些详细一些的介绍文档和官网制作。大家可以提前先去看一下。也欢迎提一些意见和建议。&lt;/p&gt;&lt;p&gt;各位晚安 : )&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2402324.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/03/16/2402324.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/03/16/2394332.html</id><title type="text">【前端应该知道的那些事儿】运动学基础</title><summary type="text">【写在前面的话：】前不久刚看到过一句话：说好的技术文章应该让读者感觉增加信心，而不是失去信心。有感于这句话是因为以前觉得发一些貌似高深的，看起来nb的东西才算一篇好博文，可是多少有点炫技的成分。可是后来越发觉想把一个看起来简单的问题说通透也着实不易。我希望今后的文章多少能带给更多的读者一些帮助吧。 这是我的目标之一。web前端，确实算编码里面的挺特殊的一个职位，不仅仅要理性的编码，还要感性的接触UI，通常我都把这种工作叫做需要情商的码字工作者。要说前端有多难，我想会被很多做算法或者底层的同学所不齿。确实，前台的工作并不算难，尤其是web端的前台，有困难的部分，那也是少数。所以在互联网发展初期，</summary><published>2012-03-16T12:41:00Z</published><updated>2012-03-16T12:41:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/03/16/2394332.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/03/16/2394332.html"/><content type="html">&lt;p&gt;&lt;span style="color: #008000;"&gt;【写在前面的话：】&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;前不久刚看到过一句话：说好的技术文章应该让读者感觉增加信心，而不是失去信心。&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;有感于这句话是因为以前觉得发一些貌似高深的，看起来nb的东西才算一篇好博文，可是多少有点炫技的成分。可是后来越发觉想把一个看起来简单的问题说通透也着实不易。我希望今后的文章多少能带给更多的读者一些帮助吧。 这是我的目标之一。&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;web前端，确实算编码里面的挺特殊的一个职位，不仅仅要理性的编码，还要感性的接触UI，通常我都把这种工作叫做需要情商的码字工作者。&lt;/p&gt;&lt;p&gt;要说前端有多难，我想会被很多做算法或者底层的同学所不齿。确实，前台的工作并不算难，尤其是web端的前台，有困难的部分，那也是少数。所以在互联网发展初期，都没有前端这个职位，就算后来有了前端这个职位，也曾被当作是门槛最低的IT类职位之一。很多同学学习前端相关的知识，初衷很简单，因为好学，包括当年的自己也是一样 : )&lt;/p&gt;&lt;p&gt;当然，如今随着交互逻辑的不断复杂，用户体验的不断提升，外带很多后端的逻辑也都纷纷转到前端来实现。前端的工作者们开始有了一些价值。当然，你要担当的更多，必然需要会的更多。所以如今对于一个优秀的前台编码工作者来说，要求高了很多。&lt;/p&gt;&lt;p&gt;但是同样，还是不能算难。因为哪怕前端们开始接触一些算法，一些数学物理的东西，但常用的，通常也仅限于初衷，高中的程度。所以神马高等数学，高等物理之类的。咱暂时还用不上，大家完全不用惧。&lt;/p&gt;&lt;p&gt;那么，比如：&lt;/p&gt;&lt;p&gt;【关于缓动】&lt;br /&gt;我相信在DHTML时代，也就是所谓的动态 html 的时候，那时候javascript 脚本除了用来做一些表单验证和提交之外，开始干起让页面动起来的事情。最常见的莫过于什么幻灯片，轮播banner之类的。甚至在当时能够手写出一款好的，兼容的轮播插件成了一件非常niu的事儿。那么回想一下，我们最开始学习，尝试自己写一个轮播插件的时候，遇到的头疼的事儿，我想缓动应该算一个了吧。&lt;br /&gt;好吧，咱们就先来说说它。&amp;nbsp;&lt;/p&gt;&lt;p&gt;缓动难吗，不难，我们先说一个最简单的。所谓&amp;ldquo;缓动&amp;rdquo;，无非就是运动的越来越缓慢呗。那么怎么让一个物体运动的越来越缓慢呢。&lt;/p&gt;&lt;p&gt;我们用的计时器，不管你是用setInterval也好，setTimeout也好，或者 requestAnimationFrame也好，思路都一样。反正把它想象成是单位时间重复调用某个函数就行。那就好，既然单位时间是一样，那么我们让单位时间内 物体运行的距离 越来越小不就成了&amp;ldquo;缓动&amp;rdquo;了吗。&lt;/p&gt;&lt;p&gt;ok，咱们可以试试看：&lt;/p&gt;&lt;p&gt;假如我们有一段固定的路程100米，然后让物体 每个单位时间里面运动的距离都是 它距离目的地剩余距离的1/10， 什么意思呢。 即物体最开始距离目的地100米，那么它第一个单位时间里朝目的地运动 100*1/10 ，即10米， 于是，第二个单位时间里，它距离目的地 就只有90米了，那么第二次运动 90*1/10 ，即9米，... 不断叠加下去，由于物体总是距离目的地越来越近，那么 它单位时间里运动的距离必然越来越小。 这不就达到了 我们缓动的目的的了么。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;(function () {&lt;br/&gt;var moveDis = 0,&lt;br/&gt;conEl = document.getElementById('container'),&lt;br/&gt;maxDis = (conEl.offsetWidth-22) || (800-20), // 总距离&lt;br/&gt;moveEl = document.getElementById('move');&lt;br/&gt;&lt;br/&gt;function step () { &lt;br/&gt;var nowLeft = parseInt(moveEl.style['left']),&lt;br/&gt;leftDis = maxDis - nowLeft, // 获取距离目的地的距离&lt;br/&gt;stepDis = Math.ceil(leftDis*.015); // 每次移动 剩余距离的 固定百分比。&lt;br/&gt;&lt;br/&gt;nowLeft += stepDis; // 不断叠加&lt;br/&gt;moveEl.style['left'] = nowLeft + 'px';&lt;br/&gt;&lt;br/&gt;requestAnimFrame(step); // repeat&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;step();&lt;br/&gt;&lt;br/&gt;})();&lt;br/&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="btn1" href="http://hongru.github.com/test/cnblogs/0313/1.html" target="_blank"&gt;Tween Demo 1&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;当然，这是最简单的模式，咱们接着往下看。&lt;/p&gt;&lt;p&gt;那么，那些看起来高深的缓动公式是怎么来的呢？？&lt;/p&gt;&lt;p&gt;其实也很简单，想想我们初中，高中学的数学吧， 二次函数，三角函数之类的。&lt;/p&gt;&lt;p&gt;先看二次函数，也就是我们的抛物线：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012031320314967.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;为什么我要说先看二次函数或者三角函数呢。他们的轨迹跟 缓动有什么关系？我们接着往下看：&lt;/p&gt;&lt;p&gt;拿上面的那个最简单的demo举例，我们把 方块的运行距离s 和时间 t的运动关系 画出来，看会是什么样子的。&lt;/p&gt;&lt;p&gt;看这个demo：&lt;br /&gt; &lt;a class="btn1" href="http://hongru.github.com/test/cnblogs/0313/2.html" target="_blank"&gt;Tween Demo 2&lt;/a&gt;&lt;/p&gt;&lt;p&gt;这里面的缓动算法跟上面那个最简单的模式一模一样。我们把它的 t-s 路线图画出来，可以看出一点端倪了吧。没看出来的同学，把它旋转一下，想象成 x轴 时间， y轴位移。那么是不是就跟 我上面画那个二次函数 的左半部分 的形状很像。&lt;/p&gt;&lt;p&gt;所以，到此为止，相信不难理解，为什么缓动的公式通常和二次函数或者三角函数有关，直观一点的话说，就是在某一个区间内 位移的变化率 是随着时间递减的。 那么这种 轨迹都可以用作 缓动公式。&lt;/p&gt;&lt;p&gt;那么，&lt;/p&gt;&lt;p&gt;我们怎么用二次函数来做缓动呢？很简单，大家随着我的思路来。我们要设计一个缓动的接口api，假如是类似下面这样,我们先想一个最简单的方式。&lt;/p&gt;&lt;p&gt;【已知】：一物体要从 0 运行到 400， 运行时间为1秒（1000ms）。&lt;br /&gt;那么我们怎么为它来设计一个二次函数的缓动呢。我们先画一个示意图：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012031612282462.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;那么求的 方程 的系数 a = 0.00005, b = -0.1;&lt;/p&gt;&lt;p&gt;那么方程就出来了 s = 0.00005*t^2 - 0.1*t &amp;nbsp;(0 &amp;lt; t &amp;lt;=1000);&lt;/p&gt;&lt;p&gt;剩下的就好办了，把每个时间点的位置渲染出来就好了。&lt;br /&gt;例如，我们做个例子，设计一个api：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;// from 表示起始点&lt;br/&gt;// to 表示到达位置&lt;br/&gt;// t 表示运行总时间&lt;br/&gt;tween(from, to, t)&lt;br/&gt;&lt;/div&gt;&lt;p&gt;按照上面说的思路，其实就是已知运行距离（from-to）和运行时间t ，求一个二次函数公式而已。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;// 二次函数 s = a*t^2 + b*t;&lt;br/&gt;// 顶点： (to-from) = a*t^2 + b*t&lt;br/&gt;// 右侧x轴交点： 0 = a*(2t)^2 + b*2t&lt;br/&gt;// 得出 a = -(to-from)/t^2; b = 2(to-from)/t;&lt;br/&gt;&lt;br/&gt;     var left = a*st*st + b*st;&lt;br/&gt;     o.style['left'] = from + left + 'px';&lt;br/&gt;&lt;/div&gt;&lt;p&gt;看demo：&lt;br /&gt;　 &lt;a class="btn1" href="http://hongru.github.com/test/cnblogs/0313/3.html" target="_blank"&gt;Tween Demo 3&lt;/a&gt; 　(demo里面由于用的普通的dom生成的点图，会占内存，请不要测试过多次 ^^).&lt;/p&gt;&lt;p&gt;原理其实就是那么简单，其实大家可以自己试一下，熟悉了之后完全可以封装出自己的好用的，易用的缓动方法。&lt;/p&gt;&lt;p&gt;用这类的二次函数，还有一个很常见的场景，就是&amp;ldquo;重力系统&amp;rdquo;。&lt;/p&gt;&lt;p&gt;我们知道，如果忽略所谓的空气阻力和一些外界干扰因素，重力系统其实就完全可以简化成 二次函数（抛物线）问题。&lt;/p&gt;&lt;p&gt;比如我们做个小游戏，系统有固定的向下的重力 g ,那么由用户操作的主角 在像上跳的过程中，就完全可以按 上面说的方式来考虑。&lt;/p&gt;&lt;p&gt;基本思路上面都说了，这次我们换个思路。都说数学和物理是相通的，那么这次已知 在一个重力系统中，跳起初速度和重力大小。那么假设一个物体跳起该怎么运动呢？&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;S = v0*t + a*t^2&lt;br/&gt;v0 = a*t&lt;/div&gt;&lt;p&gt;这两个应该是初中的物理公式吧。已知初速度v0 和加速度 a ，求位移还不简单。&lt;br /&gt;其他的我就不多说了，看一个简单的demo吧：&lt;br /&gt;&lt;a class="btn1" href="http://hongru.github.com/test/cnblogs/0313/4.html" target="_blank"&gt;弹跳Demo&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;看完了【二次函数】，咱们再看看【三角函数】，其实在我们常用的特效中，三角函数能做的事情比二次函数多很多。但是今天就只讲跟【缓动】相关的。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/160412/2012031618105440.png" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;前面说了，凡是大家看到类似这种&amp;ldquo;山坡&amp;rdquo;形状的图，基本都可以做成类似的缓动。那么我们取sin函数的前 PI/2 部分，可以看出他也完全满足所谓的 缓动的图形条件。&lt;/p&gt;&lt;p&gt;而且，基于sin函数做的缓动公式 相对于二次函数而言，思路更简单。因为更容易得出 位移相对时间的公式S-T：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;/**&lt;br/&gt; 我们假设 每一帧 间隔时间为 dt， 那么在这个dt时间内 运动的距离为ds&lt;br/&gt;那么，假设一个物体 从 from 移动到 to 所花的时间为t， 则容易得出 在这个时间区间内用sin公式得到 每个dt的位移公式ds&lt;br/&gt;*/&lt;br/&gt;function tween (from, to, t) {&lt;br/&gt;    // sin函数; ds = (to-from)*Math.sin(Math.PI*dt/(2*t));&lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;剩下的工作，就是把计算出来的当前位置渲染到页面即可。我们这里还是以类似的例子为例：&lt;br /&gt;　 &lt;a class="btn1" href="http://hongru.github.com/test/cnblogs/0313/5.html" target="_blank"&gt;sin 缓动 demo&lt;/a&gt; 　&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #008000;"&gt;【写在后面的话】&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;不知不觉也写了这么多了，所谓&amp;ldquo;会者不难&amp;rdquo;，本文说到底其实涉及的技术技巧其实并不多，我花这么大篇幅来说也是希望能给对运动学还不甚了解的同学一点帮助吧。&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #008000;"&gt;我希望我能把简单的东西说明白，至于有没有达到这个目的我也不得而知。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #008000;"&gt;其实在前端的工作里，还有一些常用的数学和物理知识，但是都不难。说起来都很简单。比如前一阵的mac QQ浏览器的logo 周围的闪动旋转的星星。就是用了简单的椭圆公式。&lt;/span&gt;&lt;br /&gt;http://hongru.github.com/test/qqbrowser/index.html&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2394332.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/03/16/2394332.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/02/26/2368285.html</id><title type="text">每天出门前，记得提醒自己一遍，别落下了梦想</title><summary type="text">好久没有更新blog了。今天终于有了心再写一篇。算是对自己工作和生活的一种回顾吧。11年底和12年初的时候自己都没有写什么总结性质的博文，或者之类的东西。但是心里确是有稍微对过去的时间做一点结算工作的。【关于个人】过去差不多一年的时间里。做的事情不多，但是其实也不少。我这里都可以大概列出除了日常在公司工作之外自己在业余为自己专业提升而做的事情。在自己的github上面，去年有不少更新。主要的工作集中在3个较为大的项目上面。一个是自己一点一点累计起来的关于javascript的基本lib。取名叫做Leta,内容倒没什么特别值得说的，都是根据工作经验和工作需求累积起来的关于前端工作的基本脚本库。当</summary><published>2012-02-25T16:19:00Z</published><updated>2012-02-25T16:19:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/02/26/2368285.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/02/26/2368285.html"/><content type="html">&lt;p&gt;好久没有更新blog了。今天终于有了心再写一篇。算是对自己工作和生活的一种回顾吧。&lt;/p&gt;&lt;p&gt;11年底和12年初的时候自己都没有写什么总结性质的博文，或者之类的东西。但是心里确是有稍微对过去的时间做一点结算工作的。&lt;/p&gt;&lt;p&gt;【关于个人】&lt;/p&gt;&lt;p&gt;过去差不多一年的时间里。做的事情不多，但是其实也不少。我这里都可以大概列出除了日常在公司工作之外自己在业余为自己专业提升而做的事情。在自己的&lt;span style="color: #ff6600;"&gt;&lt;strong&gt;&lt;a href="https://github.com/hongru" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;github&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;上面，去年有不少更新。主要的工作集中在3个较为大的项目上面。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;一个是自己一点一点累计起来的&lt;strong&gt;关于javascript的基本lib&lt;/strong&gt;。取名叫做&lt;span style="color: #ff6600;"&gt;&lt;strong&gt;&lt;a href="https://github.com/hongru/Leta" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;Leta&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;,内容倒没什么特别值得说的，都是根据工作经验和工作需求累积起来的关于前端工作的基本脚本库。当然还并没有完成，目前大概80%的样子吧。并且没有过多整理，相对有一点凌乱，在当前市面上已有众多相对完善的javascript lib 或者framework来说，可能好多人会说我这算又一个闭门造轮子，没什么太大的意义。然而我不这么想，我并不把它当成一个轮子，仅当作这是我专业成长的一个必经之路。重要的积累和沉淀的过程，至于会不会进行推广，有没有人来用，那又是另外一回事了。现在的我倒是没那么关注。&lt;/li&gt;&lt;li&gt;第二个是&lt;strong&gt;一个webgl的lib&lt;/strong&gt;。取名&lt;span style="color: #ff6600;"&gt;&lt;strong&gt;&lt;a href="https://github.com/hongru/Sandy" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;Sandy&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;,这个名字我倒是觉得可以小说一下。为什么会取名叫sandy，估计好多人也发现了，sandy的读音和中文的&amp;lsquo;3D&amp;rsquo;读音蛮像的。所以为社么我们只能让英文的东西音译成中文？我们也可以把中文的发音译成英文不是吗？前一年我大概花了半年的时间一直在研究webgl相关的东西，倒也不是说想做出什么惊世骇俗之作。国外关于这方面前沿技术的研究比我们国内成熟很多，以three.js的代表的3D 的脚本框架 稍微列一下，都可以列出不下10个。他们确实是我们学习和借鉴的很好的榜样。 因为兴趣原因，我倒也做了不少关于3D建模的demo，包括算法模拟的也好，使用webgl的也好。也做过一两次分享。这里还有一个分享时总结的slide &lt;span style="color: #ff6600;"&gt;&lt;strong&gt;&lt;a href="http://hongru.github.com/share/3D.html" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;http://hongru.github.com/share/3D.html&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;&lt;em&gt;（请使用现代浏览器浏览，鼠标拖拽翻页）&lt;/em&gt;。&lt;br /&gt;这是关于3D的东东。另外最近还利用webgl做了一个&lt;span style="color: #ff6600;"&gt;&lt;strong&gt;简单的照片美化的工具 &lt;a href="http://hongru.github.com/test/FiPhoto/fiphoto.html" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;Mr.Photo&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;。出发点也很简单，因为自己非常喜欢ios上的一个照片分享的应用instagram，里面对于图像滤镜的处理做的非常出色。能让不会拍照的我，用手机拍的照片经过它的滤镜就能变出大片的感觉。所以也就是想尝试一下。正好借助glfx，做了这个小东西。有兴趣的同学可以试一下。&lt;/li&gt;&lt;li&gt;第三个主要的方面就是也是大概持续了好几个月一直到现在还在做的一个&lt;span style="color: #ff6600;"&gt;&lt;strong&gt;html5 基于canvas 的2d或者2.5d 的游戏引擎。取名叫&lt;a href="https://github.com/hongru/hongru.github.com/tree/master/proj/laro" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;Laro&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;, 我这里也说一下这个名字的由来。这个单词念起来有点奇怪，按英文的发音有点像&amp;ldquo;腊肉&amp;rdquo;,其实这个单词是菲律宾语中 &amp;ldquo;游戏&amp;rdquo;的意思。 至于为什么选他，无非就是我对 L 打头的单词都有莫名的好感吧。这个游戏引擎经过大概接近半年的断断续续的更新。现在主体框架也算大概完成了90%左右了吧。只是一直在往上堆功能，还没有时间进行整理。比较凌乱。文档和demo相对比较缺乏。我这里经过在网上不断的搜集素材，&lt;span style="color: #ff6600;"&gt;&lt;strong&gt;拼接出了一个简单的&lt;a href="http://hongru.github.com/proj/laro/examples/emberwind/index.html" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;Demo&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&amp;nbsp;demo里面基本涵盖了做一个基于canvas的2d类游戏所需的基本的东西，比如场景切换，资源加载，动画帧的绘制，进程控制，声音控制，输入输出等等。当然刚说了，这只是一个不成形的demo。暂不用过多深究。随着html5技术的演变和不断推进，当前html5 相关的游戏框架也不算少了。而自己这个的亮点在哪里？我自己认为是对于游戏进程的控制上，也就是&amp;ldquo;&lt;strong&gt;有限状态机FSM&lt;/strong&gt;&amp;rdquo;，详细这个东西对很多人来说并不陌生。但是目前把这个概念引入的页游的制作思路上的，好像还不多。 至于这个东西对于像游戏这种 典型的 事件驱动的 编程模型 的好处，有兴趣的同学可以看看代码。后续的文档等我这个框架完善一些了。我会详细补上。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;【接下来的打算】&lt;/p&gt;&lt;p&gt;刚才也说了，上面3块东西耗了我基本业余大部分的时间和精力。上面3个项目总的代码量 加上注释的话，应该大约在15000 行左右。看起来不算多。但这确实挺费时费力的，跟平时的业务代码确实不太一样。所以关于去年一年自己的积累和成长，自己还算满意的。&lt;/p&gt;&lt;p&gt;同时，正因为一年内同时进行了3个项目，无法集中精力来完成其中一个，导致了上面3个项目的完成度都不太理想。大概都是在80%~90%左右。这也是我为什么之前一直没有公开宣布的原因。所以&lt;span style="color: #333399;"&gt;&lt;strong&gt;接下来一段时间的工作就是把这3个东西好好的完善下去。包括代码组织的调整，部分重构，以及冗余代码的提出，目录结构的整理。 还有完整的项目文档，以及各个方面的demo与讲解。 这会是很大一部分的工作量。&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我的目的也不是说要多少人来用我的东西，或者要多少人知道，只是希望能够通过我个人的努力，完成一些东西，能给业界带来一些新鲜的活力。就已经很满足了。当然如果有人觉得代码里面有些部分有值得用的地方。部分拷出去使用也是ok的。&lt;/p&gt;&lt;p&gt;【关于团队】&lt;/p&gt;&lt;p&gt;去年7月份我换了工作。换了城市。到现在也有7个月有余了。在新的团队里面。我们希望也能为前端的技术发展做出一点点小小的贡献。所以今年除了工作上业务的需求外，团队的建设和发展也会是团队里每一个成员的重责。目前已经有开始着手在做了。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;span style="color: #333399;"&gt;有了团队自己的 blog&amp;nbsp;&lt;a href="http://webpluz.org/" target="_blank"&gt;&lt;span style="color: #333399;"&gt;http://webpluz.org/&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;,虽然blog上线才不久，内容也不算多，但是大家都会努力的将自己日常的工作积累往上分享的。我自己今后的blog大部分也会同时跟新到 这个团队blog上面。我们会尽量把自己的团队blog做起来，尽量保质保量。主要方向还是在前端的前沿技术方面。希望大家如果觉得里面内容对自己还算有些帮助，多捧个场。&lt;/li&gt;&lt;li&gt;&lt;span style="color: #333399;"&gt;&lt;strong&gt;关于【html5沙龙】。这个之前一直没提及。&lt;a href="http://www.mhtml5.com/" target="_blank"&gt;&lt;span style="color: #333399;"&gt;http://www.mhtml5.com/&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;不了解的同学可以去看看。主要是w3ctech 和 html5 研究小组，联合举办的在各大城市分享和推广html5相关技术的活动。基本每月在各大城市都会有，请相关同行进行分享交流，或者组织讨论，以及codejam等各种形式。目前深圳区的活动 都是由我们团队组织和举办的。怎么说我也算是参与和组织者之一吧。所以也希望大家感兴趣的可以经常关注下这方面的活动。&lt;/li&gt;&lt;li&gt;另外团队也刚在github上建立了一个organization，希望今后能有好的作品以团队的形式开源给同行。&lt;span style="color: #333399;"&gt;&lt;strong&gt;&lt;a href="https://github.com/organizations/qwt" target="_blank"&gt;&lt;span style="color: #333399;"&gt;https://github.com/organizations/qwt&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&amp;nbsp;，由于刚建立，目前里面暂时没有什么内容。但是这里可以给大家提前透露一下。可能不久后就会有好几个项目开源到上面。包括&lt;ul&gt;&lt;li&gt;&lt;span style="color: #333399;"&gt;&lt;strong&gt;css3 UI Lib， 一个方便大家选取使用 css3常用ui特效和控件的lib&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="color: #333399;"&gt;&lt;strong&gt;还有就是上面提到的，关于3D建模的framework。&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="color: #333399;"&gt;&lt;strong&gt;经过完善后的游戏引擎，包括游戏制作相关工具等。&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="color: #333399;"&gt;&lt;strong&gt;新版的Jx 框架等等。&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;好了，说了这么多，该总结的，该展望的都差不多了。接下来就剩踏实努力的继续朝着自己的目标前进吧。&lt;/p&gt;&lt;p&gt;随着年龄的日益增长，也渐渐对自己所谓的梦想开始有了逐渐清晰的认知。至于梦想是什么？可能每个人都不太一样吧，这里暂时就不说了，梦想说的白了就没那么有力量了。&lt;/p&gt;&lt;p&gt;这里希望所有有梦想的人都踏踏实实努力的为了它努力吧。&lt;span style="color: #333399;"&gt;&lt;strong&gt;要相信，只要你付出的够多，一定会有回报的&lt;/strong&gt;&lt;/span&gt;。&lt;/p&gt;&lt;p&gt;今天刚好看了一个关于 动画大师 宫崎骏 和他儿子 工作的一段纪录片。 很有感概：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;关于亲情，梦想，执着与坚持。对于专业度的严苛以及完美主义。这一定是其中最好的诠释之一。梦想不是说说而已的，梦想一定是一生的方向。 今天和朋友吃完饭闲聊，我们为什么会对生活有诸多抱怨。 那是因为原来我们对自己要求太低了。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff6600;"&gt;有兴趣的朋友可以看看&lt;a href="http://v.youku.com/v_show/id_XMzA1NTU5MDI4.html" target="_blank"&gt;&lt;span style="color: #ff6600;"&gt;http://v.youku.com/v_show/id_XMzA1NTU5MDI4.html&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;好了，今天到此为止，各位&lt;strong&gt;&lt;span style="color: #ff6600;"&gt;晚安&lt;/span&gt;&lt;/strong&gt;。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2368285.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/02/26/2368285.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/01/14/2322540.html</id><title type="text">canvas保存为data:image扩展功能的实现</title><summary type="text">【已知】canvas提供了toDataURL的接口，可以方便的将canvas画布转化成base64编码的image。目前支持的最好的是png格式，jpeg格式的现代浏览器基本也支持，但是支持的不是很好。【想要的】往往这么简单直接的接口通常都满足不了需求。我想要的不仅是简单的通过画布生成一个png，我不想新开一个tab，然后还要右键另存为...我还需要更方便的自由的配置生成的图片的大小，比例等。另外如果我还要别的图片格式，比如位图bmp，gif等怎么办...【解决办法】a)想直接把图片生成后download到本地，其实办法也很简单。直接改图片的mimeType，强制改成steam流类型的。比如‘</summary><published>2012-01-14T12:27:00Z</published><updated>2012-01-14T12:27:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/01/14/2322540.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/01/14/2322540.html"/><content type="html">&lt;p&gt;【已知】&lt;br /&gt;canvas提供了toDataURL的接口，可以方便的将canvas画布转化成base64编码的image。目前支持的最好的是png格式，jpeg格式的现代浏览器基本也支持，但是支持的不是很好。&lt;/p&gt;&lt;p&gt;【想要的】&lt;br /&gt;往往这么简单直接的接口通常都满足不了需求。我想要的不仅是简单的通过画布生成一个png，我不想新开一个tab，然后还要右键另存为...&lt;/p&gt;&lt;p&gt;我还需要更方便的自由的配置生成的图片的大小，比例等。&lt;/p&gt;&lt;p&gt;另外如果我还要别的图片格式，比如位图bmp，gif等怎么办...&lt;/p&gt;&lt;p&gt;【解决办法】&lt;br /&gt;a)想直接把图片生成后download到本地，其实办法也很简单。直接改图片的mimeType，强制改成steam流类型的。比如&amp;lsquo;image/octet-stream&amp;rsquo;，浏览器就会自动帮我们另存为..&amp;nbsp;&lt;/p&gt;&lt;p&gt;b)图片大小，及比例的可控倒也好办，我们新建一个我们想要大小的canvas，把之前的canvas画布重新按照所要的比例，及大小draw到新的canvas上，然后用新的canvas来toDataURL即可。&lt;/p&gt;&lt;p&gt;c)想要bmp位图会麻烦些... 没有直接的接口，需要我们自己来生成。生成图片的响应头和响应体有一定的规则，略显麻烦。不过还能接受。剩下的就是性能问题，按像素级别来操作，对于一个大图来说计算量很有压力。&lt;/p&gt;&lt;p&gt;【实现】&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span style="color: #008000;"&gt;/*&lt;/span&gt;&lt;span style="color: #008000;"&gt;*&lt;br /&gt; * covert canvas to image&lt;br /&gt; * and save the image file&lt;br /&gt; &lt;/span&gt;&lt;span style="color: #008000;"&gt;*/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; Canvas2Image = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; () {&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; check if support sth.&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; $support = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; () {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; canvas = document.createElement('canvas'),&lt;br /&gt;            ctx = canvas.getContext('2d');&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; {&lt;br /&gt;            canvas: !!ctx,&lt;br /&gt;            imageData: !!ctx.getImageData,&lt;br /&gt;            dataURL: !!canvas.toDataURL,&lt;br /&gt;            btoa: !!window.btoa&lt;br /&gt;        };&lt;br /&gt;    }();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; downloadMime = 'image/octet-stream';&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; scaleCanvas (canvas, width, height) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; w = canvas.width,&lt;br /&gt;            h = canvas.height;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (width == undefined) {&lt;br /&gt;            width = w;&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (height == undefined) {&lt;br /&gt;            height = h;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; retCanvas = document.createElement('canvas');&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; retCtx = retCanvas.getContext('2d');&lt;br /&gt;        retCanvas.width = width;&lt;br /&gt;        retCanvas.height = height;&lt;br /&gt;        retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; retCanvas;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; getDataURL (canvas, type, width, height) {&lt;br /&gt;        canvas = scaleCanvas(canvas, width, height);&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; canvas.toDataURL(type);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; saveFile (strData) {&lt;br /&gt;        document.location.href = strData;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; genImage(strData) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; img = document.createElement('img');&lt;br /&gt;        img.src = strData;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; img;&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; fixType (type) {&lt;br /&gt;        type = type.toLowerCase().replace(/jpg/i, 'jpeg');&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; r = type.match(/png|jpeg|bmp|gif/)[0];&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; 'image/' + r;&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; encodeData (data) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!window.btoa) { &lt;span style="color: #0000ff;"&gt;throw&lt;/span&gt; 'btoa undefined' }&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; str = '';&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt; data == 'string') {&lt;br /&gt;            str = data;&lt;br /&gt;        } &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt; {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; i = 0; i &amp;lt; data.length; i ++) {&lt;br /&gt;                str += String.fromCharCode(data[i]);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; btoa(str);&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; getImageData (canvas) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; w = canvas.width,&lt;br /&gt;            h = canvas.height;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; canvas.getContext('2d').getImageData(0, 0, w, h);&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; makeURI (strData, type) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; 'data:' + type + ';base64,' + strData;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000;"&gt;/*&lt;/span&gt;&lt;span style="color: #008000;"&gt;*&lt;br /&gt;     * create bitmap image&lt;br /&gt;     * 按照规则生成图片响应头和响应体&lt;br /&gt;     &lt;/span&gt;&lt;span style="color: #008000;"&gt;*/&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; genBitmapImage = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (data) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; imgHeader = [],&lt;br /&gt;            imgInfoHeader = [];&lt;br /&gt;        &lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; width = data.width,&lt;br /&gt;            height = data.height;&lt;br /&gt;&lt;br /&gt;        imgHeader.push(0x42); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 66 -&amp;gt; B&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgHeader.push(0x4d); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 77 -&amp;gt; M&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; fsize = width * height * 3 + 54; &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; header size:54 bytes&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgHeader.push(fsize % 256); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; r&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        fsize = Math.floor(fsize / 256);&lt;br /&gt;        imgHeader.push(fsize % 256); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; g&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        fsize = Math.floor(fsize / 256);&lt;br /&gt;        imgHeader.push(fsize % 256); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; b&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        fsize = Math.floor(fsize / 256);&lt;br /&gt;        imgHeader.push(fsize % 256); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; a&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;        imgHeader.push(0);&lt;br /&gt;        imgHeader.push(0);&lt;br /&gt;        imgHeader.push(0);&lt;br /&gt;        imgHeader.push(0);&lt;br /&gt;&lt;br /&gt;        imgHeader.push(54); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; offset -&amp;gt; 6&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgHeader.push(0);&lt;br /&gt;        imgHeader.push(0);&lt;br /&gt;        imgHeader.push(0);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; info header&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgInfoHeader.push(40); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; info header size&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgInfoHeader.push(0);&lt;br /&gt;        imgInfoHeader.push(0);&lt;br /&gt;        imgInfoHeader.push(0);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 横向info&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; _width = width;&lt;br /&gt;        imgInfoHeader.push(_width % 256);&lt;br /&gt;        _width = Math.floor(_width / 256);&lt;br /&gt;        imgInfoHeader.push(_width % 256);&lt;br /&gt;        _width = Math.floor(_width / 256);&lt;br /&gt;        imgInfoHeader.push(_width % 256);&lt;br /&gt;        _width = Math.floor(_width / 256);&lt;br /&gt;        imgInfoHeader.push(_width % 256);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 纵向info&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; _height = height;&lt;br /&gt;        imgInfoHeader.push(_height % 256);&lt;br /&gt;        _height = Math.floor(_height / 256);&lt;br /&gt;        imgInfoHeader.push(_height % 256);&lt;br /&gt;        _height = Math.floor(_height / 256);&lt;br /&gt;        imgInfoHeader.push(_height % 256);&lt;br /&gt;        _height = Math.floor(_height / 256);&lt;br /&gt;        imgInfoHeader.push(_height % 256);&lt;br /&gt;&lt;br /&gt;        imgInfoHeader.push(1);&lt;br /&gt;        imgInfoHeader.push(0);&lt;br /&gt;        imgInfoHeader.push(24); &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 24位bitmap&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgInfoHeader.push(0);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; no compression&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        imgInfoHeader.push(0);&lt;br /&gt;        imgInfoHeader.push(0);&lt;br /&gt;        imgInfoHeader.push(0);&lt;br /&gt;        imgInfoHeader.push(0);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; pixel data&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; dataSize = width * height * 3;&lt;br /&gt;        imgInfoHeader.push(dataSize % 256);&lt;br /&gt;        dataSize = Math.floor(dataSize / 256);&lt;br /&gt;        imgInfoHeader.push(dataSize % 256);&lt;br /&gt;        dataSize = Math.floor(dataSize / 256);&lt;br /&gt;        imgInfoHeader.push(dataSize % 256);&lt;br /&gt;        dataSize = Math.floor(dataSize / 256);&lt;br /&gt;        imgInfoHeader.push(dataSize % 256);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; blank space&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; i = 0; i &amp;lt; 16; i ++) {&lt;br /&gt;            imgInfoHeader.push(0);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; padding = (4 - ((width * 3) % 4)) % 4;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; imgData = data.data;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; strPixelData = '';&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; y = height;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;do&lt;/span&gt; {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; offsetY = width * (y - 1) * 4;&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; strPixelRow = '';&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; x = 0; x &amp;lt; width; x ++) {&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; offsetX = 4 * x;&lt;br /&gt;                strPixelRow += String.fromCharCode(imgData[offsetY + offsetX + 2]);&lt;br /&gt;                strPixelRow += String.fromCharCode(imgData[offsetY + offsetX + 1]);&lt;br /&gt;                strPixelRow += String.fromCharCode(imgData[offsetY + offsetX]);&lt;br /&gt;            }&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; n = 0; n &amp;lt; padding; n ++) {&lt;br /&gt;                strPixelRow += String.fromCharCode(0);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            strPixelData += strPixelRow;&lt;br /&gt;        } &lt;span style="color: #0000ff;"&gt;while&lt;/span&gt;(-- y);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; (encodeData(imgHeader.concat(imgInfoHeader)) + encodeData(strPixelData));&lt;br /&gt;&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000;"&gt;/*&lt;/span&gt;&lt;span style="color: #008000;"&gt;*&lt;br /&gt;     * saveAsImage&lt;br /&gt;     * @param canvasElement&lt;br /&gt;     * @param {String} image type&lt;br /&gt;     * @param {Number} [optional] png width&lt;br /&gt;     * @param {Number} [optional] png height&lt;br /&gt;     &lt;/span&gt;&lt;span style="color: #008000;"&gt;*/&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; saveAsImage = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height, type) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; ($support.canvas &amp;amp;&amp;amp; $support.dataURL) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (type == undefined) { type = 'png'; }&lt;br /&gt;            type = fixType(type);&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (/bmp/.test(type)) {&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; data = getImageData(scaleCanvas(canvas, width, height));&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; strData = genBitmapImage(data);&lt;br /&gt;                saveFile(makeURI(strData, downloadMime));&lt;br /&gt;            } &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt; {&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; strData = getDataURL(canvas, type, width, height);&lt;br /&gt;                saveFile(strData.replace(type, downloadMime));&lt;br /&gt;            }&lt;br /&gt;        &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; convertToImage = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height, type) {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; ($support.canvas &amp;amp;&amp;amp; $support.dataURL) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (type == undefined) { type = 'png'; }&lt;br /&gt;            type = fixType(type);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (/bmp/.test(type)) {&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; data = getImageData(scaleCanvas(canvas, width, height));&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; strData = genBitmapImage(data);&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; genImage(makeURI(strData, 'image/bmp'));&lt;br /&gt;            } &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt; {&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; strData = getDataURL(canvas, type, width, height);&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; genImage(strData);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; {&lt;br /&gt;        saveAsImage: saveAsImage,&lt;br /&gt;        saveAsPNG: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; saveAsImage(canvas, width, height, 'png');&lt;br /&gt;        },&lt;br /&gt;        saveAsJPEG: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; saveAsImage(canvas, width, height, 'jpeg');            &lt;br /&gt;        },&lt;br /&gt;        saveAsGIF: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; saveAsImage(canvas, width, height, 'gif')           &lt;br /&gt;        },&lt;br /&gt;        saveAsBMP: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; saveAsImage(canvas, width, height, 'bmp');           &lt;br /&gt;        },&lt;br /&gt;        &lt;br /&gt;        convertToImage: convertToImage,&lt;br /&gt;        convertToPNG: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; convertToImage(canvas, width, height, 'png');&lt;br /&gt;        },&lt;br /&gt;        convertToJPEG: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; convertToImage(canvas, width, height, 'jpeg');               &lt;br /&gt;        },&lt;br /&gt;        convertToGIF: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; convertToImage(canvas, width, height, 'gif');              &lt;br /&gt;        },&lt;br /&gt;        convertToBMP: &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (canvas, width, height) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; convertToImage(canvas, width, height, 'bmp');              &lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;}();&lt;/div&gt;&lt;p&gt;【Demo】&lt;br /&gt;&lt;a href="http://hongru.github.com/proj/canvas2image/index.html"&gt;http://hongru.github.com/proj/canvas2image/index.html&lt;/a&gt;&lt;br /&gt;可以试着在canvas上涂涂画画，然后保存看看。如果用bmp格式的话，需要支持 btoa 的base64编码，关于base64编码规则可看上一篇博文&lt;/p&gt;&lt;p&gt;【不完美的地方】&lt;br /&gt;1）jpeg接口本身就不完善，当canvas没有填充颜色或图片时，保存的jpeg由于是直接由png的alpha通道强制转换过来的，所以在png的透明部分在jpeg里面就是黑色的。&lt;/p&gt;&lt;p&gt;2）gif的限制太多。且可用性不大，有png就够了&lt;/p&gt;&lt;p&gt;3）bmp位图生成，计算量稍显大了。&lt;/p&gt;&lt;p&gt;4）由于是强制改mimeType来实现的自动下载，所以下载的时候文件类型不会自动识别。&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2322540.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/01/14/2322540.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html</id><title type="text">关于base64编码的原理及实现</title><summary type="text">我们的图片大部分都是可以转换成base64编码的data：image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外，大部分现代浏览器都已经支持原生的基于base64的encode和decode，例如btoa和atob。（将canvas画布保存成img并强制改变mimetype进行下载，会在下一篇记录）但是处于好奇心，还是驱使我去了解下base64编码的原理。以便也在不支持原生base64编码的ie下可以得以实现。【Base64】-base64的编码都是按字符串长度，以每3个8bit的字符为一组，-然后针对每组，首先获取每个字符的ASCII编码，-然后将ASCII编码转换成8b</summary><published>2012-01-14T03:47:00Z</published><updated>2012-01-14T03:47:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html"/><content type="html">&lt;p&gt;我们的图片大部分都是可以转换成base64编码的data：image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外，大部分现代浏览器都已经支持原生的基于base64的encode和decode，例如btoa和atob。（将canvas画布保存成img并强制改变mimetype进行下载，会在下一篇记录）&lt;/p&gt;&lt;p&gt;但是处于好奇心，还是驱使我去了解下base64编码的原理。以便也在不支持原生base64编码的ie下可以得以实现。&lt;/p&gt;&lt;p&gt;【Base64】&lt;br /&gt;-base64的编码都是按字符串长度，以每3个8bit的字符为一组，&lt;br /&gt;-然后针对每组，首先获取每个字符的ASCII编码，&lt;br /&gt;-然后将ASCII编码转换成8bit的二进制，得到一组3*8=24bit的字节&lt;br /&gt;-然后再将这24bit划分为4个6bit的字节，并在每个6bit的字节前面都填两个高位0，得到4个8bit的字节&lt;br /&gt;-然后将这4个8bit的字节转换成10进制，对照Base64编码表 （下表），得到对应编码后的字符。&lt;/p&gt;&lt;p&gt;（注：1. 要求被编码字符是8bit的，所以须在ASCII编码范围内，\u0000-\u00ff，中文就不行。&lt;br /&gt;　2. 如果被编码字符长度不是3的倍数的时候，则都用0代替，对应的输出字符为=）&lt;/p&gt;&lt;table class="base64table"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th colspan="11"&gt;&lt;strong&gt;Base64 编码表&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th scope="col"&gt;Value&lt;/th&gt;&lt;th scope="col"&gt;Char&lt;/th&gt;&lt;td rowspan="18"&gt;&amp;nbsp;&lt;/td&gt;&lt;th scope="col"&gt;Value&lt;/th&gt;&lt;th scope="col"&gt;Char&lt;/th&gt;&lt;td rowspan="18"&gt;&amp;nbsp;&lt;/td&gt;&lt;th scope="col"&gt;Value&lt;/th&gt;&lt;th scope="col"&gt;Char&lt;/th&gt;&lt;td rowspan="18"&gt;&amp;nbsp;&lt;/td&gt;&lt;th scope="col"&gt;Value&lt;/th&gt;&lt;th scope="col"&gt;Char&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;Q&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;g&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;w&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;R&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;h&lt;/td&gt;&lt;td&gt;49&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;C&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;S&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;i&lt;/td&gt;&lt;td&gt;50&lt;/td&gt;&lt;td&gt;y&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;D&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;T&lt;/td&gt;&lt;td&gt;35&lt;/td&gt;&lt;td&gt;j&lt;/td&gt;&lt;td&gt;51&lt;/td&gt;&lt;td&gt;z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;E&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;U&lt;/td&gt;&lt;td&gt;36&lt;/td&gt;&lt;td&gt;k&lt;/td&gt;&lt;td&gt;52&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;F&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;V&lt;/td&gt;&lt;td&gt;37&lt;/td&gt;&lt;td&gt;l&lt;/td&gt;&lt;td&gt;53&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;G&lt;/td&gt;&lt;td&gt;22&lt;/td&gt;&lt;td&gt;W&lt;/td&gt;&lt;td&gt;38&lt;/td&gt;&lt;td&gt;m&lt;/td&gt;&lt;td&gt;54&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;H&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;39&lt;/td&gt;&lt;td&gt;n&lt;/td&gt;&lt;td&gt;55&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;40&lt;/td&gt;&lt;td&gt;o&lt;/td&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;J&lt;/td&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;Z&lt;/td&gt;&lt;td&gt;41&lt;/td&gt;&lt;td&gt;p&lt;/td&gt;&lt;td&gt;57&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;K&lt;/td&gt;&lt;td&gt;26&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;q&lt;/td&gt;&lt;td&gt;58&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;L&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;b&lt;/td&gt;&lt;td&gt;43&lt;/td&gt;&lt;td&gt;r&lt;/td&gt;&lt;td&gt;59&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;M&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;44&lt;/td&gt;&lt;td&gt;s&lt;/td&gt;&lt;td&gt;60&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;td&gt;d&lt;/td&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt;t&lt;/td&gt;&lt;td&gt;61&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;e&lt;/td&gt;&lt;td&gt;46&lt;/td&gt;&lt;td&gt;u&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;td&gt;+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;P&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;f&lt;/td&gt;&lt;td&gt;47&lt;/td&gt;&lt;td&gt;v&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;td&gt;/&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;比如举下面2个例子：&lt;br /&gt;a) 字符长度为能被3整除时：比如&amp;ldquo;Tom&amp;rdquo; ：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;            T           o           m&lt;br /&gt;ASCII:      &lt;span style="color: #800080;"&gt;84&lt;/span&gt;          &lt;span style="color: #800080;"&gt;111&lt;/span&gt;         &lt;span style="color: #800080;"&gt;109&lt;/span&gt;&lt;br /&gt;8bit字节:   &lt;span style="color: #800080;"&gt;01010100&lt;/span&gt;    &lt;span style="color: #800080;"&gt;01101111&lt;/span&gt;    &lt;span style="color: #800080;"&gt;01101101&lt;/span&gt;&lt;br /&gt;6bit字节:     &lt;span style="color: #800080;"&gt;010101&lt;/span&gt;      &lt;span style="color: #800080;"&gt;000110&lt;/span&gt;      &lt;span style="color: #800080;"&gt;111101&lt;/span&gt;      &lt;span style="color: #800080;"&gt;101101&lt;/span&gt;&lt;br /&gt;十进制:     &lt;span style="color: #800080;"&gt;21&lt;/span&gt;          &lt;span style="color: #800080;"&gt;6&lt;/span&gt;           &lt;span style="color: #800080;"&gt;61&lt;/span&gt;          &lt;span style="color: #800080;"&gt;45&lt;/span&gt;&lt;br /&gt;对应编码:   V           G           &lt;span style="color: #800080;"&gt;9&lt;/span&gt;           t  &lt;/div&gt;&lt;p&gt;所以，btoa('Tom') = VG9t&lt;/p&gt;&lt;p&gt;b) 字符串长度不能被3整除时，比如&amp;ldquo;Lucy&amp;rdquo;：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;            L           u           c           y&lt;br /&gt;ASCII:      &lt;span style="color: #800080;"&gt;76&lt;/span&gt;          &lt;span style="color: #800080;"&gt;117&lt;/span&gt;         &lt;span style="color: #800080;"&gt;99&lt;/span&gt;          &lt;span style="color: #800080;"&gt;121&lt;/span&gt;&lt;br /&gt;8bit字节:   &lt;span style="color: #800080;"&gt;01001100&lt;/span&gt;    &lt;span style="color: #800080;"&gt;01110101&lt;/span&gt;    &lt;span style="color: #800080;"&gt;01100011&lt;/span&gt;    &lt;span style="color: #800080;"&gt;01111001&lt;/span&gt;      &lt;span style="color: #800080;"&gt;00000000&lt;/span&gt;    &lt;span style="color: #800080;"&gt;00000000&lt;/span&gt;&lt;br /&gt;6bit字节:     &lt;span style="color: #800080;"&gt;010011&lt;/span&gt;      &lt;span style="color: #800080;"&gt;000111&lt;/span&gt;      &lt;span style="color: #800080;"&gt;010101&lt;/span&gt;      &lt;span style="color: #800080;"&gt;100011&lt;/span&gt;      &lt;span style="color: #800080;"&gt;011110&lt;/span&gt;  &lt;span style="color: #800080;"&gt;010000&lt;/span&gt;  &lt;span style="color: #800080;"&gt;000000&lt;/span&gt;  &lt;span style="color: #800080;"&gt;000000&lt;/span&gt;&lt;br /&gt;十进制:     &lt;span style="color: #800080;"&gt;19&lt;/span&gt;          &lt;span style="color: #800080;"&gt;7&lt;/span&gt;           &lt;span style="color: #800080;"&gt;21&lt;/span&gt;          &lt;span style="color: #800080;"&gt;35&lt;/span&gt;             &lt;span style="color: #800080;"&gt;30&lt;/span&gt;      &lt;span style="color: #800080;"&gt;16&lt;/span&gt;      (异常) (异常)      &lt;br /&gt;对应编码:   T           H           V           j               e       Q       =       =&lt;/div&gt;&lt;p&gt;由于Lucy只有4个字母，所以按3个一组的话，第二组还有两个空位，所以需要用0来补齐。这里就需要注意，因为是需要补齐而出现的0，所以转化成十进制的时候就不能按常规用base64编码表来对应，所以不是a， 可以理解成为一种特殊的&amp;ldquo;异常&amp;rdquo;，编码应该对应&amp;ldquo;=&amp;rdquo;。&lt;/p&gt;&lt;p&gt;有了上面的理论，那我们实现一个base64编码就容易了。&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span style="color: #008000;"&gt;/*&lt;/span&gt;&lt;span style="color: #008000;"&gt;*&lt;br /&gt; * base64 encoding &amp;amp; decoding&lt;br /&gt; * for fixing browsers which don't support Base64 | btoa |atob&lt;br /&gt; &lt;/span&gt;&lt;span style="color: #008000;"&gt;*/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;(&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; (win, undefined) {&lt;br /&gt; &lt;br /&gt;     &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; Base64 = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; () {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; base64hash = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';&lt;br /&gt;        &lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; btoa method&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; _btoa (s) {&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (/([^\u0000-\u00ff])/.test(s)) {&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;throw&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Error('INVALID_CHARACTER_ERR');&lt;br /&gt;            }    &lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; i = 0,&lt;br /&gt;                prev,&lt;br /&gt;                ascii,&lt;br /&gt;                mod,&lt;br /&gt;                result = [];&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;while&lt;/span&gt; (i &amp;lt; s.length) {&lt;br /&gt;                ascii = s.charCodeAt(i);&lt;br /&gt;                mod = i % 3;&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;switch&lt;/span&gt;(mod) {&lt;br /&gt;                    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 第一个6位只需要让8位二进制右移两位&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 0:&lt;br /&gt;                        result.push(base64hash.charAt(ascii &amp;gt;&amp;gt; 2));&lt;br /&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;第二个6位 = 第一个8位的后两位 + 第二个8位的前4位&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 1:&lt;br /&gt;                        result.push(base64hash.charAt((prev &amp;amp; 3) &amp;lt;&amp;lt; 4 | (ascii &amp;gt;&amp;gt; 4)));&lt;br /&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;第三个6位 = 第二个8位的后4位 + 第三个8位的前2位&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;                    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;第4个6位 = 第三个8位的后6位&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 2:&lt;br /&gt;                        result.push(base64hash.charAt((prev &amp;amp; 0x0f) &amp;lt;&amp;lt; 2 | (ascii &amp;gt;&amp;gt; 6)));&lt;br /&gt;                        result.push(base64hash.charAt(ascii &amp;amp; 0x3f));&lt;br /&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                prev = ascii;&lt;br /&gt;                i ++;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 循环结束后看mod, 为0 证明需补3个6位，第一个为最后一个8位的最后两位后面补4个0。另外两个6位对应的是异常的&amp;ldquo;=&amp;rdquo;；&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; mod为1，证明还需补两个6位，一个是最后一个8位的后4位补两个0，另一个对应异常的&amp;ldquo;=&amp;rdquo;&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;(mod == 0) {&lt;br /&gt;                result.push(base64hash.charAt((prev &amp;amp; 3) &amp;lt;&amp;lt; 4));&lt;br /&gt;                result.push('==');&lt;br /&gt;            } &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (mod == 1) {&lt;br /&gt;                result.push(base64hash.charAt((prev &amp;amp; 0x0f) &amp;lt;&amp;lt; 2));&lt;br /&gt;                result.push('=');&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; result.join('');&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; atob method&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 逆转encode的思路即可&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; _atob (s) {&lt;br /&gt;            s = s.replace(/\s|=/g, '');&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; cur,&lt;br /&gt;                prev,&lt;br /&gt;                mod,&lt;br /&gt;                i = 0,&lt;br /&gt;                result = [];&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;while&lt;/span&gt; (i &amp;lt; s.length) {&lt;br /&gt;                cur = base64hash.indexOf(s.charAt(i));&lt;br /&gt;                mod = i % 4;&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: #0000ff;"&gt;switch&lt;/span&gt; (mod) {&lt;br /&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 0:&lt;br /&gt;                        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;TODO&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 1:&lt;br /&gt;                        result.push(String.fromCharCode(prev &amp;lt;&amp;lt; 2 | cur &amp;gt;&amp;gt; 4));&lt;br /&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 2:&lt;br /&gt;                        result.push(String.fromCharCode((prev &amp;amp; 0x0f) &amp;lt;&amp;lt; 4 | cur &amp;gt;&amp;gt; 2));&lt;br /&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                    &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; 3:&lt;br /&gt;                        result.push(String.fromCharCode((prev &amp;amp; 3) &amp;lt;&amp;lt; 6 | cur));&lt;br /&gt;                        &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;br /&gt;                        &lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                prev = cur;&lt;br /&gt;                i ++;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; result.join('');&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; {&lt;br /&gt;            btoa: _btoa,&lt;br /&gt;            atob: _atob,&lt;br /&gt;            encode: _btoa,&lt;br /&gt;            decode: _atob&lt;br /&gt;        };&lt;br /&gt;    }();&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!win.Base64) { win.Base64 = Base64 }&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!win.btoa) { win.btoa = Base64.btoa }&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!win.atob) { win.atob = Base64.atob }&lt;br /&gt;&lt;br /&gt; })(window)&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a class="btn" href="http://hongru.github.com/proj/base64/test.html" target="_blank"&gt;Base64 example&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2321397.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hongru/archive/2012/01/10/2318188.html</id><title type="text">追踪子弹-初中简单的物理和数学</title><summary type="text">飞行射击类游戏很常用的追踪子弹，或者塔防里面固定炮台打 怪物的时候，为了保证子弹不会打空，追踪是必要的。然而，这是极其简单的事情。在每一帧里判断当前子弹和目标位置的距离和方向，不断修正 速度方向即可。// this.x, this.y 表示当前子弹的位置// this.tar.x, this.tar.y 表示当前目标的位置var dis = Math.sqrt(Math.pow((this.tar.x-this.x), 2) + Math.pow((this.tar.y - this.y), 2));var angleX = (this.tar.x - this.x)/dis;var angl</summary><published>2012-01-10T07:33:00Z</published><updated>2012-01-10T07:33:00Z</updated><author><name>岑安</name><uri>http://www.cnblogs.com/hongru/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hongru/archive/2012/01/10/2318188.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hongru/archive/2012/01/10/2318188.html"/><content type="html">&lt;p&gt;飞行射击类游戏很常用的追踪子弹，或者塔防里面固定炮台打 怪物的时候，为了保证子弹不会打空，追踪是必要的。&lt;/p&gt;&lt;p&gt;然而，这是极其简单的事情。&lt;/p&gt;&lt;p&gt;在每一帧里判断当前子弹和目标位置的距离和方向，不断修正 速度方向即可。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; this.x, this.y 表示当前子弹的位置&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; this.tar.x, this.tar.y 表示当前目标的位置&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; dis = Math.sqrt(Math.pow((&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.tar.x-&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.x), 2) + Math.pow((&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.tar.y - &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.y), 2));&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; angleX = (&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.tar.x - &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.x)/dis;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; angleY = (&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.tar.y - &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.y)/dis;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.speedX = speed * angleX;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.speedY = speed * angleY;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.x += &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.speedX;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.y += &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.speedY;&lt;/div&gt;&lt;p&gt;算出速度方向，然后 速度*dt 叠加到 位移即可。&lt;/p&gt;&lt;p&gt;&lt;a style="display: inline-block; text-decoration: none; background-color: #018dd9; line-height: 20px; padding: 0 16px; color: #fff; -webkit-border-radius: 4px; -moz-border-radius: 4px;" href="http://hongru.github.com/test/bullet.html" target="_blank"&gt;Trace Bullet&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hongru/aggbug/2318188.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hongru/archive/2012/01/10/2318188.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
