UNPKG

react-mgallery

Version:

React component for mobile web gallery

408 lines (345 loc) 14 kB
'use strict'; 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 _react = require('react'); var _react2 = _interopRequireDefault(_react); require('./style.less'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } // 全屏视窗 var _viewSize = { w: window.innerWidth, h: window.innerHeight }, _viewCenterPoint = { x: _viewSize.w / 2, y: _viewSize.h / 2 }; var _direction = void 0, // 'x', 'y', 0 _initialDistance = void 0, _initialScale = void 0, _initialScalerX = void 0, _initialScalerY = void 0, _initialCenterPoint = void 0, _lastCenterPoint = void 0; var DIRECTION_CHECK_OFFSET = 9; // 计算两点间距 function _calculatePointsDistance(p1, p2) { var x0 = Math.abs(p1.x - p2.x); var y0 = Math.abs(p1.y - p2.y); return Math.round(Math.sqrt(x0 * x0 + y0 * y0)); } function _calculateCenterPoint(p1, p2) { return { x: (p1.x + p2.x) / 2, y: (p1.y + p1.y) / 2 }; } function _touchToPoint(touch) { return { x: touch.pageX, y: touch.pageY }; } function _limit(min, val, max) { return Math.max(min, Math.min(max, val)); } // 计算图片缩放后的尺寸超出视窗边界 function _calculateBounds(destScale) { var item = arguments.length <= 1 || arguments[1] === undefined ? _viewSize : arguments[1]; var delta = { x: (item.w * destScale - _viewSize.w) / 2, y: (item.h * destScale - _viewSize.h) / 2 }; // 计算缩放后拖拽边界 var bounds = { minX: delta.x < 0 ? 0 : -delta.x, maxX: delta.x < 0 ? 0 : delta.x, minY: delta.y < 0 ? 0 : -delta.y, maxY: delta.y < 0 ? 0 : delta.y }; if (item.long) { bounds.maxY = item.h * (destScale - 1) / 2; bounds.minY = -(item.h - _viewSize.h + item.h * (destScale - 1) / 2); } return bounds; } exports.default = _react2.default.createClass({ displayName: 'react-mgallery', getDefaultProps: function getDefaultProps() { return { gap: 30, // 切换过程中相邻图片的间隙 actionDistance: 10, // 触发切换图片需要拖拽的距离 currentIndex: 0, maxScale: 2, minScale: 1, onLoading: function onLoading() {}, onLoaded: function onLoaded() {} }; }, getInitialState: function getInitialState() { return { currentIndex: this.props.currentIndex || 0, lazyImgs: {}, imgData: {}, x0: 0, // x轴滑动偏移量 scale: 1, scalerX: 0, scalerY: 0, transition: 0 }; }, init: function init(touches) { var p1 = _touchToPoint(touches[0]); var _state = this.state; var scale = _state.scale; var scalerX = _state.scalerX; var scalerY = _state.scalerY; if (touches.length > 1) { var p2 = _touchToPoint(touches[1]); // 缓存两个触点间初始距离 _initialDistance = _calculatePointsDistance(p1, p2); _initialCenterPoint = _calculateCenterPoint(p1, p2); } else { _initialCenterPoint = p1; } _direction = 0; _lastCenterPoint = _viewCenterPoint; // tap聚焦效果 _initialScale = scale; _initialScalerX = scalerX; _initialScalerY = scalerY; }, touchStart: function touchStart(e) { e.preventDefault(); this.init(e.touches); }, touchMove: function touchMove(e) { e.preventDefault(); this.setState({ transition: 0 }); var touches = e.touches; var p1 = _lastCenterPoint = _touchToPoint(touches[0]); if (touches.length > 1) { // 缩放 var p2 = _touchToPoint(touches[1]); var scale = _calculatePointsDistance(p1, p2) / _initialDistance; var destScale = scale * _initialScale; _lastCenterPoint = _calculateCenterPoint(p1, p2); this.applyScale(destScale); } else { // 滑动 var _state2 = this.state; var _scale = _state2.scale; var currentIndex = _state2.currentIndex; var imgData = _state2.imgData; var _props = this.props; var gap = _props.gap; var imgs = _props.imgs; var scalerX = p1.x - _initialCenterPoint.x + _initialScalerX; var scalerY = p1.y - _initialCenterPoint.y + _initialScalerY; var item = imgData[currentIndex]; var _calculateBounds2 = _calculateBounds(_scale, item); var minX = _calculateBounds2.minX; var minY = _calculateBounds2.minY; var maxX = _calculateBounds2.maxX; var maxY = _calculateBounds2.maxY; var x0 = 0; // 限制图片间间隙 minX -= gap; maxX += gap; if (scalerX > maxX) { x0 = scalerX - maxX; scalerX = maxX; } else if (scalerX < minX) { x0 = scalerX - minX; scalerX = minX; } // 第一张图限制右划切换 if (currentIndex == 0) x0 = Math.min(0, x0); // 最后一张图限制左划切换 if (currentIndex == imgs.length - 1) x0 = Math.max(0, x0); // 应用边界 scalerY = _limit(minY, scalerY, maxY); var nextState = { x0: x0, scalerX: scalerX, scalerY: scalerY }; // 水平切换发生的阈值 if (Math.abs(scalerX) < DIRECTION_CHECK_OFFSET) { nextState.scalerX = 0; } // 未缩放 if (_scale == 1 && item && item.long) { // 判断滚动方向 if (!_direction) { var deltaX = Math.abs(scalerX - _initialScalerX); var deltaY = Math.abs(scalerY - _initialScalerY); if (deltaY > DIRECTION_CHECK_OFFSET) _direction = 'y';else if (deltaX > DIRECTION_CHECK_OFFSET) _direction = 'x'; } if (_direction == 'x') { // 发生水平滚动时,限制垂直移动 delete nextState.scalerY; } else if (_direction == 'y') { // 发生垂直滚动时,限制水平移动 nextState.scalerX = nextState.x0 = 0; } } this.setState(nextState); } }, touchEnd: function touchEnd(e) { var _state3 = this.state; var scale = _state3.scale; var currentIndex = _state3.currentIndex; var x0 = _state3.x0; var imgData = _state3.imgData; var _props2 = this.props; var maxScale = _props2.maxScale; var minScale = _props2.minScale; var actionDistance = _props2.actionDistance; var imgs = _props2.imgs; var touches = e.touches; if (touches.length) { // 为剩下的触点重新初始化 this.init(touches); } else { // 触摸结束 var transition = 1; var nextIndex = currentIndex; // 判断切换图片 if (x0 < -actionDistance) nextIndex++;else if (x0 > actionDistance) nextIndex--; nextIndex = _limit(0, nextIndex, imgs.length - 1); if (nextIndex != currentIndex) { // 切换 this.setState({ transition: transition, currentIndex: nextIndex, // 重置 scale: 1, scalerX: 0, scalerY: 0, x0: 0 }); this.preLoadImg(); } else { // 应用缩放及滑动边界 var destScale = _limit(minScale, scale, maxScale); var bounds = _calculateBounds(destScale, imgData[currentIndex]); this.applyScale(destScale, bounds, transition); } } }, applyScale: function applyScale(destScale, bounds, transition) { var _state4 = this.state; var imgData = _state4.imgData; var currentIndex = _state4.currentIndex; var item = imgData[currentIndex]; var baseY = item.long ? item.h / 2 : _viewCenterPoint.y; var baseX = _viewCenterPoint.x; // 计算缩放补偿量 // 触摸中点移动距离 - 缩放导致的触摸中点偏移量 var scale = destScale / _initialScale; // 相对缩放率,以触摸开始为1 var scalerX = (_initialScalerX + _lastCenterPoint.x - _initialCenterPoint.x) * scale - (_lastCenterPoint.x - baseX) * (scale - 1); var scalerY = (_initialScalerY + _lastCenterPoint.y - _initialCenterPoint.y) * scale - (_lastCenterPoint.y - baseY) * (scale - 1); if (bounds) { // 应用边界 scalerX = _limit(bounds.minX, scalerX, bounds.maxX); scalerY = _limit(bounds.minY, scalerY, bounds.maxY); } // 计算缩放 this.setState({ scale: destScale, scalerX: scalerX, scalerY: scalerY, x0: 0, transition: transition }); }, preLoadImg: function preLoadImg(index) { var _state5 = this.state; var currentIndex = _state5.currentIndex; var lazyImgs = _state5.lazyImgs; index = index || currentIndex; var _lazyImgs = {}; var nextIndex = Math.min(this.props.imgs.length - 1, index + 1); var prevIndex = Math.max(0, index - 1); _lazyImgs[index] = 1; _lazyImgs[prevIndex] = 1; _lazyImgs[nextIndex] = 1; this.setState({ lazyImgs: _extends({}, lazyImgs, _lazyImgs) }); }, imgLoaded: function imgLoaded(e, i) { var _state6 = this.state; var imgData = _state6.imgData; var currentIndex = _state6.currentIndex; if (imgData[i]) return; if (i == currentIndex) this.props.onLoaded(); // 记录图片初始尺寸 var img = e.target; var width = img.width; var height = img.height; var naturalWidth = img.naturalWidth; var naturalHeight = img.naturalHeight; var fitH = _viewSize.w / naturalWidth * naturalHeight; // 适应屏幕宽度后的高度 var long = fitH > _viewSize.h; width = Math.min(naturalWidth, _viewSize.w); height = width * naturalHeight / naturalWidth; this.setState({ imgData: _extends(_defineProperty({}, i, { w: width, h: height, long: long }), imgData) }); }, render: function render() { var imgs = this.props.imgs; var _state7 = this.state; var x0 = _state7.x0; var scale = _state7.scale; var scalerX = _state7.scalerX; var scalerY = _state7.scalerY; var currentIndex = _state7.currentIndex; var transition = _state7.transition; var lazyImgs = _state7.lazyImgs; var imgData = _state7.imgData; var touchStart = this.touchStart; var touchMove = this.touchMove; var touchEnd = this.touchEnd; var imgLoaded = this.imgLoaded; return _react2.default.createElement( 'div', { className: 'mgallery' + (transition ? ' transition' : ''), ref: 'holder', onTouchStart: touchStart, onTouchMove: touchMove, onTouchEnd: touchEnd }, _react2.default.createElement( 'div', { className: 'mgallery-container', style: { width: imgs.length * 100 + '%', WebkitTransform: 'translateX(' + (x0 - currentIndex * _viewSize.w) + 'px)' } }, imgs.map(function (img, i) { var item = imgData[i]; var scalerStyle = i == currentIndex && item ? { WebkitTransform: 'translate3d(' + scalerX + 'px,' + scalerY + 'px,0) scale(' + scale + ')' } : {}; var itemClass = 'mgallery-item '; if (item && item.long) itemClass += 'long'; return _react2.default.createElement( 'div', { key: i, className: itemClass }, _react2.default.createElement( 'div', { className: 'mgallery-scaler', style: scalerStyle }, lazyImgs[i] ? _react2.default.createElement('img', { src: img, onLoad: function onLoad(e) { return imgLoaded(e, i); } }) : null ) ); }) ), _react2.default.createElement( 'div', { className: 'mgallery-dots' }, imgs.length > 1 ? imgs.map(function (img, i) { return _react2.default.createElement('i', { key: i, className: i == currentIndex ? 'on' : '' }); }) : null ) ); }, componentWillMount: function componentWillMount() { this.preLoadImg(); this.props.onLoading(); } });