一 目标
App的开发中可能会见到的一种用户体验,如商品列表到商品详情的跳转,产生了一次跨路由的跳转 ,在页面的跳转过程中,列表页面的图片飞入详情页面,中间没有出现白屏闪现到详情页的情况,极大的增加了两个路由之间的体验感。
如果用过flutter,可以直接通过控件hero的方案来解决,这里我们通过前端的方式实现相同的效果,本章主要讲的是我在设计时候的一些思路,最后提供完成后控件的使用方法,目前完成的控件仍然存在问题,只支持一部分的情况,我会提供强化控件上的一些想法,如果您有更好的解决方案,或者发现控件的设计存在问题,希望您可以留言。
二 解决思路
一个元素飞入第二个页面首先想到的是把当前元素拷贝下来计算第二个界面具有相同元素的位置
- 第一次进入页面创建hero元素
- 点击跳转,当前页面元素脱离当前文档流或者复制当前元素,放置在body下的第一个div,从而脱离原先所在的。 div(heroAni),隐藏或删除第一页的hero元素
- 第二个页面创建hero元素,但是不显示
- 动画开始路由转场,拷贝出来的元素开始向第二个hero元素的位置移动
- 转场结束、heroAni动画也需要结束(此时拷贝出来的heroani的位置应该在第二个页面的Hero元素的位置,且样式完全一样),删除拷贝出来的heroAni元素
- 完成✊
然后在过度的过程中飞过去,中间需要考虑的问题是在你的spa页面框架体制下,你的每个页面的生命周期是什么,在vue中,如果使用了动画转场效果,那么在转场的过程中你会发现两个页面同时出现在router-viewer下,动画结束时才会消除另外一个UI,尽管如此,它的周期执行顺序依旧是
mounted(activated)->beforeDestroy(deactivated)->mounted(activated)->beforeDestroy(deactivated)
下面梳理每个周期应该做的事情?
前期准备工作:创建一个Map(heroMap),这个数组的作用处理记录我们每个hero元素在加载出来的位置等其他相关信息的同时,他将还要负责所有的事件驱动相关的处理,我们在这个组件的开发中完全摆脱点击事件等原生事件的依赖(我们不会因为你点击了某个元素而去记录当前元素的位置,这有时会产生大量的监听,每次都需要在离开页面是清除监听,占用没有必要的资源消耗)
- mounted(activated)时期我么也需要去区分是否是首屏加载,原因是----》首屏加载的时候我们不需要将我们的需要动画的元素隐藏,将当前页面所有的需要转场动画的Hero元素的位置记录在我们预先准备的Map中
- beforeDestroy(deactivated)时期,此时页面即将销毁,立刻在我们的body下创建一层fixed的遮罩,我们将需要在此产生动画的元素添加进遮罩,并且必须完全覆盖原先的Hero元素的位置,一旦覆盖上出以后马上隐藏当前元素(动画产生时我们不需要页面同时存在三个长相一样的元素,只要一个会跑的就好?),同时将当前页面的滚动情况也推向我们预先设置好的Map
至此我们每个周期需要的工作大致结束,其中还有部分细节的操作便是计算,我们将当前页面的元素信息推向预设Map时,我们需要判断当前Map中是否已经存在某个元素匹配当前元素(?这里我没有表达清楚可能,连个页面Hero元素的关联是通过元素中的tag标示,只要预设Map中存在相同的tag标示就意味着此元素需要产生动画,这里也是我们设置Map作为驱动的主要目标)
注意点
看下我的div结构
body的大小只占有100% 为浏览器内的可视部分的高度、overflow为hidden,每个单页面中都包含一个class=page 它的高度也是100%,但是overflow为auto,因而每次滚动页面的话滚动的区域是class=page,这样的话我们添加的遮罩层fixed的大小刚好也是body的大小,不会随着内部元素的大小而超过可视区域出现滚屏,遮罩层下面的heroAni滚动的距离范围也就在body大小的范围以内。这样的结构模式在你开发移动端弹窗的时候也是至关重要的,可以有效的处理遮罩层为滚动穿透的问题。
三 插件使用
# step 1
npm i heroani --save
# step 2
import {HeroAni} from 'heroani';
Vue.use(HeroAni, {time: 500});
# step 3 注册组件
import {HeroUi} from 'heroani';
components: {
HeroUi
}
# step 4 使用
<div style="width: 100px;overflow: hidden;height: 100px">
<hero-ui master="true" key="as" tag="484e98349b1db8a3f1cf0113ba493f651">
<div class="imglovery">
</div>
</hero-ui>
</div>
四 目前存在的问题
无法适应第二个页面出现两个Hero元素的情况 ,目前没有做过下拉刷新的测试,理论上如果你的返回是缓存页面不会出发页面的重新数据加载是没有问题的
五 总结
如果想要解决而页面出现两个Hero元的情况可以改造Map的存储方式,同时向遮罩层添加HeroAni的方法也需要进行改造,希望本文中的hero对你的设计有所帮助,如果存在问题欢迎留言,我会积极改进