圆形碰撞算法 封装类

昨天群里有朋友问到, 顺便发下. FTP连不上, 我直接贴代码得了.

FTP弄好了, Demo地址 http://www.vilic.info/demo/collide/

沙发同学Lin.x爆料地下的代码部分半角被替换成了全角, 会报错. 如果需要代码的话, 打开Demo自己另存为吧.

groinup.js

/*
圆形碰撞算法
www.vilic.info
by Vilic Vane
©2010 Groinup
*/
var groinup = new function () {

    var array = this.array = new function () {
        this.clear = function (aimArray) {
            aimArray.length = 0;
        };

        this.remove = function (item, arr) {
            var i;
            for (i in arr)
                if (arr[i] == item)
                    arr.splice(i, 1);
        };

        this.addUnique = function (item, arr) {
            var found = false;
            var i;
            for (i in arr)
                if (arr[i] == item) {
                    found = true;
                    break;
                }
            if (!found) arr.push(item);
        };
    };

    var object = this.object = new function () {
        this.copy = function (obj) {
            var newObj = {};
            var i;
            for (i in obj)
                newObj[i] = obj[i];
            return newObj;
        };
    } ();

    var math = this.math = new function () {
        var sqr = this.sqr = function (n) { return n * n; };
        var sqrt = this.sqrt = function (n) { return Math.sqrt(n); };

        var modulus = this.modulus = function (a1, a2) {
            var x1 = a1.x;
            var y1 = a1.y;
            var x2 = a2.x;
            var y2 = a2.y;

            return (x1 * x2 + y1 * y2) / sqrt((sqr(x1) + sqr(y1)) * (sqr(x2) + sqr(y2)));
        };

        var point = this.point = new function () {
            var getDistance = this.getDistance = function (pos1, pos2) {
                return sqrt(sqr(pos1.x – pos2.x) + sqr(pos1.y – pos2.y));
            };
        } ();

        var circle = this.circle = new function () {
            this.getDistance = function (pos1, pos2, r1, r2) { return point.getDistance(pos1, pos2) – r1 – r2; };
        };

        var Position = this.Position = function (x, y) {
            this.x = x;
            this.y = y;
        };

    } ();

    var physics = this.physics = new function () {
        var sqr = math.sqr;
        var sqrt = math.sqrt;

        var getMovingDistance = this.getMovingDistance = function (v0, a, t) {
            return v0 * t + 0.5 * a * math.sqr(t);
        };

        var Velocity = this.Velocity = function (vx, vy) {
            this.x = vx;
            this.y = vy;
        };

        var Acceleration = this.Acceleration = function (ax, ay) {
            this.x = ax;
            this.y = ay;
        };
    } ();

    var collide = this.collide = new function () {

        //函数引用
        var sqr = math.sqr;
        var sqrt = math.sqrt;
        var modulus = math.modulus;

        var Circle = this.Circle = function (m, r, p, v, a, goFunc) {
            var _this = this;
            this.m = m;
            this.r = r;
            this.p = p;
            this.v = v;

            this.a = a || new physics.Acceleration(0, 0);
            a = this.a;

            var acTime = 0;
            this.resetAcTime = function () { acTime = 0; };
            this.onCollide = function () { };

            this.collideCallBack = function (p, c) {
                _this.onCollide(p, c);
            };

            this.onVelocityReverse = function () { };

            this.onSpeedUp = function () { };

            this._isOnSurface = false;

            if (goFunc)
                this.go = function (t) {
                    acTime += t;
                    if (goFunc(acTime) == false) go(t);
                };
            else
                this.go = go;

            function go(t) {
                var vx = v.x + a.x * t;
                var vy = v.y + a.y * t;

                var sqrVxy0 = sqr(v.x) + sqr(v.y);
                var sqrVxy = sqr(vx) + sqr(vy);

                p.x += (vx + v.x) / 2 * t;
                p.y += (vy + v.y) / 2 * t;

                v.x = vx;
                v.y = vy;

                if (sqrVxy >= sqrVxy0) _this.onSpeedUp();
            }
        };

        var Surface = this.Surface = function () {
            var kmax = sqrt(Number.MAX_VALUE) – 1;
            var circles = [];
            var isSearching = false;

            this.add = function (circle) {
                circle._isOnSurface = true;
                circles.push(circle);
            };

            this.remove = function (circle) {
                circle._isOnSurface = false;
                if (!isSearching) array.remove(circle, circles);
            };

            this.go = function (time, step) {
                collideSearch(time, step);
            };

            function collideSearch(time, step) {
                isSearching = true;

                var sAmt = Math.floor(time / step);
                for (var i = 0; i < sAmt; i++)
                    search();

                isSearching = false;

                function search() {
                    var i;

                    var removeList = [];

                    for (i in circles)
                        circles[i].go(step);

                    var getDis = math.circle.getDistance;
                    var len = circles.length;
                    for (var j = 0; j < len; j++) {
                        var c1 = circles[j];

                        //判断元素是否已经移除
                        if (!c1._isOnSurface) {
                            removeList.push(c1);
                            continue;
                        }
                        for (var k = j + 1; k < len; k++) {
                            var c2 = circles[k];

                            //判断元素是否已经移除
                            if (!c1._isOnSurface) {
                                removeList.push(c1);
                                break;
                            }
                            else
                                if (!c2._isOnSurface) {
                                    array.addUnique(c2, removeList);
                                    continue;
                                }

                            var d = getDis(c1.p, c2.p, c1.r, c2.r);
                            if (getDis(c1.p, c2.p, c1.r, c2.r) <= 0)
                                dealCollide(c1, c2);
                        }
                    }

                    //移除在搜索过程中被搁置的需要移除的项
                    for (i in removeList)
                        array.remove(removeList[i], circles);
                }

                function dealCollide(c1, c2) {
                    //获取相关数据
                    var p1 = c1.p, p2 = c2.p;
                    var v1 = c1.v, v2 = c2.v;
                    var m1 = c1.m, m2 = c2.m;
                    var x1 = p1.x, x2 = p2.x, y1 = p1.y, y2 = p2.y;
                    var vx1 = v1.x, vx2 = v2.x, vy1 = v1.y, vy2 = v2.y;

                    var a11 = { x: vx1, y: vy1 };
                    var a12 = { x: x2 – x1, y: y2 – y1 };
                    var a21 = { x: vx2, y: vy2 };
                    var a22 = { x: x1 – x2, y: y1 – y2 };

                    //计算两个圆圆心连线的斜率
                    var k;
                    var dx = x1 – x2;

                    //判断斜率是否存在
                    if (dx) k = (y1 – y2) / dx;
                    //如果不存在,则去尽可能大的值
                    else k = kmax;

                    //**速度分解

                    //*第一个圆
                    //圆心连线方向的分速度
                    var vox1 = apart(vx1, vy1);
                    var voy1 = vox1 * k;

                    //与连线方向垂直的分速度
                    var vpx1 = vx1 – vox1;
                    var vpy1 = vy1 – voy1;

                    //*第二个圆
                    //圆心连线方向的分速度
                    var vox2 = apart(vx2, vy2);
                    var voy2 = vox2 * k;

                    //与连线方向垂直的分速度
                    var vpx2 = vx2 – vox2;
                    var vpy2 = vy2 – voy2;

                    //碰撞后与连线方向垂直的分速度不变,连线方向的分速度变化
                    var vox1a = calculateV(m1, m2, vox1, vox2);
                    var vox2a = calculateV(m2, m1, vox2, vox1);

                    var voy1a = calculateV(m1, m2, voy1, voy2);
                    var voy2a = calculateV(m2, m1, voy2, voy1);

                    //重新复合速度
                    v1.x = vox1a + vpx1;
                    v1.y = voy1a + vpy1;
                    v2.x = vox2a + vpx2;
                    v2.y = voy2a + vpy2;

                    //告知动量变化量
                    var dp1 = sqrt(sqr(vox1a – vox1) + sqr(voy1a – voy1)) * m1;
                    var dp2 = sqrt(sqr(vox2a – vox2) + sqr(voy2a – voy2)) * m2;

                    c1.collideCallBack(dp1, c2);
                    c2.collideCallBack(dp2, c1);

                    //分解出与圆心连线方向的分速度的x方向速度
                    function apart(vx, vy) {
                        return (k * vy + vx) / (sqr(k) + 1);
                    }

                    //根据动量和能量守恒计算速度变化
                    function calculateV(m1, m2, v1, v2) {
                        return ((m1 – m2) * v1 + 2 * m2 * v2) / (m1 + m2);
                    }
                }
            }
        };
    } ();
}

