import * as React from 'react' import { Props, State } from './type' import * as Styled from './style' import getOffsetTop from '../utils/getOffsetTop' import { easeInOutCubic } from '../utils/animate' import getScroll from '../utils/getScroll' import raf from 'raf' import BScroll from 'better-scroll' export class PageIndicator extends React.Component { public static defaultProps = new Props(); public state = new State(); public sharpMatcherRegx = /#([^#]+)$/; public bounds:number = 5; public animating:boolean = false; public anchorContainerParent:any = null; public scrollWrap:any = null; public anchorContainerDom:any = null; public AnchorContainerScroll:any; /** * 获取当前高亮锚点 */ public getCurrentAnchor() { let anchorSections = []; this.props.anchorData.forEach((anchor) => { let { anchorId } = anchor; if(!anchorId){ return; } let target = document.getElementById(anchorId); if (target) { let top = getOffsetTop(target, this.anchorContainerDom); if (top < this.bounds) { anchorSections.push({ anchor: anchor, top: top }); } } }); if (anchorSections.length) { var maxSection = anchorSections.reduce(function (prev, curr) { return curr.top > prev.top ? curr : prev; }); return maxSection.anchor.anchorId; } return ''; } public scrollTo(anchor, callback) { let scrollTop = getScroll(window, true); let targetElement = document.getElementById(anchor); if (!targetElement) { return; } let targetScrollTop = getOffsetTop(targetElement, document.body); let startTime = Date.now(); let frameFunc = () => { let timestamp = Date.now(); let time = timestamp - startTime; let nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450); window.scrollTo(window.pageXOffset, nextScrollTop); if (time < 450) { raf(frameFunc); } else { callback(); } }; raf(frameFunc); } public handleScrollTo(anchor) { this.animating = true; this.setState({ activeAnchor: anchor }); this.scrollTo(anchor, () => { this.animating = false; let top = this.anchorContainerDom.getBoundingClientRect().top if(getOffsetTop(this.anchorContainerDom, this.anchorContainerParent) < 0){ this.anchorContainerDom.style.position = 'absolute'; }else if(top <= 0) { this.anchorContainerDom.style.position = 'fixed'; } }); this.adjust(anchor) }; public handleScroll() { if (this.animating || !this.props.anchorData) { return; } let activeAnchor = this.state.activeAnchor; let currentActiveAnchor = this.getCurrentAnchor(); if (activeAnchor !== currentActiveAnchor) { this.setState({ activeAnchor: currentActiveAnchor }); } let top = this.anchorContainerDom.getBoundingClientRect().top if(getOffsetTop(this.anchorContainerDom, this.anchorContainerParent) < 0){ this.anchorContainerDom.style.position = 'absolute'; }else if(top <= 0) { this.anchorContainerDom.style.position = 'fixed'; } this.adjust(currentActiveAnchor) } public adjust(anchorId) { if(this.props.type == 1){ return; } if(!anchorId && this.AnchorContainerScroll){ this.AnchorContainerScroll.scrollTo(0, 0, 0) return } const viewportWidth = this.anchorContainerParent.clientWidth const tabListWidth = this.anchorContainerDom.clientWidth const minTranslate = Math.min(0, viewportWidth - tabListWidth) const middleTranslate = viewportWidth / 2 const items = this.anchorContainerDom.children let width = 0 this.props.anchorData.every((item, index) => { if (item.anchorId === anchorId) { return false } width += items[index].clientWidth return true }) let translate = middleTranslate - width translate = Math.max(minTranslate, Math.min(0, translate)) this.AnchorContainerScroll && this.AnchorContainerScroll.scrollTo(translate, 0, 300) } public componentDidMount() { if(this.props.type == 1){ this.anchorContainerDom.style.width = `${this.anchorContainerDom.offsetWidth}px`; } if(!this.props.isEdit && this.props.type == 2){ //横向滚动样式 const wrapper = this.scrollWrap this.AnchorContainerScroll = new BScroll(wrapper, { scrollX: true, click: true, scrollY: false }) } window.addEventListener('scroll', () => this.handleScroll()); this.handleScroll(); } public render() { let { style, backgroundColor, color, activeColor, type, marginTop} = this.props const isDefaultTheme = backgroundColor.toLowerCase() === '#fff' && color === '#333' && activeColor === '#d33a31'; return ( this.anchorContainerParent = ref} style={style}>
this.scrollWrap = ref} className="anchor-wrap j-scroll-wrapper" style={{marginTop: `${marginTop}rem`, overflow: 'hidden'}}> this.anchorContainerDom = ref} className={`anchor-ul type-${type}`} theme={{backgroundColor, isDefaultTheme}}> {this.props.anchorData && this.props.anchorData.map((data, index) => { let { anchorName, anchorId, focusImage, unfocusImage } = data; if (focusImage) { focusImage = focusImage.url; } if (unfocusImage) { unfocusImage = unfocusImage.url; } return ( this.handleScrollTo(anchorId)}> {anchorName} ) })}
) } }