/**
* native
* 拥有native app的基本操作功能
* @author 一只柯楠
* 1、动画切换页面
* 2、以HASH为线轴,/分开一个级别
* 3、
*/
(function () {
var initializing = false,
superTest = /一只柯楠/.test(function () {一只柯楠;}) ? /\b_super\b/ : /.*/;
// 临时Class
this.Class = function () {};
// 继承方法extend
Class.extend = function (prop) {
var _super = this.prototype;
//创建一个实例,但不执行init
initializing = true;
var prototype = new this();
initializing = false;
for (var name in prop) {
// 用闭包保证多级继承不会污染
prototype[name] = (typeof prop[name] === 'function' && typeof _super[name] === 'function' && superTest.test(prop[name])) ? (function (name, fn) {
return function () {
var temp = this._super;
// 当前子类通过_super继承父类
this._super = _super[name];
//继承方法执行完毕后还原
var ret = fn.apply(this, arguments);
this._super = temp;
return ret;
}
})(name, prop[name]) : prop[name];
}
//真实的constructor
function Class () {
if (!initializing && this.init) {
this.init.apply(this, arguments);
}
}
Class.prototype = prototype;
Class.constructor = Class;
Class.extend = arguments.callee;
return Class;
}
})();
/**
* @top prototype
*
*/
(function(Bakbone){
this.iNative= {
version: 1.0,
};
var $= Backbone.$,
config= {
debug: false,
},
/*
* 顶级基础类,继承Backbone.View.prototype
*/
Browser= Class.extend( _.extend(Backbone.View.prototype, {
init: function(el, options){
//root元素节点
if(options){
this.el= $(el)[0];
}else if(el){
if(el instanceof Element=== true || el instanceof $ === true || _.isString(el))
this.el= $(el)[0];
else
options= el;
}
this._configure(options || {});
//拿到view外围元素this.$el
this._ensureElement();
//需要在init里每次都用新的,不能直接挂在外面,会被重用
this.children= [];
//代理事件
this.delegateEvents();
},
parentFrame: null,
tagName: 'div',
sleep: true,
pageshow: function(){
console.log('pageshow', this)
},
pagehide: function(){
console.log('pagehide', this)
},
//销毁对象,所有需要销毁的动作都在这里执行
destroy: function(retain) {
var me = this;
//触发删除事件
me.trigger('destroy');
//解绑所有元素的事件
me.$el.find('*').off();
//删除元素
if(!remove)
$this.$el.remove();
me.__proto__ = null;
_.each(me, function(i, key){
delete me[key];
});
}
})),
/*
* 浏览器类,window和page公用
*/
defChild= {
//scrollTop距离
pageY: 0,
//最后一次的hash
hash: [],
},
TopWindow= Browser.extend({
init: function(el, options){
this._super(el, options);
this._bindChild();
/*
var cur, wind;
cur= wind= this;
while(parent){
wind= cur;
cur= cur.parent;
}
this.wind= wind;
*/
},
//index/test/
currentChild: null,
previousChild: null,
childChild: false,
currentHash: [],
previousHash: [],
/*
* 执行本身的pageshow, 判断当前子控制器是否发生切换并执行
*/
_pageshow: function(hash){
var me= this;
me.previousHash= me.currentHash;
me.currentHash= hash;
//唤醒自己
this.sleep= false;
//使用第二节判断hash[1]
this.checkChild(hash[1], function(val, key){
if(_.isFunction(val.frame)) val.frame= new val.frame;
//仅在发生改变时才切换当前子节点
if(me.currentChild!==val){
if(me.currentChild!== null){
me.childChange= true;
me.setPreviour(me.currentChild);
}
me.setCurrent(val);
}else{
me.childChange= false;
}
});
hash= hash.join('');
this.pageshow(hash);
this.trigger('pageshow', {hash: hash});
},
_pagehide: function(hash){
var previousChild, frame;
//让上一个子控制器睡眠
if(this.childChange){
frame= this.previousChild.frame;
frame._pagehide(hash);
hash= hash.join('');
frame.trigger('pagehide', {hash: hash});
}
},
setCurrent: function(child, hash){
this.currentChild= child;
},
setPreviour: function(child){
this.previousChild= child;
},
checkChild: function(hash, callback){
var me= this;
return _.some(me.children, function(val, key){
if(val.format.test(hash)){
callback(val, key);
//停止遍历
return true;
}
});
},
addChild: function(route, name, callback){
if (!_.isRegExp(route)) route = router._routeToRegExp(route);
if (_.isFunction(name) || name instanceof Browser) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
//告知父级控制器
if(_.isFunction(callback))
callback.prototype.parentFrame= this;
else
callback.parentFrame= this;
//前面加一个对象是为了不把变量加载defChild上
this.children.unshift(_.extend({}, defChild, {
//匹配正则
format: route,
//控制器
frame: callback
}));
},
_bindChild: function(){
if (!this.routes) return;
this.routes = _.result(this, 'routes');
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.addChild(route, this.routes[route]);
}
},
destroy: function(retain){
this._super(retain);
}
}),
/*
*window类
*/
iWindow= TopWindow.extend({
init: function(el, options){
this._super(el, options);
var me= this, $root= this.$el, html='';
this.$el.css({
'-webkit-box-orient': 'horizontal'
});
_.each(this.children,function(val, key){
html+= '<div style="display:none;width:100%;-webkit-transform: translate3d(0px, 0px, 0px);" ></div>';
});
var record=0,
$toNode,
$fromNode,
events= 'touchstart'+($.browser.android ? '' : ' scroll'),
$doc= $(document),
resizePos= function(){
$doc.off(events, resizePos);
$toNode.css({
"-webkit-transform": "translate3d(0px, 0px, 0px)",
marginTop: 0
})
//滚动到对应位置,如果露出地址栏,则不滚动
if(me.currentChild.pageY || pageYOffset){
scrollTo(0, me.currentChild.pageY);
}
};
me.$docNodes= $(html).appendTo($root).on('webkitTransitionEnd', function(){
var $this= $(this);
if(me.currentChild.frame.el===this.firstElementChild){
$toNode= $this;
$doc.on(events, resizePos)
}else{
$fromNode= $this;
}
//触发pageshow和pagehide,为了保证pageshow在pagehide之前发生,加record来判断
if((++record)===2){
$toNode.css({
'pointer-events': 'auto',
'position': 'static',
'-webkit-transition': 'none'
})
$fromNode.css({
'pointer-events': 'none',
'display': 'none',
'position': 'static',
'-webkit-transition': 'none'
})
record= 0;
me.currentChild.frame._pageshow(me.currentChild.hash);
me._pagehide(me.currentChild.hash);
}
});
handler=function(){
$root.height(1000);
scrollTo(0, 1);
setTimeout(function(){
me.trigger('hidebar', { //hidebar事件
height: innerHeight
});
var css= {'min-height': innerHeight};
// me.$docNodes.css(css);
css.height='auto';
$root.css(css);
}, 0)
};
$(window).on('load orientation' ,handler)
Backbone.history.start();
},
hideAddressBar: true,
toFrame: function(frame){
var curIndex= _.indexOf(this.children, frame),
preIndex= _.indexOf(this.children, this.previousChild),
toNode= this.$docNodes.eq(curIndex),
fromNode= this.$docNodes.eq(preIndex),
transferTime= frame.frame.transferTime,
dir= curIndex> preIndex ? 1 : -1, me= this,
//记住page切走之前的位置
pageY= pageYOffset;
// 将页面嵌入
if(!toNode[0].firstElementChild)
toNode.append(frame.frame.$el);
//显示出来
toNode.css({'display':'block', 'position': 'absolute'});
//如果没有上一个页面则直接显示
if(!me.childChange){
//同类page,不同子page,直接发生下级触发pageshow
toNode.css({'position': 'static'});
return this.currentChild.frame._pageshow(this.currentChild.hash);
}
//离开的页面记住距离顶部的位置
this.previousChild.pageY= pageY;
//把当前的page设置不占位
fromNode.css({'position': 'absolute'});
//要进入的page位置左边或者右边
toNode.css({
// "-webkit-transform": "translate3d("+(dir*100)+"%, "+(pageY- frame.pageY)+"px, 0px)",
"-webkit-transform": "translate3d("+(dir*100)+"%, 0px, 0px)",
marginTop: pageY- frame.pageY
});
//延时0s开始动画
setTimeout(function(){
var css= {
"-webkit-transition-property": "-webkit-transform",
// "-webkit-transform": "translate3d(0px, "+(pageY- frame.pageY)+"px, 0px)",
"-webkit-transform": "translate3d(0px, 0px, 0px)",
"-webkit-transition-duration": transferTime+"ms",
"-webkit-transition-timing-function": "ease-out",
marginTop: pageY- frame.pageY
};
//退出去
toNode.css(css);
css["-webkit-transform"]="translate3d("+(-dir*100)+"%, 0px, 0px)";
//进入
delete css.marginTop;
fromNode.css(css);
if(transferTime==0){
toNode.trigger('webkitTransitionEnd');
fromNode.trigger('webkitTransitionEnd');
}
}, $.browser.android ? 100 : 0)
},
setNodePos: function(ele, left, top){
var style= ele.style;
style.webkitTransform= style.webkitTransform.replace(/translate3d.+$/, 'translate3d\('+left+', '+top+', 0px)');
return this;
},
/*
*给window控制器添加页面
*/
addChild: function(route, name, callback){
this._super.apply(this, arguments);
var child= this.children[0];
var me= this;
route= child.format;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
//unshift一个空内容,因为没有父级别,判断child时,会使用hash[1]
//
(hash= fragment.split('/')).unshift('')
me._pageshow.call(me, hash);
router.trigger('route', name, args);
});
},
/*
* 执行子控制器的pageshow和hide
*/
_pageshow: function(hash){
this._super.apply(this, arguments);
//去除第一级别
this.currentChild.hash= hash.slice(1);
this.toFrame(this.currentChild);
}
}),
iPage= TopWindow.extend({
init: function(el, options){
this._super(el, options);
},
transferTime: 0,
_pageshow: function(hash){
this._super.apply(this, arguments);
var childHash=hash.slice(1);
if(this.currentChild){
this.currentChild.hash= childHash;
this.currentChild.frame._pageshow(childHash);
}
},
_pagehide: function(hash){
//睡眠
this.sleep= true;
this.pagehide(hash);
this.trigger('pagehide', {hash: hash});
this._super.apply(this, arguments);
},
});
var Router= Backbone.Router.extend({}),
router= new Router;
iNative.iWindow= iWindow;
iNative.iPage= iPage;
})(Backbone);
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1"/>
<meta name="format-detection" content="telephone=no" />
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
<title>n.js by conan</title>
<link rel="stylesheet" href="css/index.css" />
<script src="script/underscore.js" type="text/javascript"></script>
<script src="script/zepto.js" type="text/javascript"></script>
<script src="script/backbone.js" type="text/javascript"></script>
<script src="script/n.js" type="text/javascript"></script>
<style type="text/css">
#root>div #index_view{
background: yellow;
width: 100%;
height:600px;
}
#channel_view{
width: 100%;
height:500px;;
background: blue;
}
.title{
height: 60px;
line-height: 60px;
text-align: center;
background: #dedede;
border-bottom: 2px #ddd solid;
}
.title>a{
position: absolute;
left: 0px;
top: 10px;
height: 40px;
line-height: 40px;
padding: 0 22px;
font-size: 14px;
}
.content>a{
display: block;
height: 60px;
margin: 200px 0;
border-bottom: 2px #ddd solid;
border-top: 2px #eee solid;
text-align: center;
line-height: 60px
}
.index,.channel,
.tvshow,.movie{
display: none;
}
hr{
margin: 20px 0;
}
</style>
</head>
<body>
<div id="root">
</div>
<div class="index">
<h1 class="title">首页</h1>
<div class="content">
<a href="#channel/tvshow?zn=123">去看tvshow</a>
<a href="#channel/movie?zn=456">去movie</a>
<div class="status"></div>
</div>
</div>
<div class="channel">
<h1 class="title"><a href="#index?fr=channel">返回首页</a>频道页</h1>
<div class="movie">
这里是movie
<hr />
这里是movie
<hr />
这里是movie
这里是movie
<hr />
这里是movie
<hr />
这里是movie
这里是movie
<hr />
这里是movie
<hr />
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
这里是movie
这里是movie
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<hr />
这里是movie
<hr />
这里是movie
<hr />
<div class="status"></div>
</div>
<div class="tvshow">
这里是tvshow
<hr />
<hr />
<div calss="asdf">
这这里是tvshow</div>
<hr />
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
<div calss="asdf">
这这里是tvshow</div>
这这里是tvshow</div>
这里是tvshow
<hr />
这这里是tvshow
<hr />
这里是tvshow
<hr />
这里是tvshow
<hr />
这这里是tvshow
<hr />
这里是tvshow
<hr />
<hr />
这里是tvshow
<hr />
这这里是tvshow
<hr />
这里是tvshow
<hr />
<hr />
这里是tvshow
<hr />
这这里是tvshow
<hr />
这里是tvshow
<hr />
<div class="status"></div>
</div>
</div>
<script>
(function(window){
var index = iNative.iPage.extend({
id: 'index_view',
el: $('.index'),
transferTime: 300,
pageshow: function(hash){
this.$('.status').html('big--'+hash);
this.$el.show();
},
pagehide: function(hash){
}
}),
Movie= iNative.iPage.extend({
init: function(){
this._super.apply(this, arguments);
},
id: 'movie',
el: $('.movie'),
pageshow: function(hash){
console.log(this.id, 'pageshow');
this.$('.status').html('little--'+hash);
this.$el.show();
},
pagehide: function(hash){
console.log(this.id, 'pagehide');
}
}),
Tvshow= Movie.extend({
id: 'tvshow',
el: $('.tvshow')
}),
channel= index.extend({
id: 'channel_view',
el: $('.channel'),
routes: {
'movie*page': 'movie',
// 'tvshow*page': 'tvshow'
},
movie: Movie,
// tvshow: Tvshow,
pageshow: function(hash){
this._super.apply(this, arguments);
this.addChild('tvshow*page',Tvshow);
this.$el.show();
if(this.previousChild) this.previousChild.frame.$el.hide();
}
}),
iwindow= iNative.iWindow.extend({
routes: {
'channel*page': 'channel',
'*page': 'index',
},
index: index,
pageshow: function(){},
pagehide: function(){},
channel: channel,
});
app= new iwindow('#root')
})(window)
</script>
</body>
</html>