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>