UNPKG

weex-nuke

Version:

基于 Rax 、Weex 的高性能组件体系 ~~

353 lines (334 loc) 9.56 kB
/** @jsx createElement */ 'use strict'; import { createElement, Component, findDOMNode, PropTypes } from 'rax'; import View from 'nuke-view'; import { Detection } from 'nuke-ep-utils'; import { connectStyle } from 'nuke-theme-provider'; import Emitter from '../util/emitter'; import Transform from './transform.js'; import stylesProvider from '../styles/index'; import Finger from './finger'; import Nav from './nav'; // 在weex及非android4.4以下的机器中才使用ep绑定 let expressionBinding = {}; try { expressionBinding = require('@weex-module/expressionBinding'); } catch (e) { } const formatRem = num => ( `${( (parseInt(num, 10) * document.documentElement.clientWidth) / 750 ).toString()}px` ); class Tabbar extends Component { constructor(props) { super(props); this.childrenElements = []; // 存放tab主体element this.tabMap = {}; // 用于记录哪个tab是被激活过的状态 this.tabPage = []; // 存放tab节点 this.ob = null; this.scrolling = false; this.maxIndex = (props.children && props.children.length - 1) || 0; // tab的子节点只能是tab.item this.state = { curIndex: props.activeKey && props.activeKey >= 0 && props.activeKey <= this.maxIndex ? props.active : 0, transform: 0, prevIndex: null, }; if ( typeof props.activeKey === 'number' && props.activeKey >= 0 && props.activeKey <= this.maxIndex ) { this.tabMap[props.activeKey] = true; } } componentWillMount() { } componentDidMount() { const { curIndex } = this.state; this.ob = findDOMNode(`view${curIndex}`); } /** * 横向滑动停止 */ panEnd = (e) => { if (newIndex < 0) { newIndex = 0; } else if (newIndex > this.maxIndex) { newIndex = this.maxIndex; } if (Detection.iOS) { setTimeout(() => { this.slideTo(newIndex, 'pan'); }, 50); } else { this.slideTo(newIndex, 'pan'); } }; onSwipe(evt) { const { direction } = evt; let { curIndex } = this.state; if (this.focused) { return false; } switch (direction) { case 'Left': curIndex < this.maxIndex && ++curIndex && this.bindStyle(curIndex); break; case 'Right': curIndex > 0 && curIndex-- && this.bindStyle(curIndex); break; } this.slideTo(curIndex); } endAnimation() { this.list.style.webkitTransition = '0'; // this.ob && this.ob.style && (this.ob.style.webkitTransition = '0'); } bindStyle(current) { // this.ob && this.restore(); // this.ob = findDOMNode(`view${current}`); // if (this.ob && !this.ob.scaleX) { // Transform(this.ob); // } } restore(rotate = true) { // this.ob.translateX = this.ob.translateY = 0; // !!rotate && (this.ob.rotateZ = 0); // this.ob.scaleX = this.ob.scaleY = 1; // this.ob.originX = this.ob.originY = 0; } onPressMove(evt) { const { curIndex } = this.state; this.endAnimation(); if (!this.focused) { if ( (curIndex === 0 && evt.deltaX > 0) || (curIndex === this.maxIndex && evt.deltaX < 0) ) { this.translateX += evt.deltaX / 3; this.list.style.transform = `translateX(${formatRem(this.translateX)})`; } else { this.translateX += evt.deltaX; } } evt.preventDefault(); } /** * tab切换前的回调函数 * @param index 即将滚动去的位置 * @param type 触发滚动的事件类型 */ slideTo = (index, type) => { const time = 300; if (this.isMoving === true) return; this.isMoving = true; const { onChange, beforeSlide, forbidNavScroll } = this.props; const prevIndex = this.state.curIndex; const moveEndCallback = () => { if (typeof onChange === 'function') { onChange(index, prevIndex, type); } this.setState({ curIndex: index, prevIndex, }); this.isMoving = false; }; const navRef = this.refs.nav && this.refs.nav.wrappedInstance; if (typeof beforeSlide === 'function') { if (!beforeSlide(index, type)) return; } // 选中状态滚动,当只有两个item,无需scroll的时候需要禁止滚动 if (!forbidNavScroll) { navRef.scrollTo(index); } navRef.focusMove(index); navRef.setState({ curIndex: index, }); // if (Detection.epEnable) { // // 高版本系统的手机才使用transform动画 // const animation = require('@weex-module/animation'); // const sliderElement = this.refs.listSlider; // const dist = 750 * index; // animation.transition( // findDOMNode(sliderElement), // { // styles: { // transform: `translateX(-${dist}px)`, // }, // delay: 0, // duration: time, // timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', // ['cubic-bezier(0.25, 0.46, 0.45, 0.94)', 'cubic-bezier(0,0,0.25,1)'] // }, // () => {} // ); // } this.list.style.webkitTransition = '300ms ease'; this.translateX = -index * (750 + this.props.gap); this.list.style.transform = `translateX(${formatRem(-index * 750)})`; moveEndCallback(); }; render() { const { curIndex, prevIndex } = this.state; const { themeStyle, children, style, navTop, dataSource, renderNavItem, navStyle, renderLoading, navFocusStyle, forceRender, // ignore SCU just forceRender navContentStyle, } = this.props; const { slider } = themeStyle; if (!children.length) { return null; } if (navTop) { slider.top = navStyle && navStyle.height; navStyle.top = 0; delete navStyle.bottom; } else { slider.bottom = navStyle && navStyle.height; navStyle.bottom = 0; delete navStyle.top; } // 根据子节点计算tab偏移 const sliderStyle = [slider]; sliderStyle.push({ width: 750 * children.length, }); this.translateX = -750 * this.state.curIndex; sliderStyle.push({ transform: `translateX(${formatRem(-750 * this.state.curIndex)})`, }); if (this.tabMap[curIndex]) { this.tabMap[curIndex]++; } else { this.tabMap[curIndex] = 1; } let formatChildren = []; formatChildren = children.map((child, index) => { const length = children.length; const next = circleIndex(this.state.curIndex + 1, length); const prev = circleIndex(this.state.curIndex - 1, length); const shouldRender = this.tabMap[index] === 1; const eachTabStyle = [themeStyle.eachTab, { left: 750 * index }]; if (this.tabMap[index] && curIndex === index) { this.tabPage[curIndex] = ( <View style={eachTabStyle} key={index} shouldRender={shouldRender}> {child} </View> ); } if (this.tabMap[index]) { return this.tabPage[index]; } if ( typeof renderLoading === 'function' && (index === next || index === prev) ) { return ( <View style={eachTabStyle} key={index}> {renderLoading(index)} </View> ); } return null; }); // } else { // formatChildren = children.map((child, index) => { // child.props.id = `view${index}`; // if (index === curIndex) { // return child; // } // return null; // }); // } // const childrenElements = [ // <Finger // onPressMove={this.onPressMove.bind(this)} // onSwipe={this.onSwipe.bind(this)} // > // <View // style={sliderStyle} // id="listWrapper" // ref={node => { // this.list = findDOMNode(node); // }} // onHorizontalPan={ // Detection.epEnable && this.props.epEnable // ? this.onHorizontalPan // : null // } // > // {formatChildren} // </View> // </Finger>, // ]; const childrenElements = [ <View style={sliderStyle} id="listWrapper" ref={(node) => { this.list = findDOMNode(node); }} onHorizontalPan={ Detection.epEnable && this.props.epEnable ? this.onHorizontalPan : null } > {formatChildren} </View>, ]; const headerStyle = navTop ? { ...navStyle, top: 0, positon: 'relative', } : navStyle; const NavWithProps = ( <View style={[themeStyle.header, headerStyle]}> <Nav ref="nav" dataSource={dataSource} renderNavItem={renderNavItem} slideTo={(...args) => this.slideTo(...args)} style={navStyle} contentStyle={navContentStyle} navFocusStyle={navFocusStyle} forceRender={forceRender} /> </View> ); navTop ? childrenElements.unshift(NavWithProps) : childrenElements.push(NavWithProps); return ( <View style={[themeStyle.wrapContainer, style]}>{childrenElements}</View> ); } } function circleIndex(i, len) { return (len + (i % len)) % len; } // Tabbar.propTypes = { // }; Tabbar.defaultProps = { epEnable: false, navTop: true, }; Tabbar.displayName = 'Ep-Tabbar'; export default connectStyle(stylesProvider)(Tabbar);