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

版本:>= 3.3.0

s-transition

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

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

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

  1. san.defineComponent({
  2. template: '<div><button s-transition="opacityTransition">click</button></div>',
  3. opacityTransition: {
  4. // 过渡动画控制器的结构在下文中描述
  5. // ...
  6. }
  7. });

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

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

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

  1. san.defineComponent({
  2. template: '<div><button s-transition="trans.opacity">click</button></div>',
  3. trans: {
  4. opacity: {
  5. // 过渡动画控制器的结构在下文中描述
  6. // ...
  7. }
  8. }
  9. });

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

动画控制器

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

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

  1. san.defineComponent({
  2. template: `
  3. <div>
  4. <button on-click="toggle">toggle</button>
  5. <button s-if="isShow" s-transition="opacityTrans">Hello San!</button>
  6. <button s-else s-transition="opacityTrans">Hello ER!</button>
  7. </div>
  8. `,
  9. toggle: function () {
  10. this.data.set('isShow', !this.data.get('isShow'));
  11. },
  12. opacityTrans: {
  13. enter: function (el, done) {
  14. var steps = 20;
  15. var currentStep = 0;
  16. function goStep() {
  17. if (currentStep >= steps) {
  18. el.style.opacity = 1;
  19. done();
  20. return;
  21. }
  22. el.style.opacity = 1 / steps * currentStep++;
  23. requestAnimationFrame(goStep);
  24. }
  25. goStep();
  26. },
  27. leave: function (el, done) {
  28. var steps = 20;
  29. var currentStep = 0;
  30. function goStep() {
  31. if (currentStep >= steps) {
  32. el.style.opacity = 0;
  33. done();
  34. return;
  35. }
  36. el.style.opacity = 1 - 1 / steps * currentStep++;
  37. requestAnimationFrame(goStep);
  38. }
  39. goStep();
  40. }
  41. }
  42. });

提示

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

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

动画控制器 Creator

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

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

  1. san.defineComponent({
  2. template: `
  3. <div>
  4. <button on-click="toggle">toggle</button>
  5. <button s-if="isShow" s-transition="opacityTrans">Hello San!</button>
  6. <button s-else s-transition="opacityTrans">Hello ER!</button>
  7. </div>
  8. `,
  9. toggle: function () {
  10. this.data.set('isShow', !this.data.get('isShow'));
  11. },
  12. opacityTrans: function () {
  13. return {
  14. enter: function (el, done) {
  15. var steps = 20;
  16. var currentStep = 0;
  17. function goStep() {
  18. if (currentStep >= steps) {
  19. el.style.opacity = 1;
  20. done();
  21. return;
  22. }
  23. el.style.opacity = 1 / steps * currentStep++;
  24. requestAnimationFrame(goStep);
  25. }
  26. goStep();
  27. },
  28. leave: function (el, done) {
  29. var steps = 20;
  30. var currentStep = 0;
  31. function goStep() {
  32. if (currentStep >= steps) {
  33. el.style.opacity = 0;
  34. done();
  35. return;
  36. }
  37. el.style.opacity = 1 - 1 / steps * currentStep++;
  38. requestAnimationFrame(goStep);
  39. }
  40. goStep();
  41. }
  42. }
  43. }
  44. });

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

  1. san.defineComponent({
  2. template: `
  3. <div>
  4. <button on-click="toggle">toggle</button>
  5. <button on-click="toggleTrans">toggle transition</button>
  6. <button s-if="isShow" s-transition="opacityTrans(noTransition)">Hello San!</button>
  7. <button s-else s-transition="opacityTrans(noTransition)">Hello ER!</button>
  8. </div>
  9. `,
  10. toggle: function () {
  11. this.data.set('isShow', !this.data.get('isShow'));
  12. },
  13. toggleTrans: function () {
  14. this.data.set('noTransition', !this.data.get('noTransition'));
  15. },
  16. initData: function () {
  17. return {
  18. noTransition: false
  19. };
  20. },
  21. opacityTrans: function (disabled) {
  22. return {
  23. enter: function (el, done) {
  24. if (disabled) {
  25. done();
  26. return;
  27. }
  28. var steps = 20;
  29. var currentStep = 0;
  30. function goStep() {
  31. if (currentStep >= steps) {
  32. el.style.opacity = 1;
  33. done();
  34. return;
  35. }
  36. el.style.opacity = 1 / steps * currentStep++;
  37. requestAnimationFrame(goStep);
  38. }
  39. goStep();
  40. },
  41. leave: function (el, done) {
  42. if (disabled) {
  43. done();
  44. return;
  45. }
  46. var steps = 20;
  47. var currentStep = 0;
  48. function goStep() {
  49. if (currentStep >= steps) {
  50. el.style.opacity = 0;
  51. done();
  52. return;
  53. }
  54. el.style.opacity = 1 - 1 / steps * currentStep++;
  55. requestAnimationFrame(goStep);
  56. }
  57. goStep();
  58. }
  59. }
  60. }
  61. });