index.html

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>

<html xmlns=”http://www.w3.org/1999/xhtml“>
<head>
    <title>碰撞类</title>
    <style type=”text/css”>
        #c1 { position: absolute; margin: -10px; width: 20px; height: 20px; background-color: Red; -webkit-border-radius: 10px; -moz-border-radius: 10px; -o-border-radius: 10px; }
        #c2 { position: absolute; margin: -15px; width: 30px; height: 30px; background-color: Blue; -webkit-border-radius: 15px; -moz-border-radius: 15px; -o-border-radius: 15px; }
        #c3 { position: absolute; margin: -6px; width: 12px; height: 12px; background-color: Green; -webkit-border-radius: 6px; -moz-border-radius: 6px; -o-border-radius: 6px; }
    </style>
    <script src=”groinup.js” type=”text/javascript”></script>
</head>
<body>
    <div id=”c1″></div>
    <div id=”c2″></div>
    <div id=”c3″></div>
</body>
<script type=”text/javascript”>
    //使用举例

    //获取元素
    var c1 = document.getElementById(‘c1’);
    var c2 = document.getElementById(‘c2’);
    var c3 = document.getElementById(‘c3’);

    //容器
    var surface = new groinup.collide.Surface();

    //定义碰撞的圆形, 参数依次为 质量, 半径, 位置, 速度, 加速度, 搜索过程中的回调函数
    var ci1 = new groinup.collide.Circle(10, 10, new groinup.math.Position(210, 200), new groinup.physics.Velocity(40, 10), 0, null);
    var ci2 = new groinup.collide.Circle(5, 15, new groinup.math.Position(400, 220), new groinup.physics.Velocity(-50, -10), 0, null);
    var ci3 = new groinup.collide.Circle(2, 6, new groinup.math.Position(100, 320), new groinup.physics.Velocity(60, -10), 0, null);

    //向容器中添加圆形
    surface.add(ci1);
    surface.add(ci2);
    surface.add(ci3);

    //控制运动
    var i = 0;
    var interval = setInterval(setPos, 30);

    function setPos() {
        if (i++ > 100) clearInterval(interval);

        //移动, 参数分别为移动的时间和单次搜索的时间(单次搜索的时间越短, 运动越精确)
        surface.go(0.05, 0.005);

        //设置位置
        sP(c1, ci1.p.x, ci1.p.y);
        sP(c2, ci2.p.x, ci2.p.y);
        sP(c3, ci3.p.x, ci3.p.y);

        function sP(o, x, y) {
            o.style.left = x + ‘px’;
            o.style.top = y + ‘px’;
        }
    }

