LOGO

过渡

编辑本文

在视图中,过渡效果是常见的场景。平滑的过渡动画能够给用户更好的感官体验。san 提供了基础的过渡机制,你可以基于此开发丰富的过渡效果。

版本:>= 3.3.0

s-transition

在元素上通过 s-transition 指令,可以声明过渡动画控制器。

1
<button s-transition="opacityTransition">click</button>

这个对象是元素 owner 的成员。

1
2
3
4
5
6
7
8
san.defineComponent({
template: '<div><button s-transition="opacityTransition">click</button></div>',

opacityTransition: {
// 过渡动画控制器的结构在下文中描述
// ...
}
});

我们通常把 s-transition 和条件或循环指令一起使用。

1
2
<button s-transition="opacityTransition" s-if="allowEdit">Edit</button>
<b s-transition="opacityTransition" s-else>Edit not allow</b>

s-transition 声明的过渡动画控制器可以是 owner 组件的深层成员。

1
2
3
4
5
6
7
8
9
10
san.defineComponent({
template: '<div><button s-transition="trans.opacity">click</button></div>',

trans: {
opacity: {
// 过渡动画控制器的结构在下文中描述
// ...
}
}
});

注意s-transition 只能应用在具体的元素中。template 这种没有具体元素的标签上应用 s-transition 将没有效果。

动画控制器

过渡动画控制器是一个包含 enterleave 方法的对象。

enterleave 方法的签名为 **function({HTMLElement}el, {Function}done)**。san 会把要过渡的元素传给过渡动画控制器,控制器在完成动画后调用 done 回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
san.defineComponent({
template: `
<div>
<button on-click="toggle">toggle</button>
<button s-if="isShow" s-transition="opacityTrans">Hello San!</button>
<button s-else s-transition="opacityTrans">Hello ER!</button>
</div>
`,

toggle: function () {
this.data.set('isShow', !this.data.get('isShow'));
},

opacityTrans: {
enter: function (el, done) {
var steps = 20;
var currentStep = 0;

function goStep() {
if (currentStep >= steps) {
el.style.opacity = 1;
done();
return;
}

el.style.opacity = 1 / steps * currentStep++;
requestAnimationFrame(goStep);
}

goStep();
},

leave: function (el, done) {
var steps = 20;
var currentStep = 0;

function goStep() {
if (currentStep >= steps) {
el.style.opacity = 0;
done();
return;
}

el.style.opacity = 1 - 1 / steps * currentStep++;
requestAnimationFrame(goStep);
}

goStep();
}
}
});

提示

san 把动画控制器留给应用方实现,框架本身不内置动画控制效果。应用方可以:

  • 使用 css 动画,在 transitionend 或 animationend 事件监听中回调 done
  • 使用 requestAnimationFrame 控制动画,完成后回调 done
  • 在老旧浏览器使用 setTimeout / setInterval 控制动画,完成后回调 done
  • 发挥想象力

动画控制器 Creator

s-transition 指令声明对应的对象如果是一个 function,san 将把它当成 过渡动画控制器 Creator

每次触发过渡动画前,san 会调用过渡动画控制器 Creator,用其返回的对象作为过渡动画控制器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
san.defineComponent({
template: `
<div>
<button on-click="toggle">toggle</button>
<button s-if="isShow" s-transition="opacityTrans">Hello San!</button>
<button s-else s-transition="opacityTrans">Hello ER!</button>
</div>
`,

toggle: function () {
this.data.set('isShow', !this.data.get('isShow'));
},

opacityTrans: function () {
return {
enter: function (el, done) {
var steps = 20;
var currentStep = 0;

function goStep() {
if (currentStep >= steps) {
el.style.opacity = 1;
done();
return;
}

el.style.opacity = 1 / steps * currentStep++;
requestAnimationFrame(goStep);
}

goStep();
},

leave: function (el, done) {
var steps = 20;
var currentStep = 0;

function goStep() {
if (currentStep >= steps) {
el.style.opacity = 0;
done();
return;
}

el.style.opacity = 1 - 1 / steps * currentStep++;
requestAnimationFrame(goStep);
}

goStep();
}
}
}
});

事件声明类似,过渡动画控制器 Creator调用支持传入参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
san.defineComponent({
template: `
<div>
<button on-click="toggle">toggle</button>
<button on-click="toggleTrans">toggle transition</button>
<button s-if="isShow" s-transition="opacityTrans(noTransition)">Hello San!</button>
<button s-else s-transition="opacityTrans(noTransition)">Hello ER!</button>
</div>
`,

toggle: function () {
this.data.set('isShow', !this.data.get('isShow'));
},

toggleTrans: function () {
this.data.set('noTransition', !this.data.get('noTransition'));
},

initData: function () {
return {
noTransition: false
};
},

opacityTrans: function (disabled) {
return {
enter: function (el, done) {
if (disabled) {
done();
return;
}

var steps = 20;
var currentStep = 0;

function goStep() {
if (currentStep >= steps) {
el.style.opacity = 1;
done();
return;
}

el.style.opacity = 1 / steps * currentStep++;
requestAnimationFrame(goStep);
}

goStep();
},

leave: function (el, done) {
if (disabled) {
done();
return;
}

var steps = 20;
var currentStep = 0;

function goStep() {
if (currentStep >= steps) {
el.style.opacity = 0;
done();
return;
}

el.style.opacity = 1 - 1 / steps * currentStep++;
requestAnimationFrame(goStep);
}

goStep();
}
}
}
});