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.
580 lines (519 loc) • 20.9 kB
JavaScript
/*!
* react-image-video-lightbox v3.0.0
* MIT Licensed
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("react"));
else if(typeof define === 'function' && define.amd)
define(["react"], factory);
else if(typeof exports === 'object')
exports["react-image-video-lightbox"] = factory(require("react"));
else
root["react-image-video-lightbox"] = factory(root["React"]);
})(window, function(__WEBPACK_EXTERNAL_MODULE__0__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE__0__;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(2);
/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXTERNAL MODULE: external {"root":"React","commonjs2":"react","commonjs":"react","amd":"react"}
var external_root_React_commonjs2_react_commonjs_react_amd_react_ = __webpack_require__(0);
// CONCATENATED MODULE: ./src/utils.js
var settle = function settle(val, target, range) {
var lowerRange = val > target - range && val < target;
var upperRange = val < target + range && val > target;
return lowerRange || upperRange ? target : val;
};
var getPointFromTouch = function getPointFromTouch(touch) {
return {
x: touch.clientX,
y: touch.clientY
};
};
var getDistanceBetweenPoints = function getDistanceBetweenPoints(pointA, pointB) {
return Math.sqrt(Math.pow(pointA.y - pointB.y, 2) + Math.pow(pointA.x - pointB.x, 2));
};
var between = function between(min, max, value) {
return Math.min(max, Math.max(min, value));
};
// CONCATENATED MODULE: ./src/index.js
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 src_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(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 = 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 = getPointFromTouch(event.touches[0]);
var pointB = getPointFromTouch(event.touches[1]);
this.lastDistance = getDistanceBetweenPoints(pointA, pointB);
};
_proto.handlePinchMove = function handlePinchMove(event) {
event.preventDefault();
var pointA = getPointFromTouch(event.touches[0]);
var pointB = getPointFromTouch(event.touches[1]);
var distance = getDistanceBetweenPoints(pointA, pointB);
var scale = 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__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("div", {
style: {
position: 'absolute',
top: '0px',
left: '0px',
padding: '15px',
color: 'white',
fontWeight: 'bold'
}
}, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("span", null, this.state.index + 1), " / ", /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("span", null, this.props.data.length)), /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("svg", {
xmlns: "http://www.w3.org/2000/svg",
height: "36px",
viewBox: "0 0 24 24",
width: "36px",
fill: "#FFFFFF"
}, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("path", {
d: "M0 0h24v24H0z",
fill: "none"
}), /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("svg", {
xmlns: "http://www.w3.org/2000/svg",
height: "48px",
viewBox: "0 0 24 24",
width: "48px",
fill: "#FFFFFF"
}, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("path", {
d: "M0 0h24v24H0z",
fill: "none"
}), /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("path", {
d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"
}))) : /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"](external_root_React_commonjs2_react_commonjs_react_amd_react_["Fragment"], null), this.state.index + 1 != this.props.data.length ? /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("svg", {
xmlns: "http://www.w3.org/2000/svg",
height: "48px",
viewBox: "0 0 24 24",
width: "48px",
fill: "#FFFFFF"
}, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("path", {
d: "M0 0h24v24H0z",
fill: "none"
}), /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("path", {
d: "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"
}))) : /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"](external_root_React_commonjs2_react_commonjs_react_amd_react_["Fragment"], null), this.state.loading && /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_["createElement"]("div", {
style: {
margin: 'auto',
position: 'fixed'
}
}, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_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__*/external_root_React_commonjs2_react_commonjs_react_amd_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;
}(external_root_React_commonjs2_react_commonjs_react_amd_react_["Component"]);
/* harmony default export */ var src = __webpack_exports__["default"] = (src_ReactImageVideoLightbox);
/***/ })
/******/ ])["default"];
});