react-mgallery
Version:
React component for mobile web gallery
408 lines (345 loc) • 14 kB
JavaScript
'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();
}
});