UNPKG

react-image-video-lightbox

Version:

A React lightbox that supports videos, images and pinch zooming on images. Optimized for mobile UI with swiping, but can be used on desktop as well.

446 lines (390 loc) 15.3 kB
"use strict"; exports.__esModule = true; exports["default"] = void 0; var React = _interopRequireWildcard(require("react")); var utils = _interopRequireWildcard(require("./utils")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } var MIN_SCALE = 1; var MAX_SCALE = 4; var SETTLE_RANGE = 0.001; var ADDITIONAL_LIMIT = 0.2; var DOUBLE_TAP_THRESHOLD = 300; var ANIMATION_SPEED = 0.04; var RESET_ANIMATION_SPEED = 0.08; var INITIAL_X = 0; var INITIAL_Y = 0; var INITIAL_SCALE = 1; var MOBILE_ICON_SIZE = 35; var DESKTOP_ICON_SIZE = 50; var ReactImageVideoLightbox = /*#__PURE__*/function (_React$Component) { _inheritsLoose(ReactImageVideoLightbox, _React$Component); function ReactImageVideoLightbox(props) { var _this; _this = _React$Component.call(this, props) || this; _this.state = { x: INITIAL_X, y: INITIAL_Y, scale: INITIAL_SCALE, width: window.innerWidth, height: window.innerHeight, index: _this.props.startIndex, swiping: false, loading: true, iconSize: window.innerWidth <= 500 ? MOBILE_ICON_SIZE : DESKTOP_ICON_SIZE }; _this.width = window.innerWidth; _this.height = window.innerHeight; _this.handleTouchStart = _this.handleTouchStart.bind(_assertThisInitialized(_this)); _this.handleTouchMove = _this.handleTouchMove.bind(_assertThisInitialized(_this)); _this.handleTouchEnd = _this.handleTouchEnd.bind(_assertThisInitialized(_this)); _this.onNavigationCallback = _this.props.onNavigationCallback && typeof _this.props.onNavigationCallback === 'function' ? _this.props.onNavigationCallback : function () {}; return _this; } var _proto = ReactImageVideoLightbox.prototype; _proto.zoomTo = function zoomTo(scale) { var _this2 = this; var frame = function frame() { if (_this2.state.scale === scale) return; var distance = scale - _this2.state.scale; var targetScale = _this2.state.scale + ANIMATION_SPEED * distance; _this2.zoom(utils.settle(targetScale, scale, SETTLE_RANGE)); _this2.animation = requestAnimationFrame(frame); }; this.animation = requestAnimationFrame(frame); }; _proto.reset = function reset() { var _this3 = this; var frame = function frame() { if (_this3.state.scale === INITIAL_SCALE && _this3.state.x === INITIAL_X && _this3.state.y === INITIAL_Y) return; var scaleDelta = INITIAL_SCALE - _this3.state.scale; var targetScale = utils.settle(_this3.state.scale + RESET_ANIMATION_SPEED * scaleDelta, INITIAL_SCALE, SETTLE_RANGE); var nextWidth = _this3.width * targetScale; var nextHeight = _this3.height * targetScale; _this3.setState({ scale: targetScale, width: nextWidth, height: nextHeight, x: INITIAL_X, y: INITIAL_Y }, function () { _this3.animation = requestAnimationFrame(frame); }); }; this.animation = requestAnimationFrame(frame); }; _proto.handleTouchStart = function handleTouchStart(event) { this.animation && cancelAnimationFrame(this.animation); if (event.touches.length === 2) this.handlePinchStart(event); if (event.touches.length === 1) this.handleTapStart(event); }; _proto.handleTouchMove = function handleTouchMove(event) { if (event.touches.length === 2) this.handlePinchMove(event); if (event.touches.length === 1) this.handlePanMove(event); }; _proto.handleTouchEnd = function handleTouchEnd(event) { if (event.touches.length > 0) return null; if (this.state.scale > MAX_SCALE) return this.zoomTo(MAX_SCALE); if (this.state.scale < MIN_SCALE) return this.zoomTo(MIN_SCALE); if (this.lastTouchEnd && this.lastTouchEnd + DOUBLE_TAP_THRESHOLD > event.timeStamp) { this.reset(); } if (this.state.swiping && this.state.scale === 1) { this.handleSwipe(event); } this.lastTouchEnd = event.timeStamp; }; _proto.handleSwipe = function handleSwipe(event) { var swipeDelta = event.changedTouches[0].clientX - this.swipeStartX; if (swipeDelta < -(this.width / 3)) { this.swipeRight(); } else if (swipeDelta > this.width / 3) { this.swipeLeft(); } else { this.reset(); } }; _proto.swipeLeft = function swipeLeft() { var _this4 = this; var currentIndex = this.state.index; if (currentIndex > 0) { setTimeout(function () { _this4.setState({ index: currentIndex - 1, swiping: false, x: INITIAL_X, loading: true }, function () { return _this4.onNavigationCallback(currentIndex - 1); }); }, 500); } else { this.reset(); } }; _proto.swipeRight = function swipeRight() { var _this5 = this; var currentIndex = this.state.index; if (currentIndex < this.props.data.length - 1) { setTimeout(function () { _this5.setState({ index: currentIndex + 1, swiping: false, x: INITIAL_X, loading: true }, function () { return _this5.onNavigationCallback(currentIndex + 1); }); }, 500); } else { this.reset(); } }; _proto.handleTapStart = function handleTapStart(event) { this.swipeStartX = event.touches[0].clientX; this.swipeStartY = event.touches[0].clientY; if (this.state.scale === 1) { this.setState({ swiping: true }); } }; _proto.handlePanMove = function handlePanMove(event) { if (this.state.scale === 1) { this.setState({ x: event.touches[0].clientX - this.swipeStartX }); } else { event.preventDefault(); this.setState({ x: event.touches[0].clientX - this.swipeStartX, y: event.touches[0].clientY - this.swipeStartY }); } }; _proto.handlePinchStart = function handlePinchStart(event) { var pointA = utils.getPointFromTouch(event.touches[0]); var pointB = utils.getPointFromTouch(event.touches[1]); this.lastDistance = utils.getDistanceBetweenPoints(pointA, pointB); }; _proto.handlePinchMove = function handlePinchMove(event) { event.preventDefault(); var pointA = utils.getPointFromTouch(event.touches[0]); var pointB = utils.getPointFromTouch(event.touches[1]); var distance = utils.getDistanceBetweenPoints(pointA, pointB); var scale = utils.between(MIN_SCALE - ADDITIONAL_LIMIT, MAX_SCALE + ADDITIONAL_LIMIT, this.state.scale * (distance / this.lastDistance)); this.zoom(scale); this.lastDistance = distance; }; _proto.zoom = function zoom(scale) { var nextWidth = this.width * scale; var nextHeight = this.height * scale; this.setState({ width: nextWidth, height: nextHeight, scale: scale }); }; _proto.getResources = function getResources() { var _this6 = this; var items = []; var data = this.props.data; for (var i = 0; i < data.length; i++) { var resource = data[i]; if (resource.type === 'photo') { items.push( /*#__PURE__*/React.createElement("img", { key: i, alt: resource.altTag, src: resource.url, style: { pointerEvents: this.state.scale === 1 ? 'auto' : 'none', maxWidth: '100%', maxHeight: '100%', transform: "translate(" + this.state.x + "px, " + this.state.y + "px) scale(" + this.state.scale + ")", transition: 'transform 0.5s ease-out' }, onLoad: function onLoad() { _this6.setState({ loading: false }); } })); } if (resource.type === 'video') { items.push( /*#__PURE__*/React.createElement("iframe", { key: i, width: "560", height: "315", src: resource.url, frameBorder: "0", allow: "autoplay; encrypted-media", title: resource.title, allowFullScreen: true, style: { pointerEvents: this.state.scale === 1 ? 'auto' : 'none', maxWidth: '100%', maxHeight: '100%', transform: "translate(" + this.state.x + "px, " + this.state.y + "px)", transition: 'transform 0.5s ease-out' }, onLoad: function onLoad() { _this6.setState({ loading: false }); } })); } } return items; }; _proto.UNSAFE_componentWillMount = function UNSAFE_componentWillMount() { var _this7 = this; window.addEventListener('resize', function () { if (window.innerWidth <= 500) { _this7.setState({ iconSize: MOBILE_ICON_SIZE }); } else { _this7.setState({ iconSize: DESKTOP_ICON_SIZE }); } }); }; _proto.componentWillUnmount = function componentWillUnmount() { var _this8 = this; window.removeEventListener('resize', function () { if (window.innerWidth <= 500) { _this8.setState({ iconSize: MOBILE_ICON_SIZE }); } else { _this8.setState({ iconSize: DESKTOP_ICON_SIZE }); } }); }; _proto.render = function render() { var _this9 = this; var resources = this.getResources(); return /*#__PURE__*/React.createElement("div", { onTouchStart: this.handleTouchStart, onTouchMove: this.handleTouchMove, onTouchEnd: this.handleTouchEnd, style: { top: '0px', left: '0px', overflow: 'hidden', position: 'fixed', display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'row', height: '100%', width: '100%', backgroundColor: 'rgba(0,0,0,1)' } }, this.props.showResourceCount && /*#__PURE__*/React.createElement("div", { style: { position: 'absolute', top: '0px', left: '0px', padding: '15px', color: 'white', fontWeight: 'bold' } }, /*#__PURE__*/React.createElement("span", null, this.state.index + 1), " / ", /*#__PURE__*/React.createElement("span", null, this.props.data.length)), /*#__PURE__*/React.createElement("div", { style: { position: 'absolute', top: '0px', right: '0px', padding: '10px', color: '#FFFFFF', cursor: 'pointer', fontSize: this.state.iconSize * 0.8 + "px" }, onClick: this.props.onCloseCallback }, /*#__PURE__*/React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", height: "36px", viewBox: "0 0 24 24", width: "36px", fill: "#FFFFFF" }, /*#__PURE__*/React.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }), /*#__PURE__*/React.createElement("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }))), this.state.index + 1 != 1 ? /*#__PURE__*/React.createElement("div", { style: { position: 'absolute', left: '0px', zIndex: 1, color: '#FFFFFF', cursor: 'pointer', fontSize: this.state.iconSize + "px" }, onClick: function onClick() { _this9.swipeLeft(); } }, /*#__PURE__*/React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", height: "48px", viewBox: "0 0 24 24", width: "48px", fill: "#FFFFFF" }, /*#__PURE__*/React.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }), /*#__PURE__*/React.createElement("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }))) : /*#__PURE__*/React.createElement(React.Fragment, null), this.state.index + 1 != this.props.data.length ? /*#__PURE__*/React.createElement("div", { style: { position: 'absolute', right: '0px', zIndex: 1, color: '#FFFFFF', cursor: 'pointer', fontSize: this.state.iconSize + "px" }, onClick: function onClick() { _this9.swipeRight(); } }, /*#__PURE__*/React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", height: "48px", viewBox: "0 0 24 24", width: "48px", fill: "#FFFFFF" }, /*#__PURE__*/React.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }), /*#__PURE__*/React.createElement("path", { d: "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" }))) : /*#__PURE__*/React.createElement(React.Fragment, null), this.state.loading && /*#__PURE__*/React.createElement("div", { style: { margin: 'auto', position: 'fixed' } }, /*#__PURE__*/React.createElement("style", null, "@keyframes react_image_video_spinner {\n 0% {\n transform: translate3d(-50 %, -50 %, 0) rotate(0deg);\n }\n 100% {\n transform: translate3d(-50%, -50%, 0) rotate(360deg);\n }\n }"), /*#__PURE__*/React.createElement("div", { style: { animation: '1.0s linear infinite react_image_video_spinner', border: 'solid 5px #ffffff', borderBottomColor: '#cfd0d1', borderRadius: '50%', height: 30, width: 30, position: 'fixed', transform: 'translate3d(-50%, -50%, 0)' } })), resources[this.state.index]); }; return ReactImageVideoLightbox; }(React.Component); var _default = ReactImageVideoLightbox; exports["default"] = _default; module.exports = exports.default;