背景
在做消费者安全中心这个项目的时候,有个想法是在页面切换的时候做些转场动画,使得更像原生体验。这个项目是基于Backbone来做的,熟悉Backbone的都知道,框架本身没有提供什么转场动画,市面上也没看到有人分享过什么插件实现这个效果,后来和小伙伴们研究出一个实现办法,分享出来给大家。
效果
最终实现效果如下
可在手机淘宝“我的淘宝->设置->账号与安全->安全中心”
打开或使用手机淘宝扫下面二维码
实现
要想实现转场,首先考虑在哪个时机进行?一开始我想到肯定是在View切换的时候,我来看看路由如何控制View切换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| export default Backbone.Router.extend({ currentView: null, routes: { '': 'home', 'list': 'list' }, changeView(view) { if (this.currentView !== null) { this.currentView.undelegateEvents(); } this.currentView = view; this.currentView.render(); }, home() { const homeView = new HomeView(); this.changeView(homeView); }, list() { const listView = new ListView(); this.changeView(listView); } });
|
一开始我是这样想的,changeView
函数里有view
和currentView
,currentView
是当前页面,view
是即将出来的页面,有了这两个元素,进行转场岂不是太简单了!于是想拿到这两个元素,打印出来看看
发现view
和currentView
的 el
属性都是指向app
,也就是页面的根元素。这时我懂了,Backbone所有View
的共用一个div
,只要有一个View
执行render
,那么根元素里面的内容就被替换,所以同一时刻只有一个View的内容存在
。
那么解决方案就是,我们需要保证在转场过程中两个View的内容同时存在,转场结束后把上一个View的内容清空。
Backbone.View里有个 el
属性,就是设置页面的根元素
,我们要保证转场过程中两个View的内容同时存在,那一个根元素是不够的,需要两个!
所以我们在View的构造函数里动态选页面择根元素
1 2 3 4 5 6 7 8 9 10 11 12 13
| constructor(options) { this.el = this.selectViewRoot(); $(this.el).removeClass('app-disabled').addClass('app-active'); }, selectViewRoot() { if (!$('.app-content').hasClass('app-active')) { return '.app-content'; } else if (!$('.app-content-back').hasClass('app-active')) { return '.app-content-back'; } return null; }
|
这里 app-content
和 app-content-back
是两个页面根元素,当一个View创建的时候动态选择其中一个。比如View1当前使用的根元素是app-content,从View1切换到View2的时候,View2则使用app-content-back,View2切换到View3的时候,View3则会使用app-content,如此类推。
这时,我们就可以保证在View切换的时候,两个View的内容是同时存在的。那在这个时候进行动画太简单了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| changeView(view) { if (this.currentView !== null) { this.currentView.undelegateEvents(); Scene.changeView(this.currentView.$el, view.$el); } this.currentView = view; this.currentView.render(); }, Scene.changeView(currentView, view) { if (appback) { currentView.addClass('slideOutRight'); view.addClass('slideInLeft'); } else { currentView.addClass('slideOutLeft'); view.addClass('slideInRight'); } }
|
什么时候清空掉上一个View的内容呢?那就是转场动画结束后。
1 2 3 4 5 6 7 8
| $('.app-content').on('webkitAnimationEnd', () => { $('.app-content').removeClass(...); if ($('.app-content').hasClass('app-disabled')) { $('.app-content').html(''); } });
|
到这里,转场动画已经全部实现了,上面贴的只是核心代码,详细可以自己体会。下面一张图展示这个流程。
有人会问,这个每个人都需要实现一遍吗,当然不是,这些逻辑都是与业务逻辑独立的,可以在框架层面上作封装,只需要扩展Backbone的View和Router即可,使用者完全无感知。
感想
最近看了React router,能够提供接入中间件实现转场,可见最根本的办法就是改造Backbone的Router和View,不要只限制每个View公用一个根元素,同时暴露页面进入前、页面进入后的回调给使用者,这才能进行更好的扩展。