</script>
</html>

CSS3+PNG解决GIF动画无半透明的问题

因为GIF不支持半透明, 在使用时, 如果背景固定还好说, 如果背景是变化的, 那么透明的GIF图片边缘会有很明显的粗糙锯齿.

最近做JS游戏, 因为只针对设备开发的, iPhone和iPod里的Mobile Safari支持部分CSS3, 所以就想出了这个解决办法. 虽然比较小众, 但是对于我来说, 还是有价值的.

当然, 因为PNG图一般都偏大, 所以这个方案不适用于帧数很多的动画.

思路是很简单的, 通过改变背景的位置(背景定位)来实现.

首先我们需要把动画中出现的每一帧连成一个长条, 就像电影一样, 然后通过CSS3, 使它们逐帧显示出来. 例子如下(以Webkit为例).

@-webkit-keyframes ‘somename’ {
    0% { background-position: 0px; }
    49.9999% { background-position: 0px; }
    50% { background-position: -100px; }
    100% { background-position: -100px; }
}

#someid {
    background-image: url(somepic.png);
    -webkit-animation-name: somename;
    -webkit-animation-duration: 0.2s;
    -webkit-animation-iteration-count: infinite;
}

属性的意思我就不解释了, 英文, 很明了的. 因为CSS3动画本身貌似不是为逐帧的动画而生, 所以, 请注意49.9999%和50%这个百分比, 因为相隔很近, 可以让背景一次性从一个位置, 跳动到另一个位置, 这样以来, 就模拟了帧的变化.

如果想了解更多的CSS3动画技术, 请Google之. 其实我也不是很懂. 所以说不定会有更加直接的办法呢?

通过重载原有函数实现取消所有Interval和Timeout的功能

今天凌晨写东西的时候遇到的问题, 因为用到setInterval和setTimeout的地方比较多, 懒得改, 便想到了通过重载来实现. 之前只用考虑Safari, 移植到IE之后还有些不一样.

测试了IE8和FF, 都正常. 其他的浏览器应该也没什么问题.

