视图跳转动画

概述

视图跳转动画,是指活动视图从一个视图切换至另外一个视图的过程中,展现给用户的动画效果。
View.js 提供的默认样式中,是不含跳转动画的。

设置动画

设置视图跳转动画时,开发者需要了解到视图的四种进入方式:
  1. 1.
    压入堆栈式进入
  2. 2.
    替换栈顶式进入
  3. 3.
    浏览器后退进入
  4. 4.
    浏览器前进进入
如果视图的跳转动画是不区分进入方式的,如:渐隐渐显,那么开发者不需要考虑上述差异,按照固定的模式实现即可。但如果想要根据进入方式的不同,呈现差异化的跳转动画,开发者就需要捕获进入方式,然后做出不同的响应。
例如:
区分进入方式的视图跳转动画
技术上,View.js 建议开发者使用 CSS3 animation,结合少量脚本实现跳转动画。具体流程如下:
  1. 1.
    使用 CSS,借助 animation、transition 等特性创建视图离开动画、视图进入动画
  2. 2.
    有条件地(例如:DOM 元素的 class 含有特定取值时)为视图的 DOM 元素引用动画并确定动画播放时长
  3. 3.
    通过 View.setSwitchAnimation 方法,以设置动画的“播放触发器”
对于动画的展现效果,以及动画的播放时长,View.js 均不加以限制,开发者可以自由定义。
由于 View.js 并不知道开发者定义的动画的播放时长,因此开发者需要在设置的动画播放触发器中使用定时器 延迟相同时间 后呈现界面。
下面,我们将从代码细节分析、说明动画的实现过程。

代码分析

HTML + CSS

