UNPKG

react-image-video-viewer

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.

448 lines (447 loc) 16.9 kB
"use strict"; exports.__esModule = true; exports["default"] = void 0; var React = _interopRequireWildcard(require("react")); var utils = _interopRequireWildcard(require("./utils")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } function _inheritsLoose(t, o) { t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } 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.01; var RESET_ANIMATION_SPEED = 0.01; var INITIAL_X = 0; var INITIAL_Y = 0; var INITIAL_SCALE = 1; var MOBILE_ICON_SIZE = 35; var DESKTOP_ICON_SIZE = 50; var ESCAPE_KEY = 27; var RIGHT_KEY = 39; var LEFT_KEY = 37; var ReactImageVideoLightbox = /*#__PURE__*/function (_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, overflow: undefined }; _this.width = window.innerWidth; _this.height = window.innerHeight; _this.handleTouchStart = _this.handleTouchStart.bind(_this); _this.handleTouchMove = _this.handleTouchMove.bind(_this); _this.handleTouchEnd = _this.handleTouchEnd.bind(_this); _this.onResize = _this.onResize.bind(_this); _this.handleKeyDown = _this.handleKeyDown.bind(_this); _this.onNavigationCallback = _this.props.onNavigationCallback && typeof _this.props.onNavigationCallback === 'function' ? _this.props.onNavigationCallback : function () {}; return _this; } _inheritsLoose(ReactImageVideoLightbox, _React$Component); 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 _frame2 = 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(_frame2); }); }; this.animation = requestAnimationFrame(_frame2); }; _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 && event.touches && 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 (event && 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 ? event.timeStamp : undefined; }; _proto.handleSwipe = function handleSwipe(event) { var swipeDelta = event ? event.changedTouches[0].clientX - this.swipeStartX : 0; 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); }); }, 0); } 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); }); }, 0); } 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, className: "img-viewer", 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') { if (resource.url && (resource.url.includes('.mp4') || resource.url.includes('.mov'))) { items.push(/*#__PURE__*/React.createElement("video", { onLoadStart: function onLoadStart() { _this6.setState({ loading: false }); }, onLoadedData: function onLoadedData() { _this6.setState({ loading: false }); }, key: i, className: "video-viewer", preload: "metadata", poster: resource.poster ? resource.poster : null, playsInline: true, controls: true, autoPlay: resource.autoPlay ? true : false, src: resource.url }, /*#__PURE__*/React.createElement("source", { src: resource.url + "#t=0.1", type: "video/webm" }), /*#__PURE__*/React.createElement("source", { src: resource.url + "#t=0.1", type: "video/ogg" }), /*#__PURE__*/React.createElement("source", { src: resource.url + "#t=0.1", type: "video/mp4" }), /*#__PURE__*/React.createElement("source", { src: resource.url + "#t=0.1", type: "video/3gp" }))); } else { 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.handleKeyDown = function handleKeyDown(event) { switch (event.keyCode) { case ESCAPE_KEY: document.body.style.overflow = this.state.overflow ? this.state.overflow : null; this.props.onCloseCallback(); break; case RIGHT_KEY: this.swipeRight(); break; case LEFT_KEY: this.swipeLeft(); break; default: break; } }; _proto.onResize = function onResize() { if (window.innerWidth <= 500) { this.setState({ iconSize: MOBILE_ICON_SIZE }); } else { this.setState({ iconSize: DESKTOP_ICON_SIZE }); } }; _proto.componentDidMount = function componentDidMount() { this.setState({ overflow: document.body.style.overflow }); document.body.style.overflow = 'hidden'; window.addEventListener('resize', this.onResize); document.addEventListener("keydown", this.handleKeyDown); }; _proto.componentWillUnmount = function componentWillUnmount() { document.body.style.overflow = this.state.overflow ? this.state.overflow : null; window.removeEventListener('resize', this.onResize); document.removeEventListener("keydown", this.handleKeyDown); }; _proto.render = function render() { var _this7 = 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%', background: 'rgba(0, 0, 0, 0.5019607843)', backdropFilter: 'blur(2.1428571429em)', zIndex: 100 } }, 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: function onClick() { document.body.style.overflow = _this7.state.overflow ? _this7.state.overflow : null; _this7.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() { _this7.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() { _this7.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 = exports["default"] = ReactImageVideoLightbox; module.exports = exports.default;