使用javascript打造富有个性及物理特性的JSF组件。
在1.4中修正并调整了一些QFaces中的js代码之后,继续除bug及增加功能。现在是1.5beta。可以在线看Demo,并且现在效果更好。
打算在1.5中增加一个基本
的ajaxSupport组件或是日历组件并继续调优性能,并增加一个style文件,更好的统一下样式,现在已经慢慢形成自己的风格。在增加功能的
同时考虑实用与性能,用户友好,及可扩展等。在1.4后的一段时间又使我对编程的认识提高了不少,但不只是编程上的,包括很多其它方面的。
好了,继续。QFaces的js函数库最近又增加了不少,这些都是自己慢慢增加上去的,为了打造富有个性的组件,最近又添了两个主要函数。有朋友问为什么不使用ext或者使用其它现成的js
库。说真的,我还没有使用过ext,不是很了解。我喜欢自己写核心的东西,因为只有自己写才能把它玩转得更好。在写不了的情况下,才会借鉴或使
用,在俱备足够知识之后,我喜欢重复造轮子。我并不认为重复造轮子就是错的,即使自己可能造得不好,但并不是绝对没有收获。
新增加的两个主要js函数:
QFaces.accDisplay(id, action, direction, G);
id -> 目标组件id,如某个div,或table的id等。
action -> "show", "hide" 展示方式,默认show, 显示或隐藏目标组件
direction -> "width","height","both" ,展示方向,默认height,可以从水平,垂直,或同时两个方向伸展或收缩。
G -> 重力加速度,原为0.006,在几次调整之后以0.006与高度的比率作为默认, 为什么不是9.8?——因为这里不是地球,是QFaces星球。并且时间也不是以秒为单位,而是毫秒。所以设在0.006左右效果较好。
函数可以让目标组件如“div”有一个富有弹性的展示效果,使用了自由落体的定理,所以在落下及弹起都有一个匀加速及匀减速的过程,对stepArr数组进行设置可以增加效果。 简单的使用方式可以这样: QFaces.accDisplay(id), 其它参数按默认,则将以垂直加速富弹性的方式显示目标组件。
QFaces.facesMakeFrame(id)
这个函数可以给任意组件id添加一个边框,包括div,table,可见的,不可见的等,甚至button,input...这个函数相对简单得多,但是很方便,效果也非常好,在QFaces组件库下可以直接调用,里面捆绑了8张小png作为组件边框,这是我在photoshop中做的图,然后切割而成的,可以很方便的替换,共4个角4个边。如果单独使用,那么需要指定这些图片的位置。并约束一下左上角及右下角的宽高就可以了,。单用CSS也可以做出圆角及阴影效果,但是再怎么做也没有photoshop做出来的效果好。
函数原形:
QFaces.accDisplay = function(id, action, direction, G)
QFaces.accDisplay = function(id, action, direction, G) {
var mydiv = QFaces.getComponent(id);
if (mydiv.locked) {
return;
} else {
mydiv.locked = true; // 锁定source,避免快速无限点击。
}
// 这里表示了从原始宽高度的0倍 -> 运行到原始宽高 -> 然后跳回原来的0.8倍 -> 再回到原来的宽高度。
// 配合自由落体 (S = v0t * 1/2att),看起来就是一个弹跳过程。
var stepArr = new Array();
if (action == "show" || action == null) {
stepArr[0] = 0;
stepArr[1] = 1.2;
stepArr[2] = 0.8;
stepArr[3] = 1;
} else if (action == "hide") {
stepArr[0] = 1;
stepArr[1] = 1.2;
stepArr[2] = 0;
}
// 初始化变量.高度与G的比例 250xp : 0.006S
var size = QFaces.getSize(id);
var stretchObj = new StretchObject();
stretchObj.source = mydiv;
stretchObj.action = (action == null ? "show" : action); // 确定方向
stretchObj.originalLeft = mydiv.offsetLeft;
stretchObj.originalWidth = size[0];
stretchObj.originalHeight = size[1];
stretchObj.stepArr = stepArr;
stretchObj.interval = 10;
// 以一个比率动态改变加速度大小,避免大区块的动画时间过长
// stretchObj.G = 0.006;
stretchObj.G = (stretchObj.originalHeight * 0.006 / 250);
if (direction == null || direction == "height" || direction == "both") {
stretchObj.displayHeight = true;
}
if (direction == "width" || direction == "both") {
stretchObj.displayWidth = true;
}
if (G != null) {stretchObj.G = G;} // 如果用户指定了加速度
stretchObj.init(); // 初始化
travel(stretchObj); // Start display
function travel(obj) {
// 累计单步的执行时间,并计算距离。
obj.stepTimeUsed += obj.interval;
obj.stepDistance = obj.v0 * obj.stepTimeUsed + 0.5 * obj.G * obj.stepTimeUsed * obj.stepTimeUsed;
// 设置source的位置,高宽度。
obj.setSize(obj.stepWidth + obj.stepDistance, obj.stepHeight + obj.stepDistance);
// 运行完毕后解锁source,并跳出。
if (obj.isOver()) {
obj.setSize(obj.originalWidth * obj.stepArr[obj.stepArr.length - 1], obj.originalHeight * obj.stepArr[obj.stepArr.length - 1]);
// 如果是收缩,则清理数据后隐藏,并且设回原始位置
if (obj.action == "hide") {
obj.source.style.left = obj.originalLeft + "px";
obj.source.style.width = "";
obj.source.style.height = "";
obj.source.style.display = "none";
}
obj.source.locked = false;
return;
}
// 单步运行完毕
if (obj.isStepOver()) {
obj.stepOver();
}
QFaces.setTimeout(travel, obj.interval, obj);
}
function StretchObject() {
this.source = null; // 绑定的界面控件
this.action = null;
this.originalLeft = null; // 原始的位置偏移
this.originalWidth = null; // 原始宽度
this.originalHeight = null; // 原始高度
this.displayWidth = false;
this.displayHeight = false;
this.stepArr = null; // 要运行的数组
this.v0 = null;
this.v1 = null;
this.step = 1;
this.stepWidth = 0; // 步宽,每次方向改变时都会改变。
this.stepHeight = 0; // 同上
this.stepTimeTotal = 0; // 每次落下或弹起所需要的总时间。
this.stepTimeUsed = 0; // 每次落下或弹起的时间累计。
this.stepDistance = 0; // 在stepTimeUsed下所运行的距离
this.G = 0.006; // 重力加速度,为什么不是9.8米/秒?因为这里不是地球 ————是QFaces星球
this.interval = 10;
this.isOver = function() {
return (this.step > this.stepArr.length - 1);
}
this.isZoomOut = function() {
return (this.stepArr[this.step] > this.stepArr[this.step - 1]);
}
this.isStepOver = function() {
if (this.stepTimeUsed >= this.stepTimeTotal) {return true;}// 这里防止无限止伸缩。
var currentHeight = this.stepHeight + this.stepDistance;
if (this.isZoomOut()) {
return currentHeight >= this.originalHeight * this.stepArr[this.step];
} else {
return currentHeight <= this.originalHeight * this.stepArr[this.step];
}
}
this.setSize = function(width, height) {
if (this.displayWidth && width >= 0)
this.source.style.width = width + "px";
if (this.displayHeight && height >= 0)
this.source.style.height = height + "px";
if (this.displayWidth && width >= 0) {// 重新设置source的位置偏移。
this.source.style.left = (this.originalLeft + (this.originalWidth - width) * 0.5) + "px";
}
}
this.calculateStepTimeTotal = function() {
if (this.stepArr[this.step] != null) {
var nextS = Math.abs(this.originalHeight * (this.stepArr[this.step] - this.stepArr[this.step - 1]));
if (this.isZoomOut()) { // 下降过程 s = 0.5gtt;
this.stepTimeTotal = Math.sqrt(2 * nextS / this.G);
} else {// G = (v1 - v0)/t
this.stepTimeTotal = Math.abs((this.v0 - this.v1) / this.G);
}
} else {
this.stepTimeTotal = 0;
}
}
this.stepOver = function() {
var isZoomOut_previous = this.isZoomOut();
this.stepWidth = this.originalWidth * this.stepArr[this.step];
this.stepHeight = this.originalHeight * this.stepArr[this.step];
this.setSize(this.stepWidth, this.stepHeight);
this.step += 1; // 进入下一阶段
this.stepDistance = 0;
this.stepTimeUsed = 0;
// 特别情况(渐缩):如果前一阶段是下降,并且整个过程是渐缩 。 (s = v0t + 0.5gtt)
if (this.action == "hide") {
if (isZoomOut_previous) {
var zoomInS = Math.abs(this.originalHeight * (this.stepArr[this.step] - this.stepArr[this.step - 1]));
var zoomInT = Math.sqrt(2 * zoomInS / this.G);
this.v0 = this.G * zoomInT; // 用整段最长距离计算从最大位置缩回none时的初速度,瞬速。
this.v0 = this.v0 * -1; // 上升过程,v0必须是负的
this.v1 = 0;
}
} else {// 正常渐展开的过程。
if (isZoomOut_previous) {
this.v0 = this.v1 * -1; // 在落地返弹瞬间,速度达最大,并且末速度变成反弹之后的始速度
this.v1 = 0; // 下一阶段的反弹末速度为0
} else {// 从上升到下降
// 计算下一下降过程需要经过的距离。
var tempS = Math.abs(this.originalHeight * (this.stepArr[this.step] - this.stepArr[this.step - 1]));
var tempT = parseInt(Math.sqrt((2 * tempS / this.G))); // 计算下降需要经过的时间。
var tempGT = this.G * tempT; // 计算末速度。
this.v0 = 0;
this.v1 = tempGT;
}
}
// 计算下一段所需要的时间
this.calculateStepTimeTotal();
}
this.init = function() {
this.source.style.display = "block";
this.source.style.overflow = "hidden";
this.source.style.position = "absolute";
this.source.style.zIndex = QFaces.zIndex++;
this.stepWidth = this.originalWidth * this.stepArr[this.step - 1];
this.stepHeight = this.originalHeight * this.stepArr[this.step - 1];
this.originalLeft = this.source.offsetLeft; // 必须保存原始偏移。
if (this.displayWidth)
this.source.style.width = this.stepWidth + "px";
if (this.displayHeight)
this.source.style.height = this.stepHeight + "px";
var s = this.originalHeight * (this.stepArr[this.step] - this.stepArr[this.step - 1]);
if (this.isZoomOut()) { // 加速
this.v0 = 0;
this.v1 = this.G * parseInt(Math.sqrt((2 * s / this.G)));
} else { // 减速
this.v0 = this.G * parseInt(Math.sqrt(Math.abs(2 * s / this.G))) * -1;
this.v1 = 0;
}
// 计算下一级所需要的时间.
this.calculateStepTimeTotal();
}
}
}
QFaces.facesMakeFrame = function(id)
QFaces.facesMakeFrame = function(id) {
var sourceObj = ((typeof id) == "string") ? QFaces.getComponent(id) : id;
QFaces.assertNull(sourceObj, "facesMakeFrame : Object not found " + id);
var sourceTemp = QFaces.getComponent(sourceObj.id); // sourceTemp == null 表明组件仍未被加入到组件树中。
var parentObj = sourceTemp != null ? sourceTemp.parentNode : (document.body ? document.body : document.documentElement);
var targetObj = QFaces.ele("div");
var src_0_0 = QFaces.getComponent("name.huliqing.qfaces.images.frame_top_left").value;
var src_0_1 = QFaces.getComponent("name.huliqing.qfaces.images.frame_top_center").value;
var src_0_2 = QFaces.getComponent("name.huliqing.qfaces.images.frame_top_right").value;
var src_1_0 = QFaces.getComponent("name.huliqing.qfaces.images.frame_left").value;
var src_1_2 = QFaces.getComponent("name.huliqing.qfaces.images.frame_right").value;
var src_2_0 = QFaces.getComponent("name.huliqing.qfaces.images.frame_bottom_left").value;
var src_2_1 = QFaces.getComponent("name.huliqing.qfaces.images.frame_bottom_center").value;
var src_2_2 = QFaces.getComponent("name.huliqing.qfaces.images.frame_bottom_right").value;
var img_0_0 = QFaces.ele("img");
var img_0_2 = QFaces.ele("img");
var img_2_0 = QFaces.ele("img");
var img_2_2 = QFaces.ele("img");
var _table = QFaces.ele("table");
var _arr = QFaces.eleTable(_table, 3, 3); // 创建一个3,3表格
img_0_0.setAttribute("src", src_0_0);img_0_0.style.width = img_0_0.style.height = "100%";
img_0_2.setAttribute("src", src_0_2);img_0_2.style.width = img_0_2.style.height = "100%";
img_2_0.setAttribute("src", src_2_0);img_2_0.style.width = img_2_0.style.height = "100%";
img_2_2.setAttribute("src", src_2_2);img_2_2.style.width = img_2_2.style.height = "100%";
sourceObj.style.display = "block"; // 这里必须显示出来,否则在包装后可能看不到正确的大小
_arr[0][0].appendChild(img_0_0);
_arr[0][1].style.cssText = "background:url('" + src_0_1 + "') repeat-x";
_arr[0][2].appendChild(img_0_2);
_arr[1][0].style.cssText = "background:url('" + src_1_0 + "') repeat-y";
_arr[1][1].appendChild(sourceObj); _arr[1][1].style.background = sourceObj.style.background; // 让背景色互相匹配。
_arr[1][2].style.cssText = "background:url('" + src_1_2 + "') repeat-y";
_arr[2][0].appendChild(img_2_0);
_arr[2][1].style.cssText = "background:url('" + src_2_1 + "') repeat-x";
_arr[2][2].appendChild(img_2_2);
_arr[0][0].style.cssText = "width:25px;height:22px"; // 约速左上角列的宽高度
_arr[2][2].style.cssText = "width:25px;height:25px;";// 约速右下角
_table.style.border = _table.style.padding = _table.style.margin = _table.cellSpacing = _table.cellPadding = "0";
_table.style.height = "100%"; // 当targetObj有伸缩效果时,让table也跟着自动改变
_table.setAttribute("id", sourceObj.id + ":frame:table");
targetObj.setAttribute("id", sourceObj.id + ":frame");
targetObj.appendChild(_table);
parentObj.appendChild(targetObj);// 必要的,把新组件加到原始父组件下,或是document.body下
return targetObj;
}
这两个函数都在IE6,7,8,FireFox3,
Chrmoe下测试通过,其它的没有测试。另人意外的是chrmoe的性能非常好,从网页装载或js上的速度都是最快的。性能比较如下:Chrmoe > FireFox > IE,
IE6下的效果及性能是最差的。IE7,8也没有好到哪里去。IE8标准模式存在Bug,IE8的兼容模式还可以。FireFox的友好程度最高。Chrmoe可以继续体验,性能感觉很好。
另外QFaces.js新增了不少其它辅助函数。以下是两个使用了该函数的组件,现在组件有了浮动的阴影,能完美随意拖动,并且富有弹性的伸缩展示(在1.4中是渐隐渐现的展示效果)可以看到加了边框的效果比1.4好了很多。
在线演示:
http://huliqing-qfaces.appspot.com/qfaces-example/ui-tree.faces
http://huliqing-qfaces.appspot.com/qfaces-example/ui-saveState.faces
- huliqing@huliqing.name
- http://www.huliqing.name