import BaseFoundation, { DefaultAdapter } from '../base/foundation'; import { Animation } from '@douyinfe/semi-animation'; export type BackTopClickEvent = any; export type Target = any; export interface BackTopAdapter extends DefaultAdapter { updateVisible: (visible: boolean) => void; notifyClick: (e: BackTopClickEvent) => void; targetIsWindow: (target: any) => boolean; isWindowUndefined: () => boolean; targetScrollToTop: (targetNode: any, scrollTop: number) => void; } export default class BackTopFoundation extends BaseFoundation { animation!: any; constructor(adapter: BackTopAdapter) { super({ ...adapter }); } init() { const { target } = this.getProps(); const targetNode = target(); targetNode.addEventListener('scroll', this.handleScroll); this.handleScroll(); } destroy() { const { target } = this.getProps(); const targetNode = target(); targetNode && targetNode.removeEventListener('scroll', this.handleScroll); this.animation && this.animation.destroy(); } getScroll(target: Target) { if (this._adapter.isWindowUndefined()) { return 0; } const prop = 'pageYOffset'; const method = 'scrollTop'; const isWindow = this._adapter.targetIsWindow(target); const scroll = isWindow ? target[prop] : target[method]; return scroll; } scrollTo = (targetNode: Target, from: number, to: number) => { const { duration } = this.getProps(); this.animation = new Animation( { from: { scrollTop: from }, to: { scrollTop: to }, }, { duration, easing: 'easeInOutCubic' } ); this.animation.on('frame', ({ scrollTop }: { scrollTop: number }) => { this._adapter.targetScrollToTop(targetNode, scrollTop); }); this.animation.start(); }; setScrollTop(to: number) { const { target } = this.getProps(); const targetNode = target(); const from = this.getScroll(targetNode); this.scrollTo(targetNode, from, to); } handleScroll = () => { const { target, visibilityHeight } = this.getProps(); const targetNode = target(); const update = () => { const scrollTop = this.getScroll(targetNode); this._adapter.updateVisible(scrollTop > visibilityHeight); }; requestAnimationFrame(update); }; onClick(e: BackTopClickEvent) { this.setScrollTop(0); this._adapter.notifyClick(e); } }