/**
* 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>