js部分
/** * 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部分
<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>