weex-nuke
Version:
基于 Rax 、Weex 的高性能组件体系 ~~
416 lines (370 loc) • 15.4 kB
JavaScript
/** @jsx createElement */
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _rax = require('rax');
var _nukeView = require('../../View/index.js');
var _nukeView2 = _interopRequireDefault(_nukeView);
var _nukeEpUtils = require('../../EpUtils/index.js');
var _nukeThemeProvider = require('../../ThemeProvider/index.js');
var _emitter = require('../util/emitter.js');
var _emitter2 = _interopRequireDefault(_emitter);
var _index = require('../styles/index.js');
var _index2 = _interopRequireDefault(_index);
var _nav = require('./nav.js');
var _nav2 = _interopRequireDefault(_nav);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
// 在weex及非android4.4以下的机器中才使用ep绑定
var expressionBinding = {};
try {
expressionBinding = require('@weex-module/expressionBinding');
} catch (e) {}
var Tabbar = function (_Component) {
_inherits(Tabbar, _Component);
function Tabbar(props) {
_classCallCheck(this, Tabbar);
var _this = _possibleConstructorReturn(this, (Tabbar.__proto__ || Object.getPrototypeOf(Tabbar)).call(this, props));
_this.bindExp = function (element) {
if (!_this.isMoving && element && element.ref && _nukeEpUtils.Detection.epEnable) {
_this.startTime = Date.now();
var _this$props = _this.props,
dataSource = _this$props.dataSource,
navFocusStyle = _this$props.navFocusStyle;
var listElement = (0, _rax.findDOMNode)(_this.refs.listSlider);
var index = _this.state.curIndex;
var tabFocusElement = (0, _rax.findDOMNode)(_this.refs.nav.wrappedInstance.refs.tabFocus);
// 绑定滚动只取第一个 itemWidthList的宽度
var tabItemWidth = dataSource[0] && dataSource[0].style && dataSource[0].style.width;
var dpr = 1;
var tabLeft = index * tabItemWidth / dpr;
if (navFocusStyle && navFocusStyle.left) {
// tabLeft += navFocusStyle.left;
}
var sliderLeft = index * 750 / dpr;
/*
ep表达式备注
左边界 min(x, x/3)
右边界 max(x, x/3) - ${dist}
中间 x - dist
*/
var args = [{
element: tabFocusElement.ref,
property: 'transform.translateX',
expression: '{"type":"-","children":[{"type":"NumericLiteral","value":' + tabLeft + '},{"type":"/","children":[{"type":"Identifier","value":"x"},{"type":"NumericLiteral","value":5}]}]}'
}];
if (index == 0) {
// 第一帧 min(x, x/3)
var ep2 = '{"type":"CallExpression","children":[{"type":"Identifier","value":"min"},{"type":"Arguments","children":[{"type":"Identifier","value":"x"},{"type":"/","children":[{"type":"Identifier","value":"x"},{"type":"NumericLiteral","value":3}]}]}]}';
args.push({
element: listElement.ref,
property: 'transform.translateX',
expression: ep2
});
} else if (index == dataSource.length - 1) {
// 最后一祯 `max(x, x/3) - ${sliderLeft}`
var _ep = '{"type":"-","children":[{"type":"CallExpression","children":[{"type":"Identifier","value":"max"},{"type":"Arguments","children":[{"type":"Identifier","value":"x"},{"type":"/","children":[{"type":"Identifier","value":"x"},{"type":"NumericLiteral","value":3}]}]}]},{"type":"NumericLiteral","value":' + sliderLeft + '}]}';
args.push({
element: listElement.ref,
property: 'transform.translateX',
expression: _ep
});
} else {
// `x - ${sliderLeft}`
var _ep2 = '{"type":"-","children":[{"type":"Identifier","value":"x"},{"type":"NumericLiteral","value":' + sliderLeft + '}]}';
args.push({
element: listElement.ref,
property: 'transform.translateX',
expression: _ep2
});
}
// console.log('enableBinding');
// expressionBinding.enableBinding(element.ref, 'pan');
// console.log('createBinding');
expressionBinding.createBinding(element.ref, 'pan', '', args, _this.panEnd);
}
};
_this.onHorizontalPan = function (e) {
if (e.state === 'start' && !_this.scrolling) {
var listElement = (0, _rax.findDOMNode)(_this.refs.listSlider);
_this.bindExp(listElement);
}
};
_this.panEnd = function (e) {
console.log('[debug]:panEnd');
if (e.state === 'end') {
var duration = Date.now() - _this.startTime;
var dist = e.deltaX;
var curIndex = _this.state.curIndex;
var panDist = _this.props.panDist ? _this.props.panDist : 375;
var newIndex = curIndex;
if (Math.abs(dist) > panDist || Math.abs(dist) / duration > 0.5 && duration < 200) {
if (dist > 0) {
newIndex--;
} else {
newIndex++;
}
}
if (newIndex < 0) {
newIndex = 0;
} else if (newIndex > _this.maxIndex) {
newIndex = _this.maxIndex;
}
if (_nukeEpUtils.Detection.iOS) {
setTimeout(function () {
_this.slideTo(newIndex, 'pan');
}, 50);
} else {
_this.slideTo(newIndex, 'pan');
}
}
};
_this.slideTo = function (index, type) {
var time = 300;
if (_this.isMoving === true) return;
_this.isMoving = true;
var _this$props2 = _this.props,
onChange = _this$props2.onChange,
beforeSlide = _this$props2.beforeSlide,
forbidNavScroll = _this$props2.forbidNavScroll;
var prevIndex = _this.state.curIndex;
var moveEndCallback = function moveEndCallback() {
if (typeof onChange === 'function') {
onChange(index, prevIndex, type);
}
_this.setState({
curIndex: index,
prevIndex: prevIndex
});
_this.isMoving = false;
};
var 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 (_nukeEpUtils.Detection.epEnable) {
// 高版本系统的手机才使用transform动画
var animation = require('@weex-module/animation');
var sliderElement = _this.refs.listSlider;
var dist = 750 * index;
animation.transition((0, _rax.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)']
}, function () {});
}
moveEndCallback();
};
_this.childrenElements = []; // 存放tab主体element
_this.tabMap = {}; // 用于记录哪个tab是被激活过的状态
_this.tabPage = []; // 存放tab节点
_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;
}
return _this;
}
_createClass(Tabbar, [{
key: 'componentWillMount',
value: function componentWillMount() {
var _this2 = this;
_emitter2.default.on('scroll', function (data) {
_this2.scrolling = true;
if (_this2.scrollTimer) {
clearTimeout(_this2.scrollTimer);
}
_this2.scrollTimer = setTimeout(function () {
_this2.scrolling = false;
}, 300);
});
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var _this3 = this;
if (_nukeEpUtils.Detection.epEnable && _nukeEpUtils.Detection.iOS && this.props.epEnable) {
setTimeout(function () {
var listElement = (0, _rax.findDOMNode)(_this3.refs.listSlider);
if (listElement && listElement.ref) {
expressionBinding.enableBinding(listElement.ref, 'pan');
}
}, 500);
}
// hack for android
if (_nukeEpUtils.Detection.epEnable && _nukeEpUtils.Detection.Android) {
_emitter2.default.on('slider', function (params) {
_this3.bindExp(params.element);
});
}
}
/**
* ep绑定方法
*/
/**
* 横向滑动事件
*/
/**
* 横向滑动停止
*/
/**
* tab切换前的回调函数
* @param index 即将滚动去的位置
* @param type 触发滚动的事件类型
*/
}, {
key: 'render',
value: function render() {
var _this4 = this;
var _state = this.state,
curIndex = _state.curIndex,
prevIndex = _state.prevIndex;
var _props = this.props,
themeStyle = _props.themeStyle,
children = _props.children,
style = _props.style,
navTop = _props.navTop,
dataSource = _props.dataSource,
renderNavItem = _props.renderNavItem,
navStyle = _props.navStyle,
renderLoading = _props.renderLoading,
navFocusStyle = _props.navFocusStyle,
forceRender = _props.forceRender,
navContentStyle = _props.navContentStyle;
var slider = themeStyle.slider;
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偏移
var sliderStyle = [slider];
if (_nukeEpUtils.Detection.epEnable) {
sliderStyle.push({
width: 750 * children.length
});
if (_nukeEpUtils.Detection.iOS) {
sliderStyle.push({
transform: 'translateX(-' + 750 * this.state.curIndex + ')'
});
}
if (this.tabMap[curIndex]) {
this.tabMap[curIndex]++;
} else {
this.tabMap[curIndex] = 1;
}
}
var formatChildren = [];
if (_nukeEpUtils.Detection.epEnable) {
formatChildren = children.map(function (child, index) {
var length = children.length;
var next = circleIndex(_this4.state.curIndex + 1, length);
var prev = circleIndex(_this4.state.curIndex - 1, length);
var shouldRender = _this4.tabMap[index] === 1;
var eachTabStyle = [themeStyle.eachTab, { left: 750 * index }];
if (_this4.tabMap[index] && curIndex === index) {
_this4.tabPage[curIndex] = (0, _rax.createElement)(
_nukeView2.default,
{ style: eachTabStyle, key: index, shouldRender: shouldRender },
child
);
}
if (_this4.tabMap[index]) {
return _this4.tabPage[index];
}
if (typeof renderLoading === 'function' && (index === next || index === prev)) {
return (0, _rax.createElement)(
_nukeView2.default,
{ style: eachTabStyle, key: index },
renderLoading(index)
);
}
return null;
});
} else {
formatChildren = children.map(function (child, index) {
if (index === curIndex) {
return child;
}
return null;
});
}
var childrenElements = [(0, _rax.createElement)(
_nukeView2.default,
{
style: sliderStyle,
ref: 'listSlider',
onHorizontalPan: _nukeEpUtils.Detection.epEnable && _nukeEpUtils.Detection.iOS && this.props.epEnable ? this.onHorizontalPan : null
},
formatChildren
)];
var headerStyle = navTop ? _extends({}, navStyle, {
top: 0,
positon: 'relative'
}) : navStyle;
var NavWithProps = (0, _rax.createElement)(
_nukeView2.default,
{ style: [themeStyle.header, headerStyle] },
(0, _rax.createElement)(_nav2.default, {
ref: 'nav',
dataSource: dataSource,
renderNavItem: renderNavItem,
slideTo: function slideTo() {
return _this4.slideTo.apply(_this4, arguments);
},
style: navStyle,
contentStyle: navContentStyle,
navFocusStyle: navFocusStyle,
forceRender: forceRender
})
);
navTop ? childrenElements.unshift(NavWithProps) : childrenElements.push(NavWithProps);
return (0, _rax.createElement)(
_nukeView2.default,
{ style: [themeStyle.wrapContainer, style] },
childrenElements
);
}
}]);
return Tabbar;
}(_rax.Component);
function circleIndex(i, len) {
return (len + i % len) % len;
}
// Tabbar.propTypes = {
// };
Tabbar.defaultProps = {
epEnable: false,
navTop: true
};
Tabbar.displayName = 'Ep-Tabbar';
exports.default = (0, _nukeThemeProvider.connectStyle)(_index2.default)(Tabbar);
module.exports = exports['default'];