index.html
Untitled
1
<!-- 视图1的DOM骨架 -->
2
<section data-view-id = "page1" data-view-default = "true" >
3
<header>
4
<span class = "nav-back" data-view-rel = ":back"></span>
5
Page 1
6
</header>
7
<h1>This is page 1.</h1>
8
<div data-view-rel = "page2" class = "btn">Navigate to page 2.</div>
9
</section>
10
11
<!-- 视图2的DOM骨架 -->
12
<section data-viw-id = "page2">
13
<header>
14
<span class = "nav-back" data-view-rel = ":back"></span>
15
Page 2
16
</header>
17
<h1>This is page 2.</h1>
18
<div data-view-rel = "page3" class = "btn">Navigate to page 3.</div>
19
</section>
20
21
<!-- 视图3的DOM骨架 -->
22
<section data-view-id = "page3">
23
<header>
24
<span class = "nav-back" data-view-rel = ":back"></span>
25
Page 3
26
</header>
27
<h1>This is page 3.</h1>
28
<div data-view-rel = ":default-view" class = "btn">Navigate to page 1.</div>
29
</section>
Copied!
1
/**
2
* 视图基本样式定义。
3
* 所有视图重叠在一起,默认都不显示
4
*/
5
[data-view-id] {
6
opacity: 0;
7
z-index: 0;
8
position: absolute;
9
left: 0;
10
top: 0;
11
width: 100%;
12
height: 100%;
13
box-sizing: border-box;
14
overflow: auto;
15
-webkit-overflow-scrolling: touch;
16
background: #F3F3F3;
17
box-shadow: 0 0 70px rgba(0, 0, 0, 0.3);
18
}
19
/**
20
* 视图隐藏时要呈现的半透明黑色蒙层。默认不显示
21
*/
22
[data-viw-id]:before{
23
opacity: 0;
24
content: "";
25
display: none;
26
position: absolute;
27
left: 0;
28
top: 0;
29
width: 100%;
30
height: 100%;
31
background: rgba(0, 0, 0, 0.3);
32
}
33
/**
34
* 活动视图可见
35
* 'active' 由 View.js 自动添加至活动视图
36
*/
37
[data-view-id].active {
38
opacity: 1;
39
z-index: 1;
40
}
41
42
/************** 定义动画 ***************/
43
44
/**
45
* 定义动画:向左隐藏。
46
* 当以 “压入堆栈” 的方式切换视图,包括执行浏览器前进动作,
47
* 导致视图由活动状态变为非活动状态,需要向左淡出视野时触发。
48
*/
49
@keyframes hideToLeft{
50
from{transform: translate3d(0, 0, 0); opacity: 1;}
51
to{transform: translate3d(-50%, 0, 0); opacity: 1;}
52
}
53
54
/**
55
* 定义动画:向左呈现。
56
* 当以 “压入堆栈” 的方式切换视图,包括执行浏览器前进动作,
57
* 导致视图由非活动状态变为活动状态,需要向左进入视野时触发。
58
*/
59
@keyframes showFromRight{
60
from{transform: translate3d(100%, 0, 0); opacity: 1;}
61
to{transform: translate3d(0, 0, 0); opacity: 1;}
62
}
63
64
/**
65
* 定义动画:向右隐藏。
66
* 当执行浏览器后退动作,
67
* 导致视图由活动状态变为非活动状态,需要向右淡出视野时触发。
68
*/
69
@keyframes hideToRight{
70
from{transform: translate3d(0, 0, 0); opacity: 1;}
71
to{transform: translate3d(100%, 0, 0); opacity: 1;}
72
}
73
74
/**
75
* 定义动画:向右呈现。
76
* 当执行浏览器后退动作,
77
* 导致视图由非活动状态变为活动状态,需要向右进入视野时触发。
78
*/
79
@keyframes showFromLeft{
80
from{transform: translate3d(-50%, 0, 0); opacity: 1;}
81
to{transform: translate3d(0, 0, 0); opacity: 1;}
82
}
83
84
85
86
/************** “有条件地” 引用动画 ***************/
87
88
[data-view-id].hideToLeft{
89
animation: hideToLeft 0.6s ease-out;
90
}
91
[data-view-id].hideToLeft:before{
92
display: block;
93
animation: fadeIn 0.6s linear;
94
}
95
[data-view-id].showFromLeft{
96
animation: showFromLeft 0.6s ease-out;
97
}
98
[data-view-id].showFromLeft:before{
99
display: block;
100
animation: fadeOut 0.6s linear;
101
}
102
[data-view-id].hideToRight{
103
/* z-index要比活动视图的1更高,从而使其不会被活动视图覆盖 */
104
z-index: 2;
105
animation: hideToRight 0.6s ease-out;
106
}
107
[data-view-id].showFromRight{
108
animation: showFromRight 0.6s ease-out;
109
}
Copied!

JS