/*
通过重载原有函数实现取消所有Interval和Timeout的功能
请将这段代码放到脚本中靠前的位置, 并确保它在所有setInterval和setTimeout函数被使用之前被解释运行
Vilic Vane (www.vilic.info)
©2010 Groinup
*/
new function () {
    //备份原有函数
    var _setInterval = window.setInterval;
    var _setTimeout = window.setTimeout;
    var _clearInterval = window.clearInterval;
    var _clearTimeout = window.clearTimeout;

    //私有计时器列表
    var intervalList = [];
    var timeoutList = [];

    //重载setInterval
    window.setInterval = function (code, delay) {
        var id = _setInterval(code, delay);
        intervalList.push(id);
        return id;
    };

    //重载setTimeout
    window.setTimeout = function (code, delay) {
        var id = _setTimeout(code, delay);
        timeoutList.push(id);
        return id;
    };

    //重载clearInterval
    window.clearInterval = function (intervalID) {
        _clearInterval(intervalID);
        remove(intervalID, intervalList);
    };

    //重载setTimeout
    window.clearTimeout = function (timeoutID) {
        _clearTimeout(timeoutID);
        remove(timeoutID, timeoutList);
    };

    //添加clearAllInterval, 用于取消所有Interval
    window.clearAllInterval = function () {
        for (var i = 0; i < intervalList.length; i++)
            _clearInterval(intervalList[i]);
        intervalList.length = 0;
    };

    //添加clearAllTimeout, 用于取消所有Timeout
    window.clearAllTimeout = function () {
        for (var i = 0; i < timeoutList.length; i++)
            _clearTimeout(timeoutList[i]);
        timeoutList.length = 0;
    };

    //私有数组操作函数
    function remove(item, from) {
        for (var i = 0; i < from.length; i++)
            if (from[i] == item) {
                from.splice(i, 1);
                break;
            }
    }
} ();

为什么花80万也有人愿意移民?

转自 Ruby’s Louvre, 原文链接 http://www.cnblogs.com/rubylouvre/archive/2010/07/04/1770834.html

最近拜访一位美国著名媒体的专栏作家,她1985年就来到中国,中文很好。

因为她的作家身份,提问似乎变成了双向。她问:“你能告诉我,为什么最近我总是收到很多让我移民的短信?”

短信道:“移民澳大利亚40万人民币,移民美国80万人民币。”她逐条给我看短信,可移民国度还包括加拿大、新西兰甚至新加坡。

她问:“中国不是越来越富强吗?个人不是越来越有钱吗?为什么这么多中国人有钱了还要移民外国?”

她说,那天在大街上看到一条广告,上书几个大字:“房子这么贵?还不如移民呢!”

我终于忍不住大笑,这条不够狠,我看到一条间接广告,锵锵泰斗“窦文涛”说的:“中国有风险,投胎需谨慎”。

即使是中国通,她小心记下“DOU文涛”三个字以后,却被“投胎”这两个字难倒了。她问,什么叫“投胎”?我想了很久,回答她说:”中国人,或者说佛家相信轮回。因此乐观的中国人相信,你死后会再投入人世,变成小孩,再出生。这里是更乐观的人相信,你不但会再生,而且还可以选择自己投胎的国度。”

那么上句话的意思可以是:如果你可以替孩子选择出生地的话,中国不是一个好选择。这算是给她的答案,为什么中国人会这么热衷于移民。

当然,作为一个未必真正生活在中国人群里的“中国通”,她依然不知道为什么移民要那么贵。我说,我正好有一个朋友在移民公司工作,一次我拿到了一在她那里看到一份移民美国的“收益”清单,如果按这份清单的话,那么80万的费用似乎是不贵的(他们公司好像只收20万费用)。

以下是清单部分内容:

*孩子出生。在美出生的孩子就是美国公民。注意你在北京超生一个孩子要罚款24万人民币,而在美国你可以多生几个,不用付罚款。(在加拿大每个孩子每个月政府还支付还有牛奶金呢)

*中小学学费。18岁大学前美国全部中小学都是义务教育。目前北京、上海、广州三地机构办理中小学生赴美的学费每年都在3万美元以上(接近21万人民币)。3万×3年=9万 约等于超过60万人民币。

*大学学费。上大学本地学生的学费每年约3千多美金,而外籍学生要交2万多美金。每年节省1.7万×4年=6.8万 约等于50万人民币

*学生贷款。18岁以上的绿卡持有者可以在拿到绿卡当年申请各种类型的学生贷款,找到工作以后可以分年还清,通常只要拿工资的10%-20%还款。美国和加拿大的学生贷款系统非常方便简单,很多学生都靠自己贷款和打工读完大学。

*政府帮助找工作。美国政府在各地设有专门机构免费为国人找工作,绿卡持有者只要到这些部门登记技能、专长,这些部门就会为登记这免费找工作及提供工作的各种政府资讯。同时,这些部门每年还有各项基金帮助培训待业者,比如电脑、英文。

