三人行必有我师焉,择其善者而从之,其不善者而改之
三人行必有我师焉,择其善者而从之,其不善者而改之
三人行必有我师焉,择其善者而从之,其不善者而改之
三人行必有我师焉,择其善者而从之,其不善者而改之
三人行必有我师焉,择其善者而从之,其不善者而改之

  在低版本IE中使用Filters滤镜弥补CSS3的跨浏览器问题 作者:Neoxone    发表时间: 2011年05月7号,星期六     阅读:10,884 次  

跨浏览器兼容性是网页制作永恒的难题。别看各浏览器厂商都努力在自己的浏览器中实现新式的css3标准特性,但都是以扩展样式的形式提供的,
所以在未来一段时间里,样式表里的-moz-,-webkit-,(-ms-,-o-)等等前缀将长期存在。

另外,虽说各家有志于扩张自己市场的浏览器厂商都提供了css3的新特性,但鉴于其老式版本的浏览器依然存在于用户的主机上(主要是微软旗下的ie6,7,8),除了考虑各家浏览器之间的兼容性外,我们还有必要向前兼容老式浏览器。

这些老式浏览器(低版本ie)对于css3的不支持问题,真是阻碍我们迈向css3时代的一大障碍。
不幸之幸,当年ie在轻视w3c标准之时,自成一套的Filters滤镜却是具备长远眼光的。在这些Filters滤镜中,不少效果正是我们CSS3中目前实现的。

对于Filters滤镜,我们不建议单独使用,只作为解决兼容性时候的一个选择。
下面我们将解决以下低版本浏览器的问题:

1. ie6下对透明png的不支持;
2. ie6,7,8下阴影(box-shadow,text-shadow)效果的不支持;
3. ie6,7,8下渐变(Gradients)效果的不支持;
4. ie6,7,8下对rgba的不支持(一般用来做半透明层)。

1. 解决ie6对24位透明png的不支持;
第一个问题非常常见,其实有两个解决方法,
一个就是使用ie滤镜:AlphaImageLoader

使用这个滤镜之后,仍然是有一些问题没法弥补的,比如图片的repeat等等。
之前,我曾极力推荐过另外一个解决方法:使用VML,称之为完美解决方案。在这个js里已经封装好代码DD_belatedPNG
但是后来发现,使用VML效率问题比AlphaImageLoader更甚,君当慎之。

2. 解决ie6,7,8下对阴影(box-shadow,text-shadow)效果的不支持:
现在很多网站为求美观采用了很多css3的样式,其中box-shadow和text-shadow的使用率最高。
下面我们就利用Blur和dropShadow滤镜在ie6,7,8中实现这种效果:

ie的滤镜是必须要在触发haslayout情况下才能生效的(ie8除外,它已经抛弃了haslayout这个私有属性),这就是为什么我们在那里加了个zoom:1;
另外,当使用dropShadow滤镜做文字阴影时,就不应该使用background,否则阴影效果是对背景起作用了,所以我们将dropShadow滤镜加在了span上而不是class=shadow层上。
做box-shadow效果,我们还多加了一层class=ieShadow,然后使用blur滤镜来模拟的。其实滤镜中还有个Shadow滤镜,可以不需要这个额外层,但是效果并不好,所以我们并没有采用。

下面,我们做一个兼容所有浏览器的demo。

3. 解决ie6,7,8下渐变(Gradients)效果的不支持;
自从有了gradient渐变这个css3属性后,很多设计效果我们就不需要去切图了,直接使用代码就能实现,并且相对图片来说局限性更小。
为了弥补ie低版本下对gradient的不支持,我们可以使用Gradient滤镜。
ie的Gradient滤镜只支持线性渐变,且只能设置两个渐变色,不过这里注意一下,这个渐变颜色可以设置alpha透明度。

我们在看一下各浏览器兼容性的写法,遗憾的是opera并未支持。

4. 解决ie6,7,8下对rgba的不支持(一般用来做半透明层)。
这第四个问题,非常普遍,而且ie低版本下的解决方法在网上也是随处可见,就是半透明层的效果。
因为ie不支持rgba色,所以我们一般都使用ie的alpha滤镜来达到半透明效果,但是,同我们上面举的box-shadow的模拟例子一样,这个alpha滤镜和上面的blur滤镜都是针对层元素内所有子元素且包括文本节点的。所以,一旦使用了alpha滤镜,那么这个元素下面的所有东西都透明了,这常常和我们要的效果不一致。
网上一般的解决方法同上面的模拟box-shadow的blur滤镜一样,将透明层单独提取成一个层。

这种方法使用的比较普遍,但是多加的一层实在有些多余,并且有时候控制起来还比较麻烦。
我们可以换一个滤镜方法:在上面的渐变的滤镜里,我们提到了渐变色也可以使用alpha半透明值,并且这个渐变滤镜是针对于元素背景的,元素内容并不会受其影响。
那么我们可以这样实现:

显然结构,样式简单多了,再做一下其他浏览器的兼容性:

虽然我们以上解决了四种ie低版本下的不足,但在解决的过程中某些地方还是稍显繁复了,且在具体应用当中的情况或许并没有这么简单,读者当理解之后再做延伸。

除了以上四种问题被解决之外,还是有太多的css3效果在ie低版本下是无法实现的,我们不得不(在低版本浏览器中)放弃之,其中最最最遗憾的就是css3圆角效果了,当然使用VML也是能解决,封装的方法:Curved corner,同DD_belatedPNG一样,我还是建议读者谨慎采用。

ie各个Filters静态滤镜演示Demo:DXTidemo

   

  浅析圆形物体的碰撞检测及应用 作者:Neoxone    发表时间: 2011年03月16号,星期三     阅读:10,889 次  

物体的碰撞检测是游戏动画中常涉及的内容,当然我没做过游戏,只是最近接触canvas比较多,也有不少热衷html5的canvas的人提起了这个碰撞检测,所以试着拿网上一位大牛的canvas检测算法做些分析注释。
先引用些链接(看来我要形成这个习惯了):
重力版google:http://mrdoob.com/projects/chromeexperiments/google_gravity/——非规则物体的碰撞。
可融性水滴:http://hakim.se/experiments/html5/blob/03/——一个物体由多个点连接构成。

本文涉及的一个碰撞检测方法是针对于圆形物体的。显然这是比较有规律可循的,但也不要小看圆形物体的碰撞检测,一些非规则物体也可以包围球的形式使用圆形检测来检测。
代码来自:http://www.benjoffe.com/es/code/demos/collide/
本文做了一些中文翻译,并会稍加分析。

    // Coliding Discs experiment
  1. // Copyright Benjamin Joffe, 2010
  2. // 中文注释:ONEBOYS >> http://www.cssass.com
  3.  
  4.     var canvas = document.getElementById('balls');
  5.     var ctx = canvas.getContext('2d');
  6.     var co = [];
  7.     var starter = Math.random()*9 >> 0; //白球序号(随机值用右移位实现去尾取整)
  8.     
  9.     for (var i=0; i<9; i++)
  10.     {
  11.         co[i] = {  //折行排列(x,y)
  12.             x: (760/3)*(0.5+i%3),         
  13.             y: (760/3)*(0.5+((i/3)>>0)),   
  14.             r: 2*(i*2+15),             
  15. c: starter==i ? '#fff' : '#000'
  16.         };
  17.         // 随机速度(保证向量长度为1)
  18.         co[i].vx = 1-2*Math.random(); 
  19.         co[i].vy = Math.sqrt(1-co[i].vx*co[i].vx) * ( Math.random() < 0.5 ? -1 : 1 );
  20.         
  21.         // 起始阶段除白球外其他球保持静止。半径越小,i值越大,速度越大。
  22.         co[i].vx *= i==starter ? 200/co[i].r : 0;
  23.         co[i].vy *= i==starter ? 200/co[i].r : 0;
  24.     }
  25.     
  26.     function draw()
  27.     {     
  28.         ctx.fillStyle='rgba(220,220,220,0.5)';
  29.         ctx.fillRect(0,0,canvas.width, canvas.height); //涂白清屏(带透明度)
  30.         for (i=0; i<co.length; i++)
  31.         {
  32. ctx.fillStyle=co[i].c;
  33.             ctx.beginPath();
  34.             ctx.moveTo(co[i].x+co[i].r, co[i].y);
  35.             ctx.arc(co[i].x, co[i].y, co[i].r, 0, Math.PI*2, true);
  36.             ctx.fill();
  37.         }
  38.     }
  39.     
  40.     function move()
  41.     {
  42.         var total_time_left = 5;
  43.         var t=0;
  44.         var col_index; // 肇事球
  45.         var col_index2 // 被撞球
  46.         
  47.         var col_time; // 距离发生碰撞的时间
  48.         var ball_time; // 碰撞倒计时
  49.         var temp;
  50.         
  51.         var i, j, test_method;
  52.         var col_method; //碰撞方式
  53.  
  54.         while (true)
  55.         {
  56.         
  57.             col_index = -1; // choose an invalid index initially
  58.             col_time = total_time_left;
  59.             
  60.             // this loop finds the ball that will collide first
  61.             for (i=0; i<co.length; i++)
  62.             {
  63.                 // (god forbid) 若发生了灵异现象,超出了屏幕,立即反向。
  64.                 if (co[i].vx < 0 && co[i].x < co[i].r || co[i].vx > 0 && co[i].x + co[i].r > canvas.width)
  65.                 {
  66.                     co[i].vx *= -1;
  67.                     continue;
  68.                 }
  69.           
  70.                 if (co[i].vy < 0 && co[i].y < co[i].r || co[i].vy > 0 && co[i].y + co[i].r > canvas.height)
  71.                 {
  72.                     co[i].vy *= -1;
  73.                     continue;
  74.                 }
  75.                 
  76.                 // now for the real collision checks
  77.             
  78.                 // 球与墙之间检测
  79.                 for (test_method = 0; test_method < 4; test_method++ )
  80.                 {
  81.                     switch (test_method)
  82.                     {
  83.                         case 0 :
  84.                             ball_time = (co[i].r - co[i].x) / co[i].vx; //球与左壁发生碰撞的倒计时
  85.                             break;
  86.                         case 1 :
  87.                             ball_time = (co[i].r - co[i].y) / co[i].vy; // top wall
  88.                             break;
  89.                         case 2 :
  90.                             ball_time = (canvas.width - co[i].r - co[i].x) / co[i].vx; // right wall
  91.                             break;
  92.                         case 3 :
  93.                             ball_time = (canvas.height - co[i].r - co[i].y) / co[i].vy; // bottom wall
  94.                             break;
  95.                     }
  96.                     if (ball_time > 0 && ball_time < col_time) //碰撞时间为正值,且小于之前算出的碰撞时间(求出最迫切的碰撞)。
  97.                     {
  98.                         col_time = ball_time;
  99.                         col_index = i;
  100.                         col_method = test_method;
  101.                     }
  102.                 }
  103.                                 
  104.                 // 球球之间检测(检测第i球与后面所有球)
  105.                 for (j = i+1; j < co.length; j++)
  106.                 {
  107.                     ball_time = check_collision(co[i], co[j]);  /* **球球检测算法** */
  108.                     if (ball_time > 0 && ball_time < col_time)
  109.                     {
  110.                         col_method = 5;
  111.                         col_time = ball_time;
  112.                         col_index = i;
  113.                         col_index2 = j;
  114.                     }
  115.                 }
  116.             }
  117.             
  118.             // 增加位移
  119.             for (i=0; i<co.length; i++)
  120.             {
  121.                 co[i].x += col_time * co[i].vx;
  122.                 co[i].y += col_time * co[i].vy;
  123.             }
  124.             
  125.             if (col_index == -1) break; // 如无碰撞,退出循环
  126.             
  127.             // 撞到屏幕,反向
  128.             if (col_method == 0 || col_method == 2) // side walls
  129.             {
  130.                 co[col_index].vx*=-1;
  131.             }
  132.             if (col_method == 1 || col_method == 3) // top and bottom walls
  133.             {
  134.                 co[col_index].vy*=-1;
  135.             }
  136. //球球相撞
  137.             if (col_method == 5)
  138.             {
  139.                 apply_collision(co[col_index], co[col_index2]); /* ** 球球碰撞后果 ** */
  140.             }
  141.             total_time_left -= col_time;
  142.         }
  143.     }
  144.     
  145.     // 球球相撞检测
  146.      function check_collision(b1, b2){ //返回当b1,b2接触所需要的时间。
  147. var a = b1.x - b2.x,   
  148.     b = b1.vx - b2.vx, 
  149.     c = b1.y - b2.y,   
  150.     d = b1.vy - b2.vy;
  151. // I probably shouldn't have crushed the following logic into one line, makes it just a wee bit unreadable 作者在此处做了一些代码混淆。下面3行是我重写的。
  152. //return b==0&&d==0||(c=(b=2*(a*b+c*d)+0*(d=b*b+d*d))*b-4*d*(a*a+c*c-(c=b1.r+b2.r)*c))<0?-1:(-b-Math.sqrt(c))/(2*d);
  153. if(b==0&&d==0 ) return -1;
  154. var p=(a*b+c*d)/(b*b+d*d);
  155. return -p-Math.sqrt(((b1.r+b2.r)*(b1.r+b2.r)-(a*a+c*c))/(b*b+d*d)+p*p);
  156. }
  157.     
  158.     // 球球相撞处理
  159.     function apply_collision(b1, b2){ //b1,b2已接触
  160.         var
  161.             a = (b2.r * b2.r) / (b1.r * b1.r) , //半径的平方比(反映质量比)
  162.             b = (b1.y - b2.y) / (b1.x - b2.x) , //球心连线构成的正切值
  163.             c = 2*( (b1.vx - b2.vx) +b* ( b1.vy - b2.vy ) )/((1+b*b)*(1+a)); //改变的速度发生在球心连线上。
  164.         b2.vx += c;
  165.         b2.vy += c*b;
  166.         b1.vx -= c*a;
  167.         b1.vy -= c*a*b;
  168.     }
  169.     
  170.     setInterval(function()
  171.     {
  172.         move();
  173.         draw();
  174.     },30);
  175.     draw();

下面简略说一下两球之间的碰撞检测方法check_collision:
当前位置两球的x方向距离为a,y方向距离为c,两球x方向上的相对速度为b,y方向上为d。
假设经过时间t之后,两球相撞。
此时,两球的距离为(r1+r2),而在x方向上的距离应为(a+bt),y方向上距离为(c+dt)
根据直角三角形性质:(a+bt)^2+(c+dt)^2=(r1+r2)^2
经过数学换算可得:t=-(a*b+c*d)/(b*b+d*d)-Math.sqrt(((b1.r+b2.r)*(b1.r+b2.r)-(a*a+c*c))/(b*b+d*d)+((a*b+c*d)/(b*b+d*d))*((a*b+c*d)/(b*b+d*d)))
如果你不会换算,可以看看这个链接:http://www.wolframalpha.com/input/?i=(a%2Bb*t)^2%2B(c%2Bd*t)^2%3D(r1%2Br2)^2

碰撞检测符合碰撞条件之后之后,就是碰撞反弹了apply_collision。
碰撞问题其实在高中物理中,已经有所学习了。我们要解的这个问题就是一个弹性斜碰撞的问题。
这种碰撞符合的定律是:动量守恒(碰撞前后矢量和相等)和动能守恒(碰撞后起码不增加)定律。

今日就此搁笔,明天起请了年假,回来后可能的话此文再做更新,本来还准备在写个demo来着的。
回见,各位。

   

  采用CSS3的语言框效果 作者:Neoxone    发表时间: 2011年03月9号,星期三     阅读:8,740 次  

我们之前有利用css3做过一个投影效果:纯css打造投影效果——CSS3属性transform的应用
主要是利用了transforms系列属性。
另外,在这篇文章里:CSS3的崛起——体验webkit的3D-Effect
我们还讨论了目前仍属于webkit独有的3d-transforms属性。

今天这个效果的实现还是用到了transform(之中的skew)。
起源是论坛里的这个帖子:http://bbs.blueidea.com/viewthread.php?tid=3012730

看代码

上面的代码中,我们可以看见有一句ie的滤镜注释,那是ie的阴影滤镜。
其实ie在很早的时候就开始以滤镜的形式支持各种变形和阴影了,所以如果你想让ie(9以下)浏览器也能展现css3的那些效果,你也可以考虑滤镜实现。
并且,有人已经帮你实现智能转换了。非常轻松,看这里:http://www.useragentman.com/IETransformsTranslator/

而如果你不屑于ie的滤镜,认为那是即将被淘汰的事物了,那不妨看看3d-transforms以及Animation属性这种目前只有webkit支持的,未来或许能进入css3标准的东西。
http://westciv.com/tools/transforms/index.html (除一般的transforms的演示外,还有3d-transforms和Animation的演示)。

最后,提醒一句,transform属性并非css3独有,svg,canvas中也都有实现,当然他们的写法非常类似。
下面是一个canvas中的transform属性可视化演示:http://www.cssass.com/blog/index.php/2010/817.html

   

  在canvas 2D API下实现3D效果(3D版心形函数的绘制) 作者:Neoxone    发表时间: 2011年02月24号,星期四     阅读:24,210 次  

html5的canvas为我们提供了浏览器原生支持的绘图API。
(或者怎么说呢,大多数浏览器已经为我们提供了原生的绘图API:HTML5的canvas)
目前,这个API只提供2D context,并不支持3D绘图,但是web上从来就不缺牛人,各种canvas下绘制的3D效果层出不穷,令人吱吱称赞。(补充,后来有了WebGL)
有3D圣诞树:http://www.romancortes.com/blog/how-i-did-the-1kb-christmas-tree
有3D的FPS:使用 HTML 5 canvas 和光线投影算法创建伪 3D 游戏
还有3D俄罗斯:http://www.benjoffe.com/code/games/torus/
不胜枚举…

其实,无论canvas是否提供API,在我们目前这种二维显示设备下显示,势必都要将3维形状投影到2维平面坐标上。无论多炫的3D效果也只是二维平面上的投影。
对于此,读过《三体》特别是第三部《死神永生》的同学或许会大有感触吧。

人们总是喜欢用这样一个类比:想象生活在三维空间中的一张二维平面画中的扁片人,不管这幅画多么丰富多彩,其中的二维人只能看到周围世界的侧面,在他们眼中,周围的人和事物都是一些长短不一的线段而已。只有当一个二维扁片人从画中飘出来,进人三维空间,再回头看那幅画,才能看到画的全貌。
这个类比,其实也只是进一步描述了四维感觉的不可描述。

关于《三体——死神永生》里四维空间(不算时间维)的讨论,这里还有篇有意思的博文:四维世界的堵车问题

好了,言归正传。
下面提供一个三维到二维的投影算法(from www.benjoffe.com):

var theta = 4.2; //转角
var eleva = 0.6; //仰角
function iso(x,y,z){
var dist = Math.sqrt(x*x+y*y);
var angle = (x==0 && y==0) ? 0 : Math.atan(y/x) + theta + ((x<0)? Math.PI : 0);

x=Math.cos(angle)*dist;
y=-Math.sin(angle)*dist;
var fact = (y*Math.cos(eleva) + z*Math.sin(eleva)+8)/8;
y=y*Math.sin(eleva) - z*Math.cos(eleva);
x*=fact;
y*=fact;

return {
x: x,
y: y
};
}

输入是x,y,z三个三维坐标下的值,输出是x,y两个二维坐标值。

我们应用一下:
下面是一个3D球

以上代码是对球面的方程 x^2+y^2+z^2=r^2进行求解,将解(x,y,z)代入iso方法,最后根据输出二维坐标进行绘图。
对于这个球面方程的解法,也是各有各的写法。
(这里有个Functions 3D的应用,用来将方程式输出成3D图形:http://www.benjoffe.com/code/tools/functions3d/

如果你看到过我这篇文章的话:笛卡尔情书的秘密——心形函数的绘制
我相信你也很可能知道网上还有一个3D版是心形函数:(x^2 + (9/4)y^2 + z^2 – 1)^3 – x^2*z^3 – (9/80)y^2*z^3 = 0
下面我将使用上面的iso方法在canvas中将其绘制出来,你可以拖到鼠标来看3D效果。

我们知道,三维空间下的坐标系不止直角坐标一种,还有 圆柱坐标系,球坐标系等等。
下面我们将iso方法转换一下,是输入使用球坐标系值(θ,Φ,r)——转角,仰角,球半径。
首先我们先要知道,三维直角坐标系于球坐标系的换算式:

x=rsinθcosφ  
y=rsinθsinφ   
z=rcosθ

呃哦,代入iso函数后我们发现iso变的更简单了:

var theta = 4.2; //转角
var eleva = 0.6; //仰角
function iso(a,b,r){
var x,y,z
x=r*Math.cos(a+this.theta)*Math.sin(b);
y=r*Math.sin(a+this.theta)*Math.sin(b);
z=r*Math.cos(b);

var fact = (y*Math.cos(this.eleva) + z*Math.sin(this.eleva)+8)/8;
y=y*Math.sin(this.eleva) + z*Math.cos(this.eleva);
x*=fact;
y*=fact;
return {
x: x,
y: y
};
}

下面是我们使用球坐标系绘制的三维图形(三维投射到二维的图形)

   

  三体赋 作者:Neoxone    发表时间: 2011年02月11号,星期五     阅读:4,327 次  

夕阳残  血色骤  清梦徒留(叶文洁)
一念成魔   嫉世如仇(伊文斯)
嬉笑侈侈不休   半生难逢敌手(史强)
琴弦封喉   知己难求(汪淼)
思虑尘埋心头   魂色万古芳流(章北海)
志怎酬   出师未捷身先殁(泰勒&雷迪亚兹)
鸾凤相左    凝噎泪满首(希恩斯&山杉惠子)
毕一生   功成名就(萨伊)
怎敌她   似水娇柔(庄颜)
本色终现   力挽沧海横流(罗辑)
但使干戈永日休(三体世界1379号监听员)

星作酬   宇为舟   孤胆独谋(云天明)
冷若磐石   一诺千秋(维德)
啜碧茗在小楼    仗剑拨弄素手(智子)
歌缅过往    画作弥留(歌者)
情已叩    夫复何求(褚岩)
望天涯    此生相守(关一帆)
咫尺侯    沧海桑田几许惆(艾AA)
大爱如佛    奈何终是谬(程心)

悔当初   血气方遒
而如今    覆水难收
流光若返   万事皆再从头
天下同   谁人能说
(三体文明)

三百年   恩怨情仇
一朝散   烟消云走
曲终人廋   曾记多少风流
唯往事悠悠
(人类文明)

作曲/编曲:李丙雨
填词:胡尘
演唱:大若
动画剪辑:青衿