【翻译】在Ext JS和Sencha Touch中创建自定义布局
原文:Creating Custom Layouts in Ext JS and Sencha Touch
布局系统是Sencha框架中最强大和最独特的一部分。布局会处理应用程序中每个组件的大小和位置,因而,不需要手动去管理那些碎片。Ext JS与Sencha Touch的布局类有许多相似之处,最近在 Ivan Jouikov的这篇博文中对他们进行了详细的分析。
虽然是这样,但很多Ext JS和Sencha Touch开发人员可能永远都不会去了解布局系统的机制原理。Sencha框架已经提供了最常用的应用程序布局,因此很少出现应用程序需要额外功能的需求,因而不大会有人愿意去了解布局系统的内部运作。
试想一下,你的公司需要在应用程序中使用3D Carousel来显示界面元素,但没有任何标准的Sencha布局可以提供这种能力,哪怎么来解决这个问题呢?
选择基类
在开发任何Ext JS或Sencha Touch自定义组件的时候,第一步要考虑的总会是应该选择哪一个基类来扩展。在目前这种情况,就是需要使用哪种布局才能容纳3D空间的项目。由于不需要任何特殊功能,只是管理项目,因而,需要从布局继承链最低这方面着手。在当前情况下,Ext.layout.container.Container (Ext JS)和Ext.layout.Default (Sencha Touch) 可以说是最佳选择。
在Ext JS中,由于需要支持旧版浏览器,而不能采用一些现代的CSS功能,如Flexbox,因而布局系统需要手动管理许多大小和定位的计算。这样的后果就是,3D carousel布局需要重写大多数的Ext.layout.container.Container方法,如calculate、getContainerSize和getPositionOffset,以便对它的子元素进行布局。
另一个需要注意的问题是,Ext JS布局在执行“布局”时,每一个“布局”的执行都要管理多个“周期”,例如,盒子布局配置为stretchmax就需要至少两个周期,布局首先要检测每一个子组件的最大尺寸,并在第二周期将所有布局内的子组件扩展为相同的大小。布局的操作会产生大量的“布局”或“周期”(添加或移除多个条目),为了提供性能,可能会希望先暂停布局,在操作完成后再恢复布局。
相比之下,Sencha Touch的Ext.layout.Default则允许浏览器通过CSS去处理布局中大多数项目的定位和尺寸(因为Sencha Touch只支持现代浏览器,而所有这些都已实现CSS Flexbox)。因此,Ext.layout.Default包含了与添加、移除和重新定位子条目的相关的主要方法。
现在,已经了解将使用那些类来扩展新的3D Carousel布局,下面来逐步去实现它。
CSS3转换和其他魔法
为了创建3D Carousel,需要使用一些高级CSS 3D转换,如转换、过渡、rotateX/rotateY和translateZ。CSS 3D转换可以很复制,但总的来说,对于新的Sencha布局需要实现以下事情:
在父容器应用透视和转换(让它看上去是3D的)
在布局内的子组件应用转换(围绕3D形状从他们的边界开始旋转他们)
在父容器内部的DOM元素应用转换(o physically rotate the 3D shape as the user interacts with it)
正如你所预期的,通过Ext JS和Sencha Touch生成的实际的DOM元素会有些许不同,因此,虽然在两个框架中采取的方法是一样的,但产生的CSS还是会有区别。新的3D Carousel布局所需的附加CSS如下(Sencha Touch):
.x-layout-carousel {
-webkit-perspective : 1500px;
-moz-perspective : 1500px;
-o-perspective : 1500px;
perspective : 1500px;
position : relative !important;
}
.x-layout-carousel .x-inner {
-webkit-transform-style : preserve-3d;
-moz-transform-style : preserve-3d;
-o-transform-style : preserve-3d;
transform-style : preserve-3d;
}
.x-layout-carousel.panels-backface-invisible .x-layout-carousel-item {
-webkit-backface-visibility : hidden;
-moz-backface-visibility : hidden;
-o-backface-visibility : hidden;
backface-visibility : hidden;
}
.x-layout-carousel-item {
display : inline-block;
position : absolute !important;
}
.x-layout-carousel-ready .x-layout-carousel-item {
-webkit-transition : opacity 1s, -webkit-transform 1s;
-moz-transition : opacity 1s, -moz-transform 1s;
-o-transition : opacity 1s, -o-transform 1s;
transition : opacity 1s, transform 1s;
}
.x-layout-carousel-ready .x-inner {
-webkit-transition : -webkit-transform 1s;
-moz-transition : -moz-transform 1s;
-o-transition : -o-transform 1s;
transition : transform 1s;
}
为了让Ext JS布局的CSS看上去基本一样,还需要对CSS选择器做点微调。
为了在Sencha Touch和Ext JS中让3D Carousel能响应用户交互,不得不在运行时对一些附加的CSS的进行修改。下面首先要做的是扩展布局的基类,然后研究如何添加交互功能。
扩展布局基类
首先来扩展Sencha Touch的Ext.layout.Default,主要目标是添加一些针对新的3D Carousel的观感的配置项,以及一些布局内部用来正确定位子组件的功能。
初步的扩展如下:
Ext.define('Ext.ux.layout.Carousel', {
extend : 'Ext.layout.Default',
alias : 'layout.carousel',
config : {
/**
* @cfg {number} portalHeight
* Height of the carousel, in pixels
*/
portalHeight : 0,
/**
* @cfg {number} portalWidth
* Width of the carousel, in pixels
*/
portalWidth : 0,
/**
* @cfg {string} direction
* 'horizontal' or 'vertical'
*/
direction : 'horizontal' //or 'vertical'
},
onItemAdd : function () {
this.callParent(arguments);
this.modifyItems();
},
onItemRemove : function () {
this.callParent(arguments);
this.modifyItems();
},
modifyItems : function () {
//calculate child positions, etc
}
});
代码中除了config对象外,还定义了3个方法:onItemAdd、onItemRemove和modifyItems。前啷个方法只是简单的重写Ext.layout.Default的方法,以便在添加或删除子组件后编辑子组件的位置,而modifyItems是一个新的方法,用来计算所需的CSS 3D转换。
在布局指派给他们的容器时,布局系统内部的行为就会一直处于活动状态:
setContainer: function(container) {
var options = {
delegate: '> component'
};
this.dockedItems = [];
this.callSuper(arguments);
container.on('centeredchange', 'onItemCenteredChange', this, options, 'before')
.on('floatingchange', 'onItemFloatingChange', this, options, 'before')
.on('dockedchange', 'onBeforeItemDockedChange', this, options, 'before')
.on('afterdockedchange', 'onAfterItemDockedChange', this, options);
},
对于我们的布局扩展来说,为了做进一步的初始化,还需要加上以下方法:
Ext.define('Ext.ux.layout.Carousel', {
//...
setContainer : function (container) {
var me = this;
me.callParent(arguments);
me.rotation = 0;
me.theta = 0;
switch (Ext.browser.name) {
case 'IE':
me.transformProp = 'msTransform';
break;
case 'Firefox':
me.transformProp = 'MozTransform';
break;
case 'Safari':
case 'Chrome':
me.transformProp = 'WebkitTransform';
break;
case 'Opera':
me.transformProp = 'OTransform';
break;
default:
me.transformProp = 'WebkitTransform';
break;
}
me.container.addCls('x-layout-carousel');
me.container.on('painted', me.onPaintHandler, me, { single : true });
},
onPaintHandler : function () {
var me = this;
//add the "ready" class to set the CSS transition state
me.container.addCls('x-layout-carousel-ready');
//set the drag handler on the underlying DOM
me.container.element.on({
drag : 'onDrag',
dragstart : 'onDragStart',
dragend : 'onDragEnd',
scope : me
});
me.modifyItems();
}
});
在nebulous指派布局容器后,必须等到容器渲染之后才能将事件处理指定到底层的DOM。接下来,为了能让布局管理子组件,还要填补功能之间的缝隙( fill in the functional gaps):
Ext.define('Ext.ux.layout.Carousel', {
//...
modifyItems : function () {
var me = this,
isHorizontal = (me.getDirection().toLowerCase() === 'horizontal'),
ct = me.container,
panelCount = ct.items.getCount(),
panelSize = ct.element.dom[ isHorizontal ? 'offsetWidth' : 'offsetHeight' ],
i = 0,
panel, angle;
me.theta = 360 / panelCount;
me.rotateFn = isHorizontal ? 'rotateY' : 'rotateX';
me.radius = Math.round(( panelSize / 2) / Math.tan(Math.PI / panelCount));
//for each child item in the layout...
for (i; i < panelCount; i++) {
panel = ct.items.getAt(i);
angle = me.theta * i;
panel.addCls('x-layout-carousel-item');
// rotate panel, then push it out in 3D space
panel.element.dom.style[ me.transformProp ] = me.rotateFn + '(' + angle + 'deg) translateZ(' + me.radius + 'px)';
}
// adjust rotation so panels are always flat
me.rotation = Math.round(me.rotation / me.theta) * me.theta;
me.transform();
},
transform : function () {
var me = this,
el = me.container.element,
h = el.dom.offsetHeight,
style= el.dom.style;
// push the carousel back in 3D space, and rotate it
el.down('.x-inner').dom.style[ me.transformProp ] = 'translateZ(-' + me.radius + 'px) ' + me.rotateFn + '(' + me.rotation + 'deg)';
style.margin = parseInt(h / 2, 10) + 'px auto';
style.height = me.getPortalHeight() + 'px';
style.width = me.getPortalWidth() + 'px';
},
rotate : function (increment) {
var me = this;
me.rotation += me.theta * increment * -1;
me.transform();
}
});
在示例中,还栩雅大量的复杂运算来确定每一个子组件的正确位置,以及需要在容器内手动更新CSS的转换值。
最后,还需要添加用来捕获用户与3D Carousel之间交互的事件处理:
Ext.define('Ext.ux.layout.Carousel', {
//...
onDragStart : function () {
this.container.element.dom.style.webkitTransitionDuration = "0s";
},
onDrag : function (e) {
var me = this,
isHorizontal = (me.getDirection().toLowerCase() === 'horizontal'),
delta;
if (isHorizontal) {
delta = -(e.deltaX - e.previousDeltaX) / me.getPortalWidth();
}
else {
delta = (e.deltaY - e.previousDeltaY) / me.getPortalHeight();
}
me.rotate((delta * 10).toFixed());
},
onDragEnd : function () {
this.container.element.dom.style.webkitTransitionDuration = "0.4s";
}
});
事件处理只是简单的评估用户是否已将鼠标拖动到carousel,然后更新CSS过渡。
该类完整的Sencha Touch代码可以在这里下载。而扩展自Ext.layout.container.Container的Ext JS的代码与这个非常类似,但在API上还是有一些小小的区别。Ext JS代码的示例可以在这里下载。
回顾Ext.ux.layout.Carousel
下面化一点点时间来回顾一下发生了什么事。
由于3D Carousel布局只需要继承布局系统的基本功能,因而选择了Sencha Touch的Ext.layout.Default类进行扩展。接下来,添加了onItemAdd、onItemRemove和setContainer等重写方法来添加布局所需的运行配置。最好,实现了一些功能方法和事件处理,以便布局能够管理子组件的位置。
尽管3D Carousel是一个使用Sencha Touch或Ext JS创建的异想天开的例子,但它的重点在于如何在Sencha 应用程序中创建有创意的布局,而这实际上很简单。关键的地方是丫了解如何去初始化布局,以及在这期间发生了什么——其实底层的框架代码并没有你所想象的那样复杂。Sencha Touch和Ext JS的布局系统,虽然在底层会有些许的不同,但实现方法是实际上是一样的。
请注意:这只是一个技术演示,不能保证代码能运行在所有浏览器上。而事实上,所使用的CSS3转换已经意味着排查了一些浏览器,因此请不要将这个应用到生产中。
Other interesting examples of customized layouts include this Sencha Fiddle by Sencha Support engineer Seth Lemmons involving a circle menu, and this video of a custom navigation component by Andrea Cammarata, Sencha Professional Services engineer.
作者:Arthur Kay
Arthur Kay is the Developer Relations Manager at Sencha, Inc. He studied Music and Computer Science at Loyola University Chicago and has been involved with the Web since the late 1990s.
【翻译】在Ext JS和Sencha Touch中创建自定义布局的更多相关文章
- 【翻译】在Ext JS和Sencha Touch中创建自己定义布局
原文:Creating Custom Layouts in Ext JS and Sencha Touch 布局系统是Sencha框架中最强大和最独特的一部分.布局会处理应用程序中每个组件的大小和位置 ...
- 再探 Ext JS 6 (sencha touch/ext升级版) 变化篇 (编译命令、滚动条、控制层、模型层、路由)
从sencha touch 2.4.2升级到ext js 6,cmd版本升级到6.0之后发生了很多变化 首先从cmd说起,cmd 6 中sencha app build package不能使用了,se ...
- 【翻译】在Sencha Touch中创建离线/在线代理
原文:Creating an Online/Offline proxy in Sencha Touch 概述 在Sencha Touch中,一个常见的需求就是,当设备在没有连接互联网的时候,应用程序必 ...
- 【翻译】Ext JS 5.0.1 中的新功能
原文:What's New in Ext JS 5.0.1 今天,我们很高兴的宣布Ext JS 5.0.1发布了!此维护版本基于Sencha社区的反馈做了一些改进.下面让我们来了解一下这些改变. 可访 ...
- 【翻译】为Ext JS和Sencha Touch开发人员准备的应用程序监测(App Inspector)
和其他的Sencha开发人员一样,我会花费大约半天的时间在我喜欢的IDE工具上编写JavaScript,而另一半时间则是在浏览器上测试和调试我的应用程序.在过去几年,每一个主要的浏览器都已大为改善.现 ...
- 初探 Ext JS 6 (sencha touch/ext升级版)
Sencha Touch 现在已全面升级至Ext Js 6,那么我们如何使用他们呢? 首先去官网下载最新的sdk和帮助文档 sdk下载地址:https://www.sencha.com/product ...
- 【翻译】在Ext JS 5应用程序中怎样使用路由
原文:How to Use Routing in Your Ext JS 5 Apps 简单介绍 Ext JS 5是一个重要的公布版本号,它提供了很多新特性来创建丰富的.企业级的Web应用程序.MVV ...
- 【翻译】在Ext JS 5应用程序中如何使用路由
原文:How to Use Routing in Your Ext JS 5 Apps 简介 Ext JS 5是一个重要的发布版本,它提供了许多新特性来创建丰富的.企业级的Web应用程序.MVVM和双 ...
- 【翻译】Ext JS 6 Beta发布
原文:Ext JS 6 Beta is Now Available 概述 Ext JS 6的好处 新的Ext JS功能和工具 需要你的反馈意见 概述 很高兴,Ext JS 6 beta版本现在发布了. ...
随机推荐
- MyBatis 查询映射自定义枚举
背景 MyBatis查询若想映射枚举类型,则需要从 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中选一个来使用 ...
- Dynamics CRM 打开数据加密报错及修改用户邮件保存报错的解决方法
在项目里会碰到在修改用户的电子邮件时报错的问题 然后跑到数据管理里打开数据加密又是报错 解决上述问题只需要做下数据库的更改即可,把标志位置1即可,记得要重启下IIS才能生效 SELECT [Colum ...
- 【SSH系列】Hibernate映射-- 多对一单向关联映射
在hibernate中非常重要的就是映射,在前面的博文中,小编简单的介绍了基本映射,基本映射是对一个实体进行映射,关联映射就是处理多个实体之间的关系,将关联关系映射到数据库中,所谓的关联关系在对象模型 ...
- java集合循环删除
java集合循环删除,java list集合操作,java循环.分享牛,分享牛原创.java集合删除方法. 2.6.1.第一种方式 list.add("1"); list.add( ...
- Linux 高性能服务器编程——Linux服务器程序规范
问题聚焦: 除了网络通信外,服务器程序通常还必须考虑许多其他细节问题,这些细节问题涉及面逛且零碎,而且基本上是模板式的,所以称之为服务器程序规范. 工欲善其事,必先利其器,这篇主要来探 ...
- ELK平台的搭建
ELK是指Elasticsearch + Logstash + Kibaba三个组件的组合.本文讲解一个基于日志文件的ELK平台的搭建过程,有关ELK的原理以及更多其他信息,会在接下来的文章中继续研究 ...
- 详解EBS接口开发之库存事务处理采购接收和退货
(一)接收&退货常用标准表简介 1.1 常用标准表 如下表中列出了与采购接收&退货导入相关的表和说明: 表名 说明 其他信息 RCV_TRANSACTIONS 采购接收事务表 事务 ...
- Android自定义ViewGroup(四、打造自己的布局容器)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51500304 本文出自:[openXu的博客] 目录: 简单实现水平排列效果 自定义Layo ...
- Hazelcast集群原理分析
简介 hazelcast其中一个很重要的应用就是可以将多个应用服务器组成一个分布式环境的应用,形成一个cluster.这个cluster可以选举出一个master来对外工作.而cluster中的各台服 ...
- SQLite 删除表(http://www.w3cschool.cc/sqlite/sqlite-drop-table.html)
SQLite 删除表 SQLite 的 DROP TABLE 语句用来删除表定义及其所有相关数据.索引.触发器.约束和该表的权限规范. 使用此命令时要特别注意,因为一旦一个表被删除,表中所有信息也将永 ...