foundation.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  2. import { Animation } from '@douyinfe/semi-animation';
  3. export type BackTopClickEvent = any;
  4. export type Target = any;
  5. export interface BackTopAdapter extends DefaultAdapter {
  6. updateVisible: (visible: boolean) => void;
  7. notifyClick: (e: BackTopClickEvent) => void;
  8. targetIsWindow: (target: any) => boolean;
  9. isWindowUndefined: () => boolean;
  10. targetScrollToTop: (targetNode: any, scrollTop: number) => void;
  11. }
  12. export default class BackTopFoundation extends BaseFoundation<BackTopAdapter> {
  13. animation!: any;
  14. constructor(adapter: BackTopAdapter) {
  15. super({ ...adapter });
  16. }
  17. init() {
  18. const { target } = this.getProps();
  19. const targetNode = target();
  20. targetNode.addEventListener('scroll', this.handleScroll);
  21. this.handleScroll();
  22. }
  23. destroy() {
  24. const { target } = this.getProps();
  25. const targetNode = target();
  26. targetNode && targetNode.removeEventListener('scroll', this.handleScroll);
  27. this.animation && this.animation.destroy();
  28. }
  29. getScroll(target: Target) {
  30. if (this._adapter.isWindowUndefined()) {
  31. return 0;
  32. }
  33. const prop = 'pageYOffset';
  34. const method = 'scrollTop';
  35. const isWindow = this._adapter.targetIsWindow(target);
  36. const scroll = isWindow ? target[prop] : target[method];
  37. return scroll;
  38. }
  39. scrollTo = (targetNode: Target, from: number, to: number) => {
  40. const { duration } = this.getProps();
  41. this.animation = new Animation(
  42. {
  43. from: { scrollTop: from },
  44. to: { scrollTop: to },
  45. },
  46. {
  47. duration,
  48. easing: 'easeInOutCubic'
  49. }
  50. );
  51. this.animation.on('frame', ({ scrollTop }: { scrollTop: number }) => {
  52. this._adapter.targetScrollToTop(targetNode, scrollTop);
  53. });
  54. this.animation.start();
  55. };
  56. setScrollTop(to: number) {
  57. const { target } = this.getProps();
  58. const targetNode = target();
  59. const from = this.getScroll(targetNode);
  60. this.scrollTo(targetNode, from, to);
  61. }
  62. handleScroll = () => {
  63. const { target, visibilityHeight } = this.getProps();
  64. const targetNode = target();
  65. const update = () => {
  66. const scrollTop = this.getScroll(targetNode);
  67. this._adapter.updateVisible(scrollTop > visibilityHeight);
  68. };
  69. requestAnimationFrame(update);
  70. };
  71. onClick(e: BackTopClickEvent) {
  72. this.setScrollTop(0);
  73. this._adapter.notifyClick(e);
  74. }
  75. }