十一月 29, 2011
by 小寒
1 comment
现在很多交互都需要这样一个效果,在拖拽结束时,被拖拽的元素会因为惯性向前移动一段距离。接下来说一下如何实现这个效果。
这个运动的原理其实很简单,就是一个匀减速运动。匀减速运动也就是匀加速运动的一种,只是加速度的方向和速度方向符号相反。我们在中学的时候学过匀加速运动的公式为:S = vt + (1/2)at^2;用文字表达就是在t时刻,物体移动的距离S为:初始速度v * t + (1/2) * 加速度a * t的平方。在这个公式中,我们可以将加速度a和初始速度v视为常量,将t当做变量,就可以知道在t时刻物体移动的距离了。那么问题又转到了如何对a和v求解。加速度a在这个问题中其实只是一个系数而已,我们可以设定a为1秒钟减少10000像素/秒的运动速度。(a = 10000)。那么到目前为止只有初始速度v是未知的了。
在这个动画中初始速度为我拖拽时松开鼠标那一刻的瞬时速度。我们知道计算平均速度的公式为v = s / t,如果这个t你取得足够的小,那么在t时间段移动的距离s除以t就是物体在某一点上的瞬时速度。接下来的问题就是如何确定这个t,以及如何确定在t时间段中物体移动的距离s。
我们知道在移动鼠标的时候会触发mousemove事件,而在持续移动鼠标的过程中,前后相邻的两个mousemove事件的时间间隔非常小,也就符合我们上面说的t时间段,在主流浏览器中event对象会有个timeStamp属性来标识这个事件是什么时刻发生的(在IE中因为要用window.event来取得事件,window.event没有timeStamp属性,所以要用new Date来计算事件发生的时刻),那么对t的求解就变得显而易见了:var t = e1.timeStamp – e0.timeStamp; 同时在t时间段移动的距离在X轴方向上为var s = e1.clientX – e1.clientX;
有了上面的思路,在鼠标松开的瞬时速度也就变得迎刃而解了。鼠标松开的瞬时速度v即为(e1.timeStamp – e0.timeStamp) / (e1.clientX – e1.clientX); e1为最后一次mousemove事件,e0为倒数第二次mousemove事件。如何保存e1和e0可以用以下代码:
var tracker = [];
document.body.addEventListener('mousemove', function(e) {
tracker.push(e);
if(tracker.length > 2) {
tracker.shift();
}
});
待续……
JavaScript
十月 12, 2011
by 小寒
0 comments
IE浏览器用userData,主流浏览器用LocalStorage来解决本地存储的需求。userData存储的数据对于同一目录下的地址是可见的,如http://www.zhoumingzhi.com/1/foo.html可以访问到http://www.zhoumingzhi.com/1/bar.html存的数据。而LocalStorage存储的数据对相同域名下的所有页面都是可见的。
var localStorageAdapter = {
storeName: 'NTESBBS',
isLocalStorage: window.localStorage? true: false,
dataDOM: this.isLocalStorage? null: (function() {
try{
var dataDOM = document.createElement('input'),
expires = new Date();
dataDOM.type = 'hidden';
dataDOM.style.display = 'none';
dataDOM.addBehavior('#default#userData');
document.body.appendChild(dataDOM);
expires.setDate(expires.getDate() + 30);
dataDOM.expires = expires.toUTCString();
return dataDOM;
} catch(ex) {
return null;
}
})(),
set: function(key, value) {
var dataDOM = this.dataDOM;
if(this.isLocalStorage) {
window.localStorage.setItem(key, value);
} else {
if(dataDOM) {
dataDOM.load(this.storeName);
dataDOM.setAttribute(key, value);
dataDOM.save(this.storeName);
}
}
},
get: function(key) {
var dataDOM = this.dataDOM;
if(this.isLocalStorage) {
return window.localStorage.getItem(key);
} else {
if(dataDOM) {
dataDOM.load(this.storeName);
return dataDOM.getAttribute(key);
}
}
},
remove: function(key) {
var dataDOM = this.dataDOM;
if(this.isLocalStorage) {
window.localStorage.removeItem(key);
} else {
if(dataDOM) {
dataDOM.load(this.storeName);
dataDOM.removeAttribute(key);
dataDOM.save(this.storeName);
}
}
}
}
JavaScript
七月 18, 2011
by 小寒
2 comments
在高级浏览器下点击Google+的一些链接,并不是直接从服务器返回完整的页面,而是通过AJAX刷新页面的局部。
如在首页点击以下链接:
- https://plus.google.com/stream
- https://plus.google.com/photos
- https://plus.google.com/photos
- https://plus.google.com/circles
浏览器通过AJAX请求服务器返回数据,动态刷新页面上的一部分区域,这样做可以减少网络传输,快速响应,以达到更好的用户体验。同时我们注意 到,点击链接的时候,浏览器上的地址栏也做出了相应的调整,并且不会刷新整个页面。如点击“圈子”的时候,地址会变为 https://plus.google.com/circles。
这在早期浏览器版本下是做不到的,因为给window.location赋值会导致整个页面的刷新,除非是用改变URL的hash达到同样的目 的。Google+是用HTML5中history.pushState来实现替换当前的URL同时不刷新整个页面。
关于history.pushState的官方文档:https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
Google+针对不同的操作场景和浏览器,做了一些方案处理点击链接后的行为。
- 如果直接在浏览器地址栏中输入地址回车,那么就会从服务器拿完整的页面呈现给用户。
- 如果浏览器不支持history.pushState,会降级用url hash的方式。
- 如果浏览器支持history.pushState,就用AJAX取区块的数据,然后生成HTML刷新到相应的区域上,并调用 history.pushState更新地址栏,生成访问历史。
值得注意的是,Google+对AJAX返回的数据做了缓存处理,再次请求的话会从缓存中获取数据。
JavaScript
七月 5, 2011
by 小寒
0 comments
在MAC机上装了一个YUIBuilder,压缩带中文注释的文件总是出现乱码问题。跟踪了一下,发现是ant执行io操作的时候输出编码不对。解决方法是在环境变量中加入:
ANT_OPTS="-Dfile.encoding=UTF-8"
JavaScript, 前端相关
六月 20, 2011
by 小寒
2 comments
用nodejs当websocket server时,当客户端传输的数据量比较大时,数据会分片到达服务器,这时socket的data处理函数接收到的只是整个数据中的一部分,因此我们要将分片的数据拼合起来再做处理。当最后的数据到达时,数据的末尾是\ufffd,所以可以靠判断数据中是否包含\ufffd来指示整个数据是否已经全部接收。
var message = '';
socket.on('data', function(chunk) {
message += chunk;
var index = message.indexOf('\ufffd');
if(index !== -1) {
console.log(message.slice(0, index));
// console.log(message.slice(1, index));
// Do your logic here
message = message.slice(index + 1);
}
});
JavaScript
六月 11, 2011
by 小寒
1 comment
Python中的装饰器本质上是一个高阶函数,它接收一个函数,并且对这个函数进行包装,从而改变原函数的默认行为。
@deco1(deco_arg)
@deco2
def func(): pass
相当于
func = deco1(deco_arg)(deco2(func))
Demo如下:
# -*-coding:utf-8 -*-
def decorator(str):
print str
def retfn(fn):
def retfn(*arg):
print '已装饰' # 装饰
return fn(*arg) # 调用原始函数
return retfn
return retfn
@decorator("实例化装饰器")
def test(str0, str1):
print str0, str1
test('Hello', 'world');
Python
五月 10, 2011
by 小寒
0 comments
工作中碰到一个变态的性能问题。CMS中有个页面,上面有个多选框,其中有14000个选项。页面中提供给用户一个按钮,点击这个按钮时要清除已选中状态。原有的代码是这样的:
function re() {
for (var i = 0; i < document.form1.totopicid.options.length; i++) {
document.form1.totopicid.options[i].selected = false;
}
}
先抛开循环时多次计算length的问题不谈,光是执行14000次的document.form1.totopicid.options[i].selected = false;就要用户等很长时间。其实有一种更快捷的方式去做这件事情。代码如下:
function re() {
var select = document.form1.totopicid;
select.selectedIndex = 0;
select.options[0].selected = false;
}
这样不仅代码量小,而且性能也能得到很大提升。
JavaScript
五月 9, 2011
by 小寒
2 comments
网上抄来的,找不到原文链接了。望告知。
// ----------------------------------------------------------
// A short snippet for detecting versions of IE in JavaScript
// without resorting to user-agent sniffing
// ----------------------------------------------------------
// If you're not in IE (or IE version is less than 5) then:
// ie === undefined
// If you're in IE (>=5) then you can determine which version:
// ie === 7; // IE7
// Thus, to detect IE:
// if (ie) {}
// And to detect the version:
// ie === 6 // IE6
// ie > 7 // IE8, IE9 ...
// ie < 9 // Anything less than IE9
// ----------------------------------------------------------
// UPDATE: Now using Live NodeList idea from @jdalton
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
}());
JavaScript
四月 12, 2011
by 小寒
0 comments
// IE
//var sel = document.selection,
// range = sel.createrange();
//alert(range.getboundingclientrect().top);
// FF, Chrome
//var sel = window.getselection(),
// range = sel.getrangeat(0);
//alert(range.getboundingclientrect().top);
JavaScript
三月 16, 2011
by 小寒
2 comments
有时要处理一个DOM节点下面的大量子节点,比如对一个ul一次性插入几百个li,或者清空一个ul下面的所有li,或者是替换掉ul下面的所有内容。本文记录一下最佳的实践方式。
插入子节点
用innerHTML的方式要优于appendChild,但切记不要在循环里用innerHTML += “str”这样的方式,可以先把字符串拼接好,最后再赋值给innerHTML。
替换全部子节点
用replaceChild的方式要优于直接给innerHTML赋值。在firefox下,如果一个元素有大量子节点,给这个元素的innerHTML赋值将会十分的慢,目前最好的办法是Clone这个元素得到新元素,然后给新元素的innerHTML赋值,最后用新元素替换掉旧元素。代码如下:
var str = '',
test = document.getElementById('test'),
newUL = test.cloneNode(false);
for(var i = 0; i < iteration; i++) {
str += '<li>Test</li>';
}
newUL.innerHTML = str;
test.parentNode.replaceChild(newUL, test);
清除全部子节点
用replaceChild的方式要优于innerHTML = ”,道理同“替换全部子节点”。不要用removeChild循环清空一个元素下面的所有子节点,这种方式在IE 8下非常慢。代码如下:
var test = document.getElementById('test'),
newUL = test.cloneNode(false);
test.parentNode.replaceChild(newUL, test);
JavaScript