1024字节javascript写成的3D圣诞树

John [网站前端]

2010.12.20

请点击上面的图片查看Demo,或者把下面的代码保存为html文件用浏览器里打开。
由于用到canvas以及一些复杂的运算,建议使用Chrome浏览器观看,最为流畅,其次是Opera 11(已经开始有点卡了),Firefox3.6差不多是逐帧播放了,Safari 5貌似有bug,除非用鼠标不断点击,不然几乎不会动(以上结果均出自我的性能低下的小本子)。用IE的话请自重。

预祝全宇宙生物剩蛋快乐。

 查看代码 HTML4STRICT
<!doctype html>
<html>
    <head>
        <title>JS1k, 1k demo submission [856]</title>
        <meta charset="utf-8" />
    </head>
    <body>
        <canvas id="c"></canvas>
        <script>
            var b = document.body;
            var c = document.getElementsByTagName('canvas')[0];
            var a = c.getContext('2d');
            document.body.clientWidth; // fix bug in chrome.
        </script>
        <script>
        // start of submission //
        M=Math;Q=M.random;J=[];U=16;T=M.sin;E=M.sqrt;for(O=k=0;x=z=j=i=k<200;)with(M[k]=k?c.cloneNode(0):c){width=height=k?32:W=446;with(getContext('2d'))if(k>10|!k)for(font='60px Impact',V='rgba(';I=i*U,fillStyle=k?k==13?V+'205,205,215,.15)':V+(147+I)+','+(k%2?128+I:0)+','+I+',.5)':'%23cca',i<7;)beginPath(fill(arc(U-i/3,24-i/2,k==13?4-(i++)/2:8-i++,0,M.PI*2,1)));else for(;x=T(i),y=Q()*2-1,D=x*x+y*y,B=E(D-x/.9-1.5*y+1),R=67*(B+1)*(L=k/9+.8)>>1,i++<W;)if(D<1)beginPath(strokeStyle=V+R+','+(R+B*L>>0)+',40,.1)'),moveTo(U+x*8,U+y*8),lineTo(U+x*U,U+y*U),stroke();for(y=H=k+E(k++)*25,R=Q()*W;P=3,j<H;)J[O++]=[x+=T(R)*P+Q()*6-3,y+=Q()*U-8,z+=T(R-11)*P+Q()*6-3,j/H*20+((j+=U)>H&Q()>.8?Q(P=9)*4:0)>>1]}setInterval(function G(m,l){A=T(D-11);if(l)return(m[2]-l[2])*A+(l[0]-m[0])*T(D);a.clearRect(0,0,W,W);J.sort(G);for(i=0;L=J[i++];a.drawImage(M[L[3]+1],207+L[0]*A+L[2]*T(D)>>0,L[1]>>1)){if(i==2e3)a.fillText('Merry Xmas!',U,345);if(!(i%7))a.drawImage(M[13],((157*(i*i)+T(D*5+i*i)*5)%W)>>0,((113*i+(D*i)/60)%(290+i/99))>>0);}D+=.02},1)
        // end of submission //
        </script>
    </body>
</html>

作者原文里有详细的原理解析。

源文:How I did the 1kb Christmas Tree
作者:romancortes


[翻译]Data URI浅析

John [网站前端]

2010.08.05

在最近的印象中,最受期待的浏览器功能之一就是data URI了。最近已经有不少关于data URI的文章:我同事Stoyan Stefanov已经写了两篇关于data URI的文章,我旧同事Hedger Wang也写了一篇关于如何在IE使用data URI的文章。但出乎意料的是,对于data URI的误解和困惑依然屡见不鲜。它是什么?它是怎么工作的?为什么你会想去使用它?

是URI,不是URL

URL是uniform resource locator的缩写,是一个特定资源的协议(用什么方式去获取数据)和地址的组合。每一个公开可访问的资源,例如图片,JS文件,HTML文件或者样式表文件,都有一个URL告诉浏览器从哪里下载它们。浏览器会根据这个URL建立一个链接,并开始下载或执行这个文件。

URL其实也是个URI,URI是uniform resource identifier的缩写。URI指定了一个协议用来接收信息,包括一些关于资源的额外的信息。那些额外的信息可能是一个地址也可能不是(如果是的话,那么URI就是URL了),但是它总是跟一个特定的协议和有关联。因此,既然不包含地址信息,data URI也就不是URL了。

Data URI的格式

data URI的格式很简单,在RFC 2397里有清楚的说明(很短,你可以把它全部看完)。基本的格式如下:

data:[<mime type>][;charset=<charset>][;base64],<encoded data>

在这个格式中,data:URI的协议,表明这是一个data URI。第二部分,MIME type,表明了要呈现的数据的类型。拿PNG图片举个例子,它的MIME type是image/png。如果没有指定,MIME type将会默认为text/plain。charset在大多数情况下可以无视,对于图片来说它根本没用。下一部分指明了使用的编码。跟流行观念相反,你不一定要用base 64编码。如果内容不是用base 64进行编码,那么这些数据就会使用标准的URL编码(对URL安全的ASCII字符将会保留原样显示,其他会显示成%xx格式的十六进制编码)进行编码。编码后的数据可能会包含一些没用空格,

Base 64编码

Base 64编码是一个编码规则,通过它数据被转化成二进制码,然后组合成一个base 64符号的序列。Base 64符号包括大写和小写的字母A到Z,数字,符号+和/。=号是用来填充用的(可以查看Wikipedia上的文章获取更多的信息)。你真正需要知道的是base 64编码会使编码过的数据变得更小。

下面的例子是一张GIF图片用base 64进行编码后的data URI(来源):

data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge
8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1h
LnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g
77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7

同一张图片如果不用base 64编码的话将会如下显示:

data:image/gif,GIF89a%22%00%1B%00%F7%00%00lll%D6%D6%D6%FF%EB%85
%FF%E0%7B%FF%F7%91%FF%D4o%DF%DF%DF%F6%F6%F6%87%87%87%FE
%CBf%FF%F4%8E%E6%B3NKKK%C5%92-%FF%FF%99%FF%FF%FF%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%2C%00%00
%00%00%22%00%1B%00%00%08%A9%00%1F%08%1CH%B0%A0%C1%83%08
%13*%5C%C8%B0%A1%C3%87%10%23J%9CH%91%60%83%8B%0D%0C%1C
%A8h%B0%81%C5%00%1B9%0A%F4%E8%A0%A4%83%07%181j%9C%D8%80
%80%82%97%2F%0B6%40%60%80%A5%00%01)s%AA%94%D8%60%80G%84
%02P%22%E0Y%A0%81%C9%A3%25%138h%00%80g%02%A3%04%A2J%8D
%BA%60i%D3%88%0D%9E%3A%B8%C9%95kU%A6N%8D%0E%18Kv%EC%D7
%AB%10%B3%1A-%C0%B6-%5B%A3%60%23%1A%D0I%97%C1%D0%88%07
%02%20%00%C0%B7%AF_%00%08%02L%3C%60%20%80%E1%C3%88%03
%AC%14%C9%B8%B1%E3%C7%90%23K%9EL0%20%00%3B

就大小来说,用base 64编码的图片完胜,它明显小多了。

注意:Base 64编码事实上会使图像变大。然而,如果你使用了HTTP压缩,那么你并不会察觉到有什么差异,因为base 64编码的数据的压缩性极好。如果因为某些原因不能使用HTTP压缩的话,那么你可能会想知道你发送的数据究竟有多大,然后权衡下是否值得这么做。

不仅仅用于图像

尽管大部分人在谈论data URI作为在HTML或者CSS文件里嵌入图片的方法,但这里并没有指定是图片。你可以编码然后嵌入任何类型的文件,甚至是HTML本身。Ian Hickson,HTML 5名人(或者恶人,视乎你怎么看)提供了一个工具让你体验下data URI。默认的例子是使用或者不用base 64编码把一个HTML文件转变成data URI。体验一下这个data URI产生器可以帮助你具体化一下核心的概念。

注意:IE8对data URI有安全限制,使得data URI对于非图片的数据的用处大打折扣。

性能影响

data URI最有趣的地方是它可以让你把文件嵌入到其他文件中。大多数的文章都把焦点放在把data URI嵌入到CSS文件里改善性能上。实际上,大量的研究表明,HTTP请求是影响网页性能最主要的因素之一,减少请求的数量可以改善页面的性能。实际上,“最小化HTTP请求”是Yahoo! Exceptional Performance Best Practices的第一准则,它特别提到了data URI:

内联图像使用了data URI把图像数据嵌入到页面中,会增加HTML文档的大小。把内联图像在(已缓存的)样式表里组合起来可以减少HTTP请求和避免页面大小的增加。内联图像还没有得到所有主流浏览器的支持。

这对使用data URI来说是个很好的建议:把它们用在缓存频率最高的地方。通过HTTP下载的普通图片会根据它们的header和浏览器设置进行缓存,这样它们就不用每次请求都被重新下载。Data URI会被当作装载它的那个文件的一部分,所以它是所嵌入的HTML或者CSS文件的一部分。这意味着data URI没有单独的缓存控制策略。嵌入data URI会使你的文件变大,如果文件经常改动的话(例如博客的首页),那么这个变大的文件就会被频繁的下载,使网页速度变慢。

最简单的使用方法是把data URI嵌入到一个强制缓存的外部样式表里。这样的话,在浏览器缓存为空的情况下,网站速度会比较快(因为少了额外的请求),而在浏览器存在缓存的情况下,则体验是一样的。

浏览器支持情况

绝大多数的现代浏览器都支持data URI:
Firefox 2+
Opera 7.2+ – data URI必须少于4100个字符
Chrome (all versions)
Safari (all versions)
Internet Explorer 8+ – data URI必须小于32k

由于版本低于8的IE浏览器不支持data URI,你需要决定是否值得为这些不支持data URI的浏览器提供替代的东西(可以看看Stoyan的文章)。

结论

Data URI对网页来说是个很有趣和独特的概念,可能在将来会被越来越广泛的应用,暂时来说,它大概是最适合用于那些跟性能相关的任务上,但是谁也不知道将会会怎样。但目前你能看到的是使用data URI获取图片时减少额外的HTTP请求带来的一些性能上的优化。另外,Data URI还带来了使用JS动态产生图片的可能性,尽管浏览器对<canvas>的支持的不断增加可能会使得这个方法被淘汰。

Translated by icyfire @ company ON Aug 05, 2010
源文:Data URIs explained
作者:Nicholas C. Zakas


[翻译]jQuery的show/hide性能测试

John [网站前端]

2010.07.26

这篇文章是jQuery各种show/hide方式的性能测试。作者之所以测试这个源于Robert Duffy在San Francisco举行的jQuery大会上的一句话:“.hide()和.show()的执行速度会比直接改变css慢”。但由于未能找Robert Duffy问明原因,所以作者就自己去做了这个测试。下面的翻译并不是全文翻译,只节选了一些重点。

用作测试的是一个含有100个div的HTML页面,div带有class和一些内容。为了排除掉寻找这些div所花费的时间,所以把选择器$(‘div’)缓存起来了。用作测试的jQuery版本是1.4.2,所以测试结果也只是针对这个版本,在其他版本可能就不是这些结果了。

测试的jQuery方法分别是:

  • .toggle()
  • .show() 和 .hide()
  • .css({‘display’:'none’}) 和 .css({‘display’:'block’})
  • .addClass(‘hide’) 和 .removeClass(‘hide’)
  • 改变<style>元素的一个属性

.show() 和 .hide()

在所有浏览器中,这两个方法在隐藏DOM元素上相对来说比较慢。主要原因在于.hide()方法必须先保存元素的”display”属性,这样.show()才能把元素恢复到原来的状态。这里用到了.data()这个jQuery方法,把信息保存在DOM元素上。为了达到这个目的,.hide()在每个元素上循环了两次,一次用来保存当前的”display”值,一次用来更新样式”display”为”none”。根据源代码上的注释,这样做是为了防止浏览器在每个循环上进行重新渲染(reflow)。.hide()方法还会检查你是否传递了使用动画效果的参数,就算传入一个”0″也会让性能大打折扣。在第一次调用.hide()的时候性能最慢,在之后再调用则会变快。

Browser hide/show
FireFox 3.6 29ms / 10ms
Safari 4.05 6ms / 1ms
Opera 10.10 9ms / 1ms
Chrome 5.0.3 5ms / 1ms
IE 6.0 31ms / 16ms
IE 7.0 15ms / 16ms

.toggle()

这个方法是最慢的。它会检查选择器返回的每一个元素当前是否可见,如果可见的话就调用.hide()方法,不可见则调用.show()方法。不但如此,它不仅会检查你是否传递了一个boolean值进去阻止.hide()或者.show()的执行,还会检查看你是否传入了function来进行切换(toggle)而不是对可见性进行切换。看起来这个方法还有很大的改善空间,例如可以先一次过把隐藏的元素select出来,然后调用.show()方法,同时把其余的元素select出来调用.hide()方法。

Browser hide/show
FireFox 3.6 80ms / 59ms
Safari 4.05 24ms / 30ms
Opera 10.10 67ms / 201ms
Chrome 5.0.3 55ms / 20ms
IE 6.0 296ms / 78ms
IE 7.0 328ms / 47ms

.addClass() 和 .removeClass()

这是两个很漂亮的隐藏/显示DOM元素方法。在Firefox上它的速度是.show()和.hide()的两倍,而在Safari上则是三倍。不过在IE6,IE7,Chrome和Opera上,两种方法几乎没什么差别。值得一提的是,对于100个DOM节点来说,两种方法在Firefox上相差18ms,在Safari相差4ms,速度的差异只会体现在大量节点选择的时候。不过增加和移除class需要你花费更多的工作,因为你需要创建一个用于隐藏的class,然后还要时刻关注着这个class的优先级以保证DOM能隐藏。jQuery增加和移除class是通过字符串操作的,所以我觉得随着元素上class数量的增加,这个方法会变慢,但是我还没对此进行测试过。

Browser hide/show
FireFox 3.6 11ms / 11ms
Safari 4.05 2ms / 2ms
Opera 10.10 6ms / 3ms
Chrome 5.0.3 3ms / 1ms
IE 6.0 47ms / 32ms
IE 7.0 15ms / 16ms

.css({‘display’:'none’}) 和 .css({‘display’:'block’})

这两个方法也很漂亮。相对于.addClass()和.removeClass(),IE6/7和Opera上的速度都得到了提升,而在其他浏览器上则能保持水准。当你知道要改变的元素的当前display样式,或者没有通过inline的方式去改变元素的display样式时,这两个方法很好用。如果你通过inline的方式改变了display样式,那么你需要确保在使得元素重新可见时display值要设置正确。如果你只是使用了元素的默认display值或者在css里设置display值,那么你只需要用类似.css({‘display’:”})的方法移除样式,元素就会恢复到它在css上的样式或者默认display值。作为一个类库,jQuery不能假定元素的display不是通过inline方式设置的,所以它需要被人手的去确定。不过既然你知道你不会去inline的设置display,那么你就可以去避免这个造成缓慢的主要因素。

Browser hide/show
FireFox 3.6 14ms / 12ms
Safari 4.05 2ms / 1ms
Opera 10.10 2ms / 2ms
Chrome 5.0.3 2ms / 1ms
IE 6.0 16ms / 16ms
IE 7.0 0ms / 0ms // 少于15ms会变成0ms,具体看这里

禁止样式表

纯粹为了好玩,我想:如果我们不在每个dom节点上花功夫,而是去捣鼓样式表会怎样呢?这样会提高速度吗?其实就日常使用来说,上面的测试用到的方法已经足够快了,但是如果页面上有10000个节点需要进行隐藏和显示呢?只是把它们全部选择出来就已经够慢了。如果我可以控制样式表,那么就可以完全避免这些时间花费了。不过我得告诉你,这个方法是有很大风险的。

风险在于控制样式表时的跨浏览器问题。首先,我尝试能不能通过jQuery插入一个带有class的”style”标签,但是却出现了跨浏览器问题。然后我尝试用javascript去创建stylesheet节点和class,但是实在有太多的API了,要搞清楚需要花不少的时间。最后,放弃了编程的方式,我在head区里写了一个带有class的style标签。通过编程的方式来创建stylesheet实在是太慢了,但是如果它一旦被创建好,那么给它一个ID和使用它的”disabled”属性就是轻而易举的事情了。

 查看代码 HTML4STRICT
1
2
3
<style id="special_hide">.special_hide { display: none; }</style>
<!--  ...  -->
<div class="special_hide">Special hide DIV</div>

然后在javascript里:

 查看代码 JAVASCRIPT
1
$('#special_hide').attr('disabled, 'true');

搞定!所有带有”special_hide”这个class的元素都显示出来了。要隐藏它们,你只需要……

 查看代码 JAVASCRIPT
1
$('#special_hide').attr('disabled', 'false');

现在它们全部都隐藏了。总的javascript耗时在所有浏览器上都是0-1ms。你的javascript只是用来改变一个属性。当然,浏览器还是需要花费时间去重新渲染页面的,但是实际上你已经避免了javascript的处理时间。如果你调用了.toggle(),.hide()或者 .css()这几个方法,那么这个方法就会失效。因为那几个方法会通过内联方式设置css样式,这些样式有更高的优先级。要重新使这个方法生效,只需调用.css(‘display’, ”) 把内联的样式移除掉。这个方法同样需要花费你更多的精力,因为那需要去定义class,同时把这些class赋给页面上需要进行显示/隐藏的元素,但是如果你所要处理的元素数量是极其庞大的话,那么这也许是值得的。

简要回顾一下,下面是改变元素显示状态的方法,按照最快到最慢的次序排列:

  • 禁用/启用样式表
  • .css(‘display’, ”), .css(‘display’, ‘none’)
  • .addClass(), .removeClass()
  • .show(), .hide()
  • .toggle()

需要注意的是,在大多数的情况下,这些方法都足够的快了。当你要操作很大的jQuery集合时,那么.show() 和 .hide()方法在IE下就会变得很慢了,这是你可能要用addClass() 或者 .removeClass()方法。 禁用/启用样式表的方法只有在很极端的情况下才有必要用到。

Translated by icyfire @ company ON Jul 26, 2010
源文:Now you see me… show/hide performance


[js组件]弹出框artDialog

John [未分类]

2010.05.13

最近在做一个项目,对之前所用的弹出框速度不是很满意,但是一直没找到个满意的(其实ymPrompt和lhgdialog都不错),后来在Blueidea的论坛里看到了artDialog,试用了下,感觉还不错,于是用到项目上。



名称:artDialog
作者唐斌
版本v 2.0.9 Betav 2.10 preview
更新:2010-05-18
LicenseGNU Lesser General Public License

简介:artDialog是一个轻巧且高度兼容的javascript对话框组件,可让你的网页交互拥有桌面软件般的用户体验。

功能:支持锁定屏幕(遮罩)、模拟alert和confirm、多窗口弹出、静止定位、支持Ese键关闭对话框、定时关闭、自定义位置、拖动、鼠标调节窗口大小、换肤、穿越框架..

优点

  • 跨浏览器支持: 支持 Firefox, Chrome, Safari, IE6+, Opera ..
  • 自适应内容: 无需预设高宽, 对话框与按钮自适应消息内容的大小
  • 容错能力: 如果定义的宽度高度小于内容大小不会出现错位
  • 轻巧:不依赖第三方库, js压缩后不足10kb
  • 文本智能对齐: 如果设置了对话框宽度,短文本居中长文本居左对齐
  • IE6无抖动静止定位: 在 IE6 下可实现与现代浏览器一样完美静止定位效果
  • 智能定位: 使用自定义坐标的时候智能修正位置, 不会超出边界 (适用于弹出菜单)
  • 拖动流畅: 不会粘住鼠标也不会拖出浏览器视口导致无法控制
  • 界面机制: 九宫格布局, 样式钩子与结构丰富, 可定制自适应皮肤
  • IE6遮盖下拉控件支持: 支持 IE6 下覆盖下拉控件 (注:半透明皮肤不支持)

下载Google Code

使用感受
好吧,上面的介绍跟优点都是从DEMO页面抄下来。

还是说说我自己的使用感受吧。首先调用使用的是流行的传入JSON参数的方法,这个我喜欢:

 查看代码 JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
artDialog(
        {	
            content:'欢迎你来到对话框世界!',
            lock:true,
            style:'succeed noClose'
        },
        function(){
            alert('你点了确定');
        },
        function(){
            alert('你点了取消');
        }
);

另外在页面载入的时候,会预先弹出一个隐藏的对话框,预先加载了一些皮肤图片,这样可以加快以后弹出框的弹出速度,很聪明的做法。

代码结构清晰,而且扩展起来很方便。

不过虽然优点这么多,但是在实际使用中还是会发现不少问题,下面是我遇到的部分问题:

  • IE7的一个iframe bug。artDialog使用的是createElement的方法来创建节点,这跟ymPrompt和lhgdialog不同。而使用createElement方法创建的iframe在IE7下会出现边框,虽然artDialog里面设了iframe的border为0,但是这在IE7下是不会有作用的,解决方法是创建了iframe后用setAttribute(‘frameborder’, ‘0′, 0),注意需要第三个参数”0″,不然IE7还是不认。新版已改进。
  • artDialog还有个重要的特性是对象重用,之前创建的对象会用一个box装起来,并用id作标识,如果是相同的id,则会用回之前的对象,加快了弹出框的创建速度,但是这在FF之类的浏览器下工作得很好,可是在IE下,这样却会在DOM下产生许多副本,详细情况用IE DOM inspector看看就知道了。在IE下关闭一个弹出框后再打开另外一个,你会发现框了的内容会首先出现之前那个弹出框的内容再出现实际的内容。我的解决方法是……去掉对象重用的相关代码。
  • artDialog有个不错的功能:锁定。但是事实上在我这里并没用到,锁定会把屏幕锁住,弹出框不能移动,背景用遮罩盖住,突显弹出框。其实我只是想要个普通的遮罩来突显弹出框而已,不需要锁定,而且锁定时会把滚动条干掉,导致页面会整体移动,个人感觉不太好。不过扩展个overlay功能其实不难。新版已经改进了这个功能。Very good job!
  • 弹出框的定位使用的是黄金比例垂直居中,我不知道这个的好处是什么,实际上的效果并不是很好。水平居中没问题,但是垂直居中有时会太靠顶,所以最后我还是改为普通的垂直居中了。2.10已经改进。
  • 使用iframe作为弹出内容的话,artDialog会首先出现一个loading的动画,等iframe加载完才显示。这很好,但是如果你的iframe里使用了jQuery的$(document).ready()的话,那恭喜你中大奖了。ready了的代码可能会出现异常,特别是在IE下。onload的速度一般会比jQuery的ready要慢,会是这个原因引起的吗?未作深入研究,所以我直接把这个loading干掉了。

大致上我遇到比较重大的问题就是上面这几个了,希望下一个版本能让我喜出望外。

Updated 2010.05.20 artDialog的作者是个挺不错的人,跟他在微博聊了下也反应了一些问题,想不到新版本立刻就修复了。


判断滚动条滚动到底部的Javascript

John [未分类]

2010.05.04

一个判断滚动条是否滚动到底部的js。实际运用可以把clientHeight和scrollHeight放在方法外面,因为这两个值是不变的,没必要每次都进行计算。IE,FF,Opera,Chrome,Safari均可用。

 查看代码 JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function reachBottom(){
    var scrollTop = 0;
    var clientHeight = 0; 
    var scrollHeight = 0;
 
    if(document.documentElement && document.documentElement.scrollTop) {  
        scrollTop = document.documentElement.scrollTop;  
    } else if (document.body) {  
	scrollTop = document.body.scrollTop;  
    }  
 
    if(document.body.clientHeight && document.documentElement.clientHeight) {  
        clientHeight = (document.body.clientHeight < document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight;          
    } else {  
        clientHeight = (document.body.clientHeight > document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight;      
    }
 
    scrollHeight = Math.max(document.body.scrollHeight,document.documentElement.scrollHeight);
 
    if(scrollTop + clientHeight == scrollHeight) {  
        return true;  
    } else {  
        return false; 
    }
}

公历农历互转的一个PHP

John [未分类]

2010.05.01

最近为项目写的一个应用需要用到公历农历互转的功能,所以特地写了个PHP类。事实上是参照一个Javascript改写而来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<?php
class Lunar {
	public $MIN_YEAR = 1891;
	public $MAX_YEAR = 2100;
	public $lunarInfo = array(array(0,2,9,21936),array(6,1,30,9656),array(0,2,17,9584),array(0,2,6,21168),array(5,1,26,43344),array(0,2,13,59728),array(0,2,2,27296),array(3,1,22,44368),array(0,2,10,43856),array(8,1,30,19304),array(0,2,19,19168),array(0,2,8,42352),array(5,1,29,21096),array(0,2,16,53856),array(0,2,4,55632),array(4,1,25,27304),array(0,2,13,22176),array(0,2,2,39632),array(2,1,22,19176),array(0,2,10,19168),array(6,1,30,42200),array(0,2,18,42192),array(0,2,6,53840),array(5,1,26,54568),array(0,2,14,46400),array(0,2,3,54944),array(2,1,23,38608),array(0,2,11,38320),array(7,2,1,18872),array(0,2,20,18800),array(0,2,8,42160),array(5,1,28,45656),array(0,2,16,27216),array(0,2,5,27968),array(4,1,24,44456),array(0,2,13,11104),array(0,2,2,38256),array(2,1,23,18808),array(0,2,10,18800),array(6,1,30,25776),array(0,2,17,54432),array(0,2,6,59984),array(5,1,26,27976),array(0,2,14,23248),array(0,2,4,11104),array(3,1,24,37744),array(0,2,11,37600),array(7,1,31,51560),array(0,2,19,51536),array(0,2,8,54432),array(6,1,27,55888),array(0,2,15,46416),array(0,2,5,22176),array(4,1,25,43736),array(0,2,13,9680),array(0,2,2,37584),array(2,1,22,51544),array(0,2,10,43344),array(7,1,29,46248),array(0,2,17,27808),array(0,2,6,46416),array(5,1,27,21928),array(0,2,14,19872),array(0,2,3,42416),array(3,1,24,21176),array(0,2,12,21168),array(8,1,31,43344),array(0,2,18,59728),array(0,2,8,27296),array(6,1,28,44368),array(0,2,15,43856),array(0,2,5,19296),array(4,1,25,42352),array(0,2,13,42352),array(0,2,2,21088),array(3,1,21,59696),array(0,2,9,55632),array(7,1,30,23208),array(0,2,17,22176),array(0,2,6,38608),array(5,1,27,19176),array(0,2,15,19152),array(0,2,3,42192),array(4,1,23,53864),array(0,2,11,53840),array(8,1,31,54568),array(0,2,18,46400),array(0,2,7,46752),array(6,1,28,38608),array(0,2,16,38320),array(0,2,5,18864),array(4,1,25,42168),array(0,2,13,42160),array(10,2,2,45656),array(0,2,20,27216),array(0,2,9,27968),array(6,1,29,44448),array(0,2,17,43872),array(0,2,6,38256),array(5,1,27,18808),array(0,2,15,18800),array(0,2,4,25776),array(3,1,23,27216),array(0,2,10,59984),array(8,1,31,27432),array(0,2,19,23232),array(0,2,7,43872),array(5,1,28,37736),array(0,2,16,37600),array(0,2,5,51552),array(4,1,24,54440),array(0,2,12,54432),array(0,2,1,55888),array(2,1,22,23208),array(0,2,9,22176),array(7,1,29,43736),array(0,2,18,9680),array(0,2,7,37584),array(5,1,26,51544),array(0,2,14,43344),array(0,2,3,46240),array(4,1,23,46416),array(0,2,10,44368),array(9,1,31,21928),array(0,2,19,19360),array(0,2,8,42416),array(6,1,28,21176),array(0,2,16,21168),array(0,2,5,43312),array(4,1,25,29864),array(0,2,12,27296),array(0,2,1,44368),array(2,1,22,19880),array(0,2,10,19296),array(6,1,29,42352),array(0,2,17,42208),array(0,2,6,53856),array(5,1,26,59696),array(0,2,13,54576),array(0,2,3,23200),array(3,1,23,27472),array(0,2,11,38608),array(11,1,31,19176),array(0,2,19,19152),array(0,2,8,42192),array(6,1,28,53848),array(0,2,15,53840),array(0,2,4,54560),array(5,1,24,55968),array(0,2,12,46496),array(0,2,1,22224),array(2,1,22,19160),array(0,2,10,18864),array(7,1,30,42168),array(0,2,17,42160),array(0,2,6,43600),array(5,1,26,46376),array(0,2,14,27936),array(0,2,2,44448),array(3,1,23,21936),array(0,2,11,37744),array(8,2,1,18808),array(0,2,19,18800),array(0,2,8,25776),array(6,1,28,27216),array(0,2,15,59984),array(0,2,4,27424),array(4,1,24,43872),array(0,2,12,43744),array(0,2,2,37600),array(3,1,21,51568),array(0,2,9,51552),array(7,1,29,54440),array(0,2,17,54432),array(0,2,5,55888),array(5,1,26,23208),array(0,2,14,22176),array(0,2,3,42704),array(4,1,23,21224),array(0,2,11,21200),array(8,1,31,43352),array(0,2,19,43344),array(0,2,7,46240),array(6,1,27,46416),array(0,2,15,44368),array(0,2,5,21920),array(4,1,24,42448),array(0,2,12,42416),array(0,2,2,21168),array(3,1,22,43320),array(0,2,9,26928),array(7,1,29,29336),array(0,2,17,27296),array(0,2,6,44368),array(5,1,26,19880),array(0,2,14,19296),array(0,2,3,42352),array(4,1,24,21104),array(0,2,10,53856),array(8,1,30,59696),array(0,2,18,54560),array(0,2,7,55968),array(6,1,27,27472),array(0,2,15,22224),array(0,2,5,19168),array(4,1,25,42216),array(0,2,12,42192),array(0,2,1,53584),array(2,1,21,55592),array(0,2,9,54560));
 
	/**
	 * 将阳历转换为阴历
	 * @param year 公历-年
	 * @param month 公历-月
	 * @param date 公历-日
	 */
	function convertSolarToLunar($year,$month,$date){
		//debugger;
		$yearData = $this->lunarInfo[$year-$this->MIN_YEAR];
		if($year==$this->MIN_YEAR&&$month<=2&&$date<=9){
			return array(1891,'正月','初一','辛卯',1,1,'兔');
		}
		return $this->getLunarByBetween($year,$this->getDaysBetweenSolar($year,$month,$date,$yearData[1],$yearData[2]));
	}
 
	/**
	* 将阴历转换为阳历
	* @param year 阴历-年
	* @param month 阴历-月,闰月处理:例如如果当年闰五月,那么第二个五月就传六月,相当于阴历有13个月,只是有的时候第13个月的天数为0
	* @param date 阴历-日
	*/
	function convertLunarToSolar($year,$month,$date){
		$yearData = $this->lunarInfo[$year-$this->MIN_YEAR];
		$between = $this->getDaysBetweenLunar($year,$month,$date);
		$res = mktime(0,0,0,$yearData[1],$yearData[2],$year);
 
		$res = date('Y-m-d',$res+$between*24*60*60);
 
		$day	= split_date($res);
		$year	= $day[0];
		$month= $day[1];
		$day	= $day[2];
 
		return array($year, $month, $day);
	}
 
	/**
	 * 判断是否是闰年
	 * @param year
	 */
	function isLeapYear($year){
		return (($year%4==0 && $year%100 !=0) || ($year%400==0));
	}
 
	/**
	 * 获取干支纪年
	 * @param year
	 */
	function getLunarYearName($year){
		 $sky = array('庚','辛','壬','癸','甲','乙','丙','丁','戊','己');
		 $earth = array('申','酉','戌','亥','子','丑','寅','卯','辰','巳','午','未');
 
		 $year = $year.'';
		 return $sky[$year{3}].$earth[$year%12];
	}
 
	/**
	 * 根据阴历年获取生肖
	 * @param year 阴历年
	 */
	function getYearZodiac($year){
		 $zodiac = array('猴','鸡','狗','猪','鼠','牛','虎','兔','龙','蛇','马','羊');
		 return $zodiac[$year%12];
	}
 
	/**
	 * 获取阳历月份的天数
	 * @param year 阳历-年
	 * @param month 阳历-月
	 */
	function getSolarMonthDays($year,$month){
		 $monthHash = array('1'=>31,'2'=>$this->isLeapYear($year)?29:28,'3'=>31,'4'=>30,'5'=>31,'6'=>30,'7'=>31,'8'=>31,'9'=>30,'10'=>31,'11'=>30,'12'=>31);
		 return $monthHash["$month"];
	}
 
 
	/**
	 * 获取阴历月份的天数
	 * @param year 阴历-年
	 * @param month 阴历-月,从一月开始
	 */
	function getLunarMonthDays($year,$month){
		 $monthData = $this->getLunarMonths($year);
		 return $monthData[$month-1];
	}
 
	/**
	 * 获取阴历每月的天数的数组
	 * @param year
	 */
	function getLunarMonths($year){
 
		$yearData = $this->lunarInfo[$year - $this->MIN_YEAR];
		$leapMonth = $yearData[0];
 
		$bit = decbin($yearData[3]);
		for ($i = 0; $i < strlen($bit);$i ++) {
			$bitArray[$i] = substr($bit, $i, 1);
		}
 
		for($k=0,$klen=16-count($bitArray);$k<$klen;$k++){
			array_unshift($bitArray, '0');
		}
 
		$bitArray = array_slice($bitArray,0,($leapMonth==0?12:13));
		for($i=0; $i<count($bitArray); $i++){
			$bitArray[$i] = $bitArray[$i] + 29;
		}
 
		return $bitArray;
	}
 
	/**
	 * 获取农历每年的天数
	 * @param year 农历年份
	 */
	function getLunarYearDays($year){
		$yearData = $this->lunarInfo[$year-$this->MIN_YEAR];
		$monthArray = $this->getLunarYearMonths($year);
		$len = count($monthArray);
		return ($monthArray[$len-1]==0?$monthArray[$len-2]:$monthArray[$len-1]);
	}
 
	function getLunarYearMonths($year){
		 //debugger;
		$monthData = $this->getLunarMonths($year);
		$res=array();
		$temp=0;
		$yearData = $this->lunarInfo[$year-$this->MIN_YEAR];
		$len = ($yearData[0]==0?12:13);
		for($i=0;$i<$len;$i++){
			$temp=0;
			for($j=0;$j<=$i;$j++){
				$temp+=$monthData[$j];
			}
			array_push($res, $temp);
		}
	 	return $res;
	}
 
	/**
	 * 获取闰月
	 * @param year 阴历年份
	 */
	function getLeapMonth($year){
		$yearData = $this->lunarInfo[$year-$this->MIN_YEAR];
		return $yearData[0];
	}
 
	/**
	 * 计算阴历日期与正月初一相隔的天数
	 * @param year
	 * @param month
	 * @param date
	 */
	function getDaysBetweenLunar($year,$month,$date){
		$yearMonth = $this->getLunarMonths($year);
 
		$res=0;
		for($i=1;$i<$month;$i++){
			$res +=$yearMonth[$i-1];
		}
		$res+=$date-1;
 
		return $res;
	}
 
	/**
	 * 计算2个阳历日期之间的天数
	 * @param year 阳历年
	 * @param cmonth
	 * @param cdate
	 * @param dmonth 阴历正月对应的阳历月份
	 * @param ddate 阴历初一对应的阳历天数
	 */
	function getDaysBetweenSolar($year,$cmonth,$cdate,$dmonth,$ddate){
 
		$a = mktime(0,0,0,$cmonth,$cdate,$year);
		$b = mktime(0,0,0,$dmonth,$ddate,$year);
 
		return ceil(($a-$b)/24/3600);
	}
 
	/**
	 * 根据距离正月初一的天数计算阴历日期
	 * @param year 阳历年
	 * @param between 天数
	 */
	function getLunarByBetween($year,$between){
		//debugger;
		$lunarArray = array();
		$yearMonth=array();
		$t=0;
		$e=0;
		$leapMonth=0;
		$m='';
 
		if($between==0){
			array_push($lunarArray, $year,'正月','初一');
			$t = 1;
			$e = 1;
		}else{
			$year = $between>0? $year : ($year-1);
			$yearMonth = $this->getLunarYearMonths($year);
			$leapMonth = $this->getLeapMonth($year);
			$between = $between>0?$between : ($this->getLunarYearDays($year)+$between);
 
			for($i=0;$i<13;$i++){
				if($between==$yearMonth[$i]){
					$t=$i+2;
					$e=1;
					break;
				}else if($between<$yearMonth[$i]){
					$t=$i+1;
					$e=$between-(empty($yearMonth[$i-1])?0:$yearMonth[$i-1])+1;
					break;
				}
			}
			$m = ($leapMonth!=0&&$t==$leapMonth+1)?('闰'.$this->getCapitalNum($t- 1,true)):$this->getCapitalNum(($leapMonth!=0&&$leapMonth+1<$t?($t-1):$t),true);
 
			array_push($lunarArray,$year,$m,$this->getCapitalNum($e,false));
		}
		array_push($lunarArray,$this->getLunarYearName($year));//天干地支
		array_push($lunarArray,$t,$e);
		array_push($lunarArray,$this->getYearZodiac($year));//12生肖
		array_push($lunarArray,$leapMonth);//闰几月
		return $lunarArray;
	}
 
	/**
	 * 获取数字的阴历叫法
	 * @param num 数字
	 * @param isMonth 是否是月份的数字
	 */
	function getCapitalNum($num,$isMonth){
		$isMonth = $isMonth || false;
		$dateHash=array('0'=>'','1'=>'一','2'=>'二','3'=>'三','4'=>'四','5'=>'五','6'=>'六','7'=>'七','8'=>'八','9'=>'九','10'=>'十 ');
		$monthHash=array('0'=>'','1'=>'正月','2'=>'二月','3'=>'三月','4'=>'四月','5'=>'五月','6'=>'六月','7'=>'七月','8'=>'八月','9'=>'九月','10'=>'十月','11'=>'冬月','12'=>'腊月');
		$res='';
 
		if($isMonth){
			$res = $monthHash[$num];
		}else{
			if($num<=10){
				$res = '初'.$dateHash[$num];
			}else if($num>10&&$num<20){
				$res = '十'.$dateHash[$num-10];
			}else if($num==20){
				$res = "二十";
			}else if($num>20&&$num<30){
				$res = "廿".$dateHash[$num-20];
			}else if($num==30){
				$res = "三十";
			}
		}
		return $res;
	}
 
}
?>

FriendOne改版第二版上线

Gavin [未分类]

2010.04.09

FriendOne第一版自上线以来,得到各方网友的热烈反映,同时网友们也提出了很多很好的建议,改动比较大的FriendOne第二版,就是集合了一些用户很常遇到和提到的问题而产生了。

这次改动,主要有两个方向的改动:平台逻辑的调整、应用系统的上线。具体如下:

1、增加了“好友”、“好友分组”等逻辑,使用户在FriendOne上不用过于依赖圈子
2、首页发表默认feed给好友,而不用一定要发到某个圈子
3、首页右上角增加了提示和操作区
4、首页的feed分“好友动态”和“圈子动态”显示
5、圈子分“常去”(亲密)和“其他”(普通)属性展示
6、在全站底部增加了一个bottom bar,放系统通知和在线好友
7、邀请不一定非要邀请某人进某个圈子了
8、个人信息增加了“好友”,增加了“好友分组”的管理,同时隐藏了“圈友”的概念
9、上线了积分系统
10、上线了应用系统,并上线了两个应用:一周心情 和 纪念卡


团队向大家推出免费短网址服务

Gavin [未分类]

2010.03.25

鉴于用户的需求,我们在FriendOne.cn上上线了短网址服务,以便于用户分享链接。同时,考虑到外部用户的需求,我们也独立公开了FriendOne.cn使用的短网址系统:http://xurl.cn/

xurl.cn界面简洁(有百度、Google风范,^_^),使用方便,性能高且稳定,还有API开放(http://xurl.cn/api.htm),便于程序员调用。另外,xurl.cn承诺终身免费,不对使用者收取任何费用。大家可放心使用。

截个图来看看:


FriendOne发布虎年新春版

Gavin [未分类]

2010.02.11

通过FriendOne(朋友一家)团队的通力合作,FriendOne虎年新春版终于和大家见面了。

相比之前的版本,新春版有以下改动或增强:

1、皮肤换成了新春版,FriendOne与您恭贺虎年新春

2、头1000个圈子赠送1000条免费短信,以及赋予更多VIP的权限

3、增加了圈子相册功能

4、增加了圈子举办活动的功能,并能通过邮件、短信通知

5、其他一些BUG的修正

详情请看效果图:


阅读全文»


关于Image的onload事件

John [未分类]

2010.02.09

这两天在做一个相册功能,因为要用js获取图像的宽高,所以用到了Image对象,并在Image对象onload后,获取其宽高。代码大概如下:

 查看代码 JAVASCRIPT
1
2
3
4
5
6
7
var image = new Image();
image.src = 'image.jpg';
image.onload = function() {
  var width = image.width;
  var height = image.height;
  //do something else
};

FF下很正常,但是在IE7下死活不触发onload事件,没错,又是IE。不过这次IE6却很淡定,因为它能正常运行- -其实不触发也还好,但是居然是有时触发有时不触发……弄了半天还是得靠Google啊……
其实只要把src放在onload后面就可以解决问题了:

 查看代码 JAVASCRIPT
1
2
3
4
5
6
7
var image = new Image();
image.onload = function() {
  var width = image.width;
  var height = image.height;
  //do something else
};
image.src = 'image.jpg';

End.