*周游世界。绿卡持有者在绝大多数国家有免签的优势。当然去朝鲜和古巴两国除外。

*回国投资。美国绿卡持有者被视为华侨,回国投资被视为外资或合资,并享受很多优惠政策。

*退休福利。绿卡持有人在一生累积十年交税记录后(底线为每月50美元的税金)可在退休后(通常为65岁)终生领取美国政府的退休金(每月700-1200美元)。

*失业救济。在有正常收入并交税不少于6个月以后失业,到美国相关部门报失业并办理相关手续就可以按月领取政府救济金(每月600-1200美金不等)。

*事故时的人道待遇。在美国发生事故(车祸,怪病)一切以救人为第一准则。即使你表明没有钱支付手术费,医院也会不懈余力地抢救……

我不是帮移民公司做广告,因为大家每天都在手机收到小广告。以上的信息确实是移民公司所列,而且事实情况大致如此,至少我知道在加拿大的社会福利比美国还要好。比如医疗保险,在有的阿尔伯塔省是全部免费,在卑诗省对穷人医保还有特别的优惠。

我只是想列出来,有一些对社会安定起决定因素的社会福利待遇,西方国家有,而我们没有,这样移民公司和移民生意才有市场。

老实说,假如移民美国费用是80万,你多生一个小孩,让他读3年美国高中,4年美国大学基本省出的钱已经超过80万人民币了—条件是假如你不想让孩子和很多学生一起高考,并且大学毕业以后打破头的找工作的话。

这些费用还只是算到了孩子大学毕业,步入社会的社会的医疗保险、失业保险、退休金等等还没有计算。

如果有能力在美国大城市买一幢房子,基本上今天你省的就更多了,就拿通州2万的房价来说吧,你在美国波士顿的郊区绝对可以买到每平方尺单价更好更便宜的房子。

除了这些可以计算的显性数字,干净的空气、清澈的蓝天、纯净的饮用水,孩子们可以活动的大片绿地。更好的市政措施、更好的交通管理等等,还有人们相对平静的心态这些都是数字和金钱无法计算的。

如果你你要问我为什么国人选择移民,我首先会回答因为他们不想自己将来的小孩喝三鹿奶粉,吃食物中的苏丹红、地沟油和各种肉类中的超标激素……好不容易挤进大学校门,毕业了以后却要排着队在富士康门口找工作。还有越来越大的基尼系数和贫富分化带来的社会不稳定性……这些也是金钱无法计算的。

“中国有风险,投胎需谨慎”这样的口号,当然只能是对自己国家最有深切体验的中国人才能总结出来的。等每个北京老外每天都收到移民加拿大、美国、澳大利亚的小广告的时候,我想这也算是黑色幽默了。

我记得当年在新东方学习英语的时候,老师曾经说过,俞敏洪为美国一飞机一飞机的输送了GRE考了高分的最好的中国学生。这是多么大的壮举啊。

如果移民公司再一飞机一飞机的输送中国掏得起移民费用的社会精英或者贪污的官员,这何尝不是壮举。

当然,如此诋毁祖国母亲的移民广告,肯定会有许多爱国者愤愤不平,但我还是觉得,作为一个爱国者,对这种广告,最好的办法就是广泛地传播它,因为,如此一来,最后留在这个国家的人,都会是纯粹的爱国者了。

离线Web应用-HTML5 Cache Manifest试验

原文地址 http://www.bennadel.com/blog/1944-Experimenting-With-HTML5-s-Cache-Manifest-For-Offline-Web-Applications.htm
译者 Vilic Vane (www.vilic.info)

(译者注:第一次翻译东西,限于英文水平,不足之处请见谅.另外,原文作者使用的是ColdFusion,代码中有些内容并非浏览器最终获得的内容,请注意区别.然后空了会争取翻一下文件内的注释.)

之前我试过通过Safari的SQLite支持来创建客户端的数据库,觉得还应该试试通过HTML5的”Cache Manifest(缓存清单)”来创建离线的Web应用程序.Cache Manifest是一个列出了应用程序所需的所有需要被缓存的资源的文本文件,以便在离线的时候程序一样可以使用.在Cache Manifest中列出的文件会被储存在”Application Cache(应用程序缓存)”里.Application Cache就像浏览器普通缓存,但是却更加强大.只要资源被储存在了Application Cache里,除非相应缓存被清除或者Cache Manifest作废,浏览器永远不会再次从网络请求这个资源.

当我刚开始使用Cache Manifest的时候,我对”离线”方面的兴趣比不上对beasty(译者注:抱歉…没查到这个单词)缓存机制方面的兴趣.只要浏览器发现了Cache Manifest文件,并且得到了你对于它创建离线缓存的许可,浏览器接下来便会在后台开始下载所有需要缓存的资源.如此一来,你只需要点击主页(只是举个例子,不必真的这么做)就可以最终让整个应用程序被缓存下来.并且,一旦应用程序的资源被缓存,对于这个程序的操作,更多的便成了本地的操作,而不是远程的.

为了进行这个HTML5的Cache Manifest试验,我创建了一个非常简单的照片列表页面,并在其中列出了一些链接,以便于查看一些较高分辨率的图片.每一个”查看”页面都是会通过照片的ID动态加载相应图片的ColdFusion页面.列表页面的HTML内容非常简单:

<!---
Use an HTML5 doc-type. This is required for some browsers to
work properly with the offline-cache.
--->
<!DOCTYPE HTML>

<!---
Add a file to the cache manifest. NOTE: Any file that calls
a cache manifest file will be automatically cached, even if
it is in the WHITELIST portion of the cache manifest.
--->
<html manifest="./cache_manifest.cfm">
<head>
<title>Cache Manifest For Offline Pages</title>

<!---
These are the two graphics (logo and startup screen) use
for "app mode" on the iPhone (when the web page is put on
the main screen as its own logo).

NOTE: This part has nothing to do with the cache manifest
stuff - this is strictly related to runnin a website in
"App Mode" on an iPhone. Feel free to remove this entirely
from the example.
--->
<link rel="apple-touch-icon" href="icon.png"/>
<link rel="apple-touch-startup-image" href="home.png" />
</head>
<body>

<h1>
Cache Manifest For Offline Pages
</h1>

<h3>
Pictures of Alisha Morrow
</h3>

<ul>
<cfoutput>

<!---
Notice that we are dynamically generating this
page. It will only be generated on the first
request. After that, this page will be coming out
of the cache.
--->
<cfloop
index="pictureIndex"
from="1"
to="5"
step="1">

<li>
<a href="view.cfm?id=#pictureIndex#"
>View Image #pictureIndex#</a>
</li>

</cfloop>

</cfoutput>
</ul>

<p>
Now:
<cfoutput>
#timeFormat( now() , "hh:mm:ss TT" )#
</cfoutput>
</p>

</body>
</html>

当我们使用Cache Manifest的时候,有两个地方非常重要,首先,因为Cache Manifest是HTML5的一部分,我们需要使用HTML5 doctype:

<!DOCTYPE HTML>

虽然不是所有浏览器都要求这样做(我猜想的话),但为了跨浏览器的兼容性,这样做更好.然后,或许更加重要的东西是我们需要添加一个链接来指向Cache Manifest文件.这条链接是定义在HTML标签里的:

<html manifest="./cache_manifest.cfm">

cache_manifest.cfm文件就是列出了应用程序中所有需要缓存的资源的文件:

Cache Manifest (cache_manifest.cfm)

<!---
Define the Cache Manifest content. I'm doing it this way since
the "CACHE MANIFEST" line needs to be the first line in the file
and storing it in a buffer allows us to TRIM later without having
ugly line breaks.
--->
<cfsavecontent variable="cacheManifest">

<!---
NOTE: Cache Manifest must be the very first thing in this
manifest file.
--->
CACHE MANIFEST

<!---
When a cache manifest is reviewed by the browser, it uses a
complete byte-wise comparison. As such, we can use COMMENTS
to defunk a previously used cache manifest. In this way, we
can use a version-comment to indicate change even when the
file list has not changed.

NOTE: If ANY part of this file is different from the previous
cache manifest, ALL of the files are re-downloaded.
--->
# Cache Manifest Version: 1.3

<!---
Let's list the file that get cached. The URLs to these files
are relative to the cache manifest file (or absolute).
--->
# Core files.
./index.cfm

# iPhone App files.
./icon.png
./home.png

# View Pages. Notice that these can be dynamic pages as long
# as they are valid URLs.
./view.cfm?id=1
./view.cfm?id=2
./view.cfm?id=3
./view.cfm?id=4
./view.cfm?id=5

# Images that get viewed.
./images/1.jpg
./images/2.jpg
./images/3.jpg
./images/4.jpg
./images/5.jpg

</cfsavecontent>


<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->


<!---
Let's reset the output and set the appropriate content type.
It is critical that the manifest file be served up as a type
"text/cache-manifest" mime-type.

NOTE: We need to be careful about the whitespace here since
the very first line of the file must contain the phrase,
"CACHE MANIFEST". As such, we must TRIM() the content.
--->
<cfcontent
type="text/cache-manifest"
variable="#toBinary( toBase64( trim( cacheManifest ) ) )#"
/>

在使用Cache Manifest的时候,有一些要点需要明白.第一,”CACHE MANIFEST”这行文字,必须出现在Cache Manifest文件的开头.第二,文件的类型必须被定义为MIME类型,”text/cache-manifest”.第三,整个Cache Manifest文件是通过每个字节来区分版本的.这第三点的意思就是说,Cache Manifest文件中任何位置的变动,包括注释内容(#),都会使原来的Cache Manifest作废.此时,整个列表中需要被缓存的资源都会被重新下载.

就像在CSS文件中一样,Cache Manifest中列出的资源路径既可以是完整的,也可以是相对这个Cache Manifest文件的.因为我们缓存的是动态页面,这些动态页面按说应该独立地列在Cache Manifest文件里(译者注:可能意思是,举个例子,有时动态页面需要更新缓存,而图片不变,这是如果动态页面的Cache Manifest文件和图片的是一个文件,便会使整个列表,也即包括图片被重新下载).还有其他的一些办法来做到这点,但是仅对于我的试验来说,这是最简单的做法.

列表中的”查看”页面除了动态显示与获得的ID相应的图片以外,什么也没做.

view.cfm

<!--- Param the image ID that we are going to view. --->
<cfparam name="url.id" type="numeric" />


<!---
Use an HTML5 doc-type. This is required for some browsers to
work properly with the offline-cache.
--->
<!DOCTYPE HTML>

<!---
Add a file to the cache manifest. NOTE: Any file that calls
a cache manifest file will be automatically cached, even if
it is in the WHITELIST portion of the cache manifest.
--->
<html manifest="./cache_manifest.cfm">
<head>
<title>View Image</title>
</head>
<body>

<h1>
View Image
</h1>

<p>
&laquo; <a href="./index.cfm">Back to List</a>
</p>

<p>
<cfoutput>
<img src="./images/#url.id#.jpg" width="300" />
</cfoutput>
</p>

</body>
</html>

我在查看页面中也加入了Cache Manifest的链接;虽然我相信它并没有被浏览器请求.只要用户首先打开的是列表页面,Cache Manifest文件便会被下载,并且查看页面应该被自动缓存了.我在这里也加上这个,只是单纯地为了保险(译者注:原文为for good measure,不知道能不能我这样翻译).

在列表页面中,你可能已经注意到了一些奇怪的LINK标签,这些LINK标签是为苹果的iPhone设计的Web应用在”App Mode(应用程序模式)”用来定义其图标和启动界面图形的.”App Mode”是当页面URL被添加到了iPhone的Home Screen(主页面)时实现的全屏的模式.这些Link标签与Cache Manifest没有关系;但是,它们能说明我进行HTML5在这一块的探索的原因-为iPhone创建可以在本地运行(译者注:原文为native-esque,没查到)的Web应用程序.

Cache Manifest似乎是HTML5的一个非常酷特征,这些例子能够在Firefox,Safari和我的iPhone上运行(没有尝试过其他浏览器).我并没有在桌面Safari的研究上下很多的功夫,但是或许它能在Firefox的离线模式和iPhone的飞行模式下使用已经非常惊人了.就像我之前所说的,我最终想要进行这个研究,是为了让我的Dig Deep Fitness web application在iPhone上像本地应用程序那样运行.

注意:当我刚开始尝试使用Cache Manifest时,为了让离线缓存被识别,费了很大的力气.两天的时间我一直在检查我的拼写和无效链接.后来我重新启动了Firefox,终于得到了一点安慰(译者注:原文blam,没查到,Google了下,貌似有类似的意思)-它开始工作了.所以,如果第一次没有作用,你或许需要重新启动一下浏览器.

QQ表情包(*.eip)导入指南

普通访客请无视此文,特殊访客请无视此句.

其实貌似可以直接双击导入,但是刚刚我导入失败,如果你也失败的话,可以试试这样的方法.

首先打开一个聊天窗口,依次点击”选择表情”>”表情管理”.

接着选择”导入和导出”>”导入表情”,之后再弹出的文件选择窗口中选中表情文件即可.

导入完成后,即可在表情列表/选项卡中看到导入的表情了.

JS混淆软件推荐

这次做的这个游戏,需要将代码混淆,到网上搜了一些混淆工具,有在线的,也有桌面的,不过多数都会害死我的JS,可能是因为我喜欢用一些特殊(其实也比较常见)的写法,而这些工具都还不够完善。

后来找到了JSA。JSA是jar应用,所以需要JAVA的环境。使用非常简单,将脚本复制到JSA的文本框,在顶部菜单栏选择“操作”>“压缩”,在弹出的窗口中设置好,确定即可。

下载地址:http://sourceforge.net/projects/jsintegration/files/

注意下载的时候选择All Files下方tools中的JSA而不是上面的JSI。

JS圆形碰撞算法的探讨及实现

也是做同一个东西,碰撞是又一个关键,受限于JS的执行速度,我们必须考虑CPU的承受能力。因为有这个想法,我一开始的思路是通过数学计算得到一个式子,用于求碰撞的位置,但是,当我假设运动是匀速直线时,得出来的是一个以时间t为未知数的一元四次方程,能解,但是很长很悲壮。更不要说匀加速运动了。

现在连续的数学方法走不通,就只有搜索,因为最终会表现到网页上物体位置的变化,而这个过程会消耗大量的资源,如果让搜索密度较大,并且每次搜索都向页面反馈的话,恐怕CPU承受不了,如果降低搜索密度,又会降低搜索的精确程度,最终影响碰撞的效果。于是,将其折中,搜索一定次数后,向页面反馈一次,这样一方面提高了精确程度,一方面也减小了CPU资源消耗。

这样一来,在哪里碰撞就变得很容易得到了,接下来的问题就是碰撞后物体的方向,和速度的大小。高中物理经常遇到两个物体对撞的问题,其实这里也大同小异,不过首先要做的,是将速度分解为沿两圆圆心连线方向和与该连线方向垂直的分速度,根据两分速度(矢量)相加等于合速度,还有到角公式tanα = (k1 – k2) / (1 + k1k2),可以算得分速度。

这时候,需要进行处理的就是圆心连线方向的分速度了,根据物理的动量守恒和能量守恒,可以算得碰撞之后的相应速度,最后,和与圆心连线垂直的分速度相加,即可得到碰撞后的速度(矢量)了。

上JS,这个是我用来计算碰撞后的速度的,里面有些东西没有上下代码,所以不能运行,仅供参考。

//获取相关数据
var p1 = c1.p, p2 = c2.p; //获取位置(坐标)
var v1 = c1.v, v2 = c2.v; //获取速度(矢量)
var m1 = c1.m, m2 = c2.m; //获取质量
var x1 = p1.x, x2 = p2.x, y1 = p1.y, y2 = p2.y; //位置坐标
var vx1 = v1.x, vx2 = v2.x, vy1 = v1.y, vy2 = v2.y; //速度

//计算两个圆圆心连线的斜率
var k;
var dx = x1 – x2;

//判断斜率是否存在
if (dx) k = (y1 – y2) / dx;
//如果不存在,则去尽可能大的值
else k = kmax;

//**速度分解

//*第一个圆
//圆心连线方向的分速度
var vox1 = apart(vx1, vy1);
var voy1 = vox1 * k;

//与连线方向垂直的分速度
var vpx1 = vx1 – vox1;
var vpy1 = vy1 – voy1;

//*第二个圆
//圆心连线方向的分速度
var vox2 = apart(vx2, vy2);
var voy2 = vox2 * k;

//与连线方向垂直的分速度
var vpx2 = vx2 – vox2;
var vpy2 = vy2 – voy2;

//碰撞后与连线方向垂直的分速度不变,连线方向的分速度变化
var vox1a = calculateV(m1, m2, vox1, vox2);
var vox2a = calculateV(m2, m1, vox2, vox1);

var voy1a = calculateV(m1, m2, voy1, voy2);
var voy2a = calculateV(m2, m1, voy2, voy1);

//重新复合速度
v1.x = vox1a + vpx1;
v1.y = voy1a + vpy1;
v2.x = vox2a + vpx2;
v2.y = voy2a + vpy2;

//告知动量变化量
var dp1 = sqrt(sqr(vox1a – vox1) + sqr(voy1a – voy1)) * m1;
var dp2 = sqrt(sqr(vox2a – vox2) + sqr(voy2a – voy2)) * m2;

c1.collideCallBack(dp1, c2);
c2.collideCallBack(dp2, c1);

//分解出与圆心连线方向的分速度的x方向速度
function apart(vx, vy) {
return (k * vy + vx) / (sqr(k) + 1);
}

//根据动量和能量守恒计算速度变化
function calculateV(m1, m2, v1, v2) {
return ((m1 – m2) * v1 + 2 * m2 * v2) / (m1 + m2);
}