1
;(function () {
2
var timer;
3
4
/**
5
* 动画持续时长,需要与css中定义的动画时长一致
6
* @type {number}
7
*/
8
var animationDuration = 600;
9
10
/**
11
* 判断给定的对象是否包含指定名称的样式类
12
*/
13
var hasClass = function (obj, clazz) {
14
if (null == clazz || (clazz = String(clazz).trim()) === "")
15
return false;
16
17
if (obj.classList && obj.classList.contains)
18
return obj.classList.contains(clazz);
19
20
return new RegExp("\\b" + clazz + "\\b", "gim").test(obj.className);
21
};
22
23
/**
24
* 为指定的对象添加样式类
25
*/
26
var addClass = function (obj, clazz) {
27
if (null == clazz || (clazz = String(clazz).trim()) === "" || hasClass(obj, clazz))
28
return;
29
30
if (obj.classList && obj.classList.add) {
31
obj.classList.add(clazz);
32
return;
33
}
34
35
obj.className = (obj.className.trim() + " " + clazz).trim();
36
};
37
38
/**
39
* 为指定的对象删除样式类
40
*/
41
var removeClass = function (obj, clazz) {
42
if (null == clazz || (clazz = String(clazz).trim()) === "" || !hasClass(obj, clazz))
43
return;
44
45
if (obj.classList && obj.classList.remove) {
46
obj.classList.remove(clazz);
47
return;
48
}
49
50
clazz = String(clazz).toLowerCase();
51
var arr = obj.className.split(/\s+/),
52
str = "";
53
for (var i = 0; i < arr.length; i++) {
54
var tmp = arr[i];
55
if (null == tmp || (tmp = tmp.trim()) === "")
56
continue;
57
58
if (tmp.toLowerCase() === clazz)
59
continue;
60
61
str += " " + tmp;
62
}
63
if (str.length > 0)
64
str = str.substring(1);
65
obj.className = str.trim();
66
};
67
68
69
/**
70
* 清除给定DOM元素上声明的动画样式
71
* @param {HTMLElement} obj
72
*/
73
var clear = function (obj) {
74
if (!obj)
75
return;
76
77
"hideToLeft, showFromRight, hideToRight, showFromLeft".split(/\s*,\s*/).forEach(function (className) {
78
removeClass(obj, className);
79
});
80
};
81
82
/**
83
* @param {Object} meta 切换信息
84
* @param {HTMLElement} meta.srcElement 视图切换时,要离开的当前视图对应的DOM元素。可能为null
85
* @param {HTMLElement} meta.targetElement 视图切换时,要进入的目标视图对应的DOM元素
86
* @param {String} type 视图切换方式
87
* @param {String} trigger 视图切换触发器
88
* @param {Function} render 渲染句柄
89
*/
90
View.setSwitchAnimation(function (meta) {
91
var srcElement = meta.srcElement,
92
tarElement = meta.targetElement,
93
type = meta.type,
94
trigger = meta.trigger,
95
render = meta.render;
96
97
/**
98
* 动画播放前清除可能存在的动画样式
99
*/
100
clear(srcElement);
101
clear(tarElement);
102
103
/**
104
* 调用View.js传递而来的渲染句柄,完成活动视图的切换,包括:
105
* 1. 视图参数的传递
106
* 2. 活动视图样式类的切换
107
* 3. leave,ready、enter等事件的触发
108
*/
109
render();
110
111
var isNav = type === View.SWITCHTYPE_VIEWNAV,
112
isChange = type === View.SWITCHTYPE_VIEWCHANGE,
113
isHistoryBack = type === View.SWITCHTYPE_HISTORYBACK,
114
isHistoryForward = type === View.SWITCHTYPE_HISTORYFORWARD;
115
116
if (/\bsafari\b/i.test(navigator.userAgent) && (isHistoryBack || isHistoryForward) && trigger ===
117
View.SWITCHTRIGGER_NAVIGATOR)
118
return;
119
120
/**
121
* 视图切换动作是“替换堆栈”的方式,或浏览器不支持对history的操作
122
*/
123
if (!View.checkIfBrowserHistorySupportsPushPopAction() || isChange) {
124
addClass(srcElement, "fadeOut");
125
addClass(tarElement, "fadeIn");
126
} else if (isHistoryForward || isNav) {
127
/**
128
* 视图切换动作是“压入堆栈”的方式(浏览器前进,或代码触发)
129
*/
130
131
addClass(srcElement, "hideToLeft");
132
addClass(tarElement, "showFromRight");
133
} else {
134
/**
135
* 视图切换动作是“弹出堆栈”的方式(浏览器后退)
136
*/
137
138
addClass(srcElement, "hideToRight");
139
addClass(tarElement, "showFromLeft");
140
}
141
142
/**
143
* 动画播放完成后清除动画样式
144
*/
145
clearTimeout(timer);
146
timer = setTimeout(function () {
147
clear(srcElement);
148
clear(tarElement);
149
}, animationDuration);
150
});
151
})();
Copied!
其中 View.setSwitchAnimation() 用于向 View.js 提供 “播放触发器”,告知 View.js 在何时播放什么动画:
  1. 1.
    压入堆栈时,源视图向左滑动隐藏,目标视图从右向左显示
  2. 2.
    弹出堆栈时,源视图向右滑动隐藏,目标视图从左向右显示
Last modified 1yr ago