├── .babelrc ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── dist ├── ReactIntense.css ├── ReactIntense.js └── polyfills.js ├── lib ├── ReactIntense.css ├── ReactIntense.tsx └── polyfills.js ├── package-lock.json ├── package.json ├── src ├── img │ ├── favicon.png │ ├── horse.jpg │ ├── horse_thumb.jpg │ ├── rain.jpg │ ├── rain_thumb.jpg │ ├── temple.jpg │ └── temple_thumb.jpg ├── index.d.ts ├── index.html ├── index.tsx └── styles.css └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-transform-modules-commonjs"], 3 | "presets": [ 4 | "@babel/preset-env", 5 | "@babel/preset-react", 6 | "@babel/preset-typescript", 7 | "minify" 8 | ], 9 | "comments": false 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: [push] 3 | permissions: 4 | contents: write 5 | jobs: 6 | build-and-deploy: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v4 11 | with: 12 | node-version: 18 13 | - name: Install and Build 🔧 14 | run: | 15 | npm ci 16 | npm run build -- --public-url https://bryce.io/react-intense 17 | - name: Deploy 🚀 18 | uses: JamesIves/github-pages-deploy-action@v4 19 | with: 20 | folder: public -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | .parcel-cache 4 | .DS_STORE -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "printWidth": 120, 6 | "singleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2017 Bryce Dorn 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-intense 2 | 3 | [![npm version](https://badge.fury.io/js/react-intense.svg)](https://badge.fury.io/js/react-intense) [![Build and Deploy](https://github.com/brycedorn/react-intense/actions/workflows/deploy.yml/badge.svg)](https://github.com/brycedorn/react-intense/actions/workflows/deploy.yml) 4 | 5 | This component is a port of [Intense Image Viewer](http://tholman.com/intense-images/) for use with React. Now with hooks! 6 | 7 | [Demo](https://bryce.io/react-intense). 8 | 9 | ## Usage 10 | 11 | Simply replace your `` element with a `` component: 12 | 13 | ```javascript 14 | import ReactIntense from 'react-intense' 15 | 16 | ... 17 | 18 | 19 | ``` 20 | 21 | Or for more flexibility, use the provided `useIntenseMaximize` hook: 22 | 23 | ```javascript 24 | import { useIntenseMaximize } from 'react-intense' 25 | 26 | ... 27 | 28 | const { maximize, renderViewer } = useIntenseMaximize(props); 29 | 30 | return ( 31 | <> 32 | 33 | {renderViewer()} 34 | 35 | ); 36 | ``` 37 | 38 | 39 | ## Optional Props 40 | 41 | | Name | Type | Description | 42 | | --- | --- | --- | 43 | | `title` | `string` | Renders in corner in maximized view. | 44 | | `caption` | `string` | Renders below title in maximized view. | 45 | | `vertical` | `boolean` | Images lock to scrolling either horizontally (default) or vertically. | 46 | | `moveSpeed` | `number` | How fast to scroll images when following mouse. | 47 | | `loader` | `React.ReactNode` | The loading spinner to use. | 48 | 49 | ## Styling 50 | 51 | Feel free to use and/or customize the provided styles in `dist/ReactIntense.css`. 52 | 53 | ## Issues 54 | 55 | If you find any issues with this component, please [report](https://github.com/brycedorn/react-intense/issues) them! 56 | 57 | ## Thanks 58 | 59 | - [Tim Holman](https://github.com/tholman) 60 | - [Paul Irish](https://gist.github.com/paulirish/1579671) 61 | - [loading.io](http://loading.io) 62 | -------------------------------------------------------------------------------- /dist/ReactIntense.css: -------------------------------------------------------------------------------- 1 | .ri-wrapper, 2 | .ri-cursor-plus { 3 | cursor: 4 | url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4M0UzRkZGRUY4ODQxMUUzQjg5REQwNUQyQTFENTVGOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4M0UzRkZGRkY4ODQxMUUzQjg5REQwNUQyQTFENTVGOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjUyNjc4QUY1Rjg0QjExRTNCODlERDA1RDJBMUQ1NUY5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjUyNjc4QUY2Rjg0QjExRTNCODlERDA1RDJBMUQ1NUY5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x6eaoAAABeVJREFUeNrcWn1M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') 5 | 25 25, 6 | no-drop; 7 | } 8 | 9 | .ri-container, 10 | .ri-cursor-x { 11 | cursor: 12 | url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3Q0IyNDI3M0FFMkYxMUUzOEQzQUQ5NTMxMDAwQjJGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo3Q0IyNDI3NEFFMkYxMUUzOEQzQUQ5NTMxMDAwQjJGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjdDQjI0MjcxQUUyRjExRTM4RDNBRDk1MzEwMDBCMkZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjdDQjI0MjcyQUUyRjExRTM4RDNBRDk1MzEwMDBCMkZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+soZ1WgAABp5JREFUeNrcWn9M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') 13 | 25 25, 14 | no-drop; 15 | } 16 | 17 | .ri-wrapper .ri-clicked { 18 | box-shadow: inset 0 0 0 500px rgba(0, 0, 0, 0.2); 19 | } 20 | 21 | .ri-container { 22 | background-color: rgba(0, 0, 0, 0.8); 23 | width: 100%; 24 | height: 100%; 25 | position: fixed; 26 | top: 0px; 27 | left: 0px; 28 | overflow: hidden; 29 | z-index: 999999; 30 | margin: 0px; 31 | transition: opacity 150ms cubic-bezier(0, 0, 0.26, 1); 32 | } 33 | 34 | .ri-caption-container { 35 | font-family: Georgia, Times, 'Times New Roman', serif; 36 | position: fixed; 37 | bottom: 0px; 38 | left: 0px; 39 | padding: 20px; 40 | color: #fff; 41 | word-spacing: 0.2px; 42 | text-shadow: -1px 0px 1px rgba(0, 0, 0, 0.4); 43 | } 44 | 45 | .ri-title { 46 | margin: 0px; 47 | padding: 0px; 48 | font-weight: normal; 49 | font-size: 40px; 50 | letter-spacing: 0.5px; 51 | line-height: 35px; 52 | text-align: left; 53 | } 54 | 55 | .ri-caption { 56 | margin: 0px; 57 | padding: 0px; 58 | font-weight: normal; 59 | font-size: 20px; 60 | letter-spacing: 0.1px; 61 | max-width: 500px; 62 | text-align: left; 63 | background: none; 64 | margin-top: 5px; 65 | } 66 | 67 | .ri-trigger { 68 | width: 200px; 69 | height: 200px; 70 | background-size: cover; 71 | background-position: 50% 50%; 72 | display: flex; 73 | justify-content: center; 74 | align-items: center; 75 | } 76 | 77 | .ri-loader { 78 | display: inline-block; 79 | width: 50px; 80 | height: 50px; 81 | border: 3px solid rgba(255, 255, 255, 0.2); 82 | border-radius: 50%; 83 | border-top-color: #fff; 84 | animation: spin 1s ease-in-out infinite; 85 | -webkit-animation: spin 1s ease-in-out infinite; 86 | } 87 | @keyframes spin { 88 | to { 89 | -webkit-transform: rotate(360deg); 90 | } 91 | } 92 | @-webkit-keyframes spin { 93 | to { 94 | -webkit-transform: rotate(360deg); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /dist/ReactIntense.js: -------------------------------------------------------------------------------- 1 | "use strict";var _react=_interopRequireWildcard(require("react"));function _typeof(a){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},_typeof(a)}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=void 0,exports.useIntenseMaximize=useIntenseMaximize;function _getRequireWildcardCache(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(_getRequireWildcardCache=function(a){return a?c:b})(a)}function _interopRequireWildcard(b,c){if(!c&&b&&b.__esModule)return b;if(null===b||"object"!=_typeof(b)&&"function"!=typeof b)return{default:b};var d=_getRequireWildcardCache(c);if(d&&d.has(b))return d.get(b);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in b)if("default"!=a&&Object.prototype.hasOwnProperty.call(b,a)){var g=f?Object.getOwnPropertyDescriptor(b,a):null;g&&(g.get||g.set)?Object.defineProperty(e,a,g):e[a]=b[a]}return e["default"]=b,d&&d.set(b,e),e}function _slicedToArray(a,b){return _arrayWithHoles(a)||_iterableToArrayLimit(a,b)||_unsupportedIterableToArray(a,b)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(a,b){if(a){if("string"==typeof a)return _arrayLikeToArray(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);return"Object"===c&&a.constructor&&(c=a.constructor.name),"Map"===c||"Set"===c?Array.from(a):"Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c)?_arrayLikeToArray(a,b):void 0}}function _arrayLikeToArray(a,b){(null==b||b>a.length)&&(b=a.length);for(var c=0,d=Array(b);cMIN_DIST_THRESHOLD){var f="",g=-1;if(s){if(d!==y){var h,i=c(d,window.innerHeight),j=window.innerHeight-(null===(h=P.current)||void 0===h?void 0:h.offsetHeight);f="translate3d(0px, ".concat(j*i,"px, 0px)"),g=d}}else if(b!==y){var k,l=c(b,window.innerWidth),m=window.innerWidth-(null===(k=P.current)||void 0===k?void 0:k.offsetWidth);f="translate3d(".concat(m*l,"px, 0px, 0px)"),g=b}J(f),z(g),F.current={x:b,y:d}}}}function c(a,b){return t?(b-a)/b:a/b}function d(){var a,b,c;null===(a=P.current)||void 0===a||a.addEventListener("mousemove",function(a){return l(a.clientX,a.clientY)}),null===(b=P.current)||void 0===b||b.addEventListener("touchmove",function(a){return m(a.touches[0])}),null===(c=P.current)||void 0===c||c.addEventListener("click",n),window.addEventListener("keyup",j),i()}function e(){var a,b,c;null===(a=P.current)||void 0===a||a.removeEventListener("mousemove",function(a){return l(a.clientX,a.clientY)}),null===(b=P.current)||void 0===b||b.removeEventListener("touchmove",function(a){return m(a.touches[0])}),null===(c=P.current)||void 0===c||c.removeEventListener("click",n),window.removeEventListener("keyup",j)}function f(){document.body.style.overflow="hidden"}function g(){document.body.style.overflow=initialOverflowValue}function h(){O.current&&window.cancelAnimationFrame(O.current),O.current=void 0,e(),g()}function i(){var a=window.requestAnimationFrame(i);O.current=a,b()}function j(a){a.preventDefault(),a.keyCode===KEYCODE_ESC&&n()}function k(){D(!0),d()}function l(a,b){E.current={x:a,y:b}}function m(a){E.current={x:a.clientX,y:a.clientY}}function n(){N(!1),h()}var o=a.src,p=a.onClick,q=a.moveSpeed,r=void 0===q?DEFAULT_MOVE_SPEED:q,s=a.vertical,t=a.invert,u=a.caption,v=a.title,w=(0,_react.useState)(0),x=_slicedToArray(w,2),y=x[0],z=x[1],A=(0,_react.useState)(!1),B=_slicedToArray(A,2),C=B[0],D=B[1],E=(0,_react.useRef)({x:0,y:0}),F=(0,_react.useRef)({x:window.innerWidth/2,y:window.innerHeight/2}),G=(0,_react.useState)(""),H=_slicedToArray(G,2),I=H[0],J=H[1],K=(0,_react.useState)(!1),L=_slicedToArray(K,2),M=L[0],N=L[1],O=(0,_react.useRef)(),P=(0,_react.useRef)(null);return{renderViewer:function(){var a={height:s?"":window.innerHeight,MozTransform:I,transform:I,WebkitTransform:I,width:s?window.innerWidth:""};return M?_react["default"].createElement("figure",{className:"ri-container",style:{opacity:C?1:0}},_react["default"].createElement("img",{ref:P,src:o,style:a,onLoad:k}),_react["default"].createElement("figcaption",{className:"ri-caption-container"},_react["default"].createElement("h1",{className:"ri-title"},v),_react["default"].createElement("h2",{className:"ri-caption"},u))):null},maximize:function(a){a.preventDefault(),p&&p(a),E.current={x:a.clientX,y:a.clientY},N(!0),f()},triggered:M,transform:I,loaded:C,onLoad:k,imgRef:P}}function ReactIntense(a){var b=a.className,c=a.renderLoader,d=void 0===c?function(){return _react["default"].createElement("div",{className:"ri-loader"})}:c,e=a.src,f=a.thumbnailSrc,g=a.vertical,h=a.caption,i=a.title,j=useIntenseMaximize(a),k=j.triggered,l=j.transform,m=j.loaded,n=j.onLoad,o=j.maximize,p=j.imgRef;return _react["default"].createElement("div",{className:"ri-wrapper"},_react["default"].createElement("div",{className:"".concat(b," ri-trigger ").concat(k?" ri-clicked":""),onClick:o,style:{backgroundImage:"url(".concat(f||e,")")}},k?d():null),function(){var a={height:g?"":window.innerHeight,MozTransform:l,transform:l,WebkitTransform:l,width:g?window.innerWidth:""};return k?_react["default"].createElement("figure",{className:"ri-container",style:{opacity:m?1:0}},_react["default"].createElement("img",{ref:p,src:e,style:a,onLoad:n}),_react["default"].createElement("figcaption",{className:"ri-caption-container"},_react["default"].createElement("h1",{className:"ri-title"},i),_react["default"].createElement("h2",{className:"ri-caption"},h))):null}())}var _default=exports["default"]=ReactIntense; -------------------------------------------------------------------------------- /dist/polyfills.js: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/paulirish/1579671 2 | (function () { 3 | var lastTime = 0; 4 | var vendors = ['ms', 'moz', 'webkit', 'o']; 5 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 6 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 7 | window.cancelAnimationFrame = 8 | window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; 9 | } 10 | 11 | if (!window.requestAnimationFrame) 12 | window.requestAnimationFrame = function (callback, element) { 13 | var currTime = new Date().getTime(); 14 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 15 | var id = window.setTimeout(function () { 16 | callback(currTime + timeToCall); 17 | }, timeToCall); 18 | lastTime = currTime + timeToCall; 19 | return id; 20 | }; 21 | 22 | if (!window.cancelAnimationFrame) 23 | window.cancelAnimationFrame = function (id) { 24 | clearTimeout(id); 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /lib/ReactIntense.css: -------------------------------------------------------------------------------- 1 | .ri-wrapper, 2 | .ri-cursor-plus { 3 | cursor: 4 | url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4M0UzRkZGRUY4ODQxMUUzQjg5REQwNUQyQTFENTVGOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4M0UzRkZGRkY4ODQxMUUzQjg5REQwNUQyQTFENTVGOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjUyNjc4QUY1Rjg0QjExRTNCODlERDA1RDJBMUQ1NUY5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjUyNjc4QUY2Rjg0QjExRTNCODlERDA1RDJBMUQ1NUY5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x6eaoAAABeVJREFUeNrcWn1MW1UUv604GZgFzSzFyRifDR/SpWzIPy5QCIlGsiZGJ05JDEELiYBEp1FKgoLRSaDwB64mmEyjxJlFDCbGBRiZf4zwlcEoDd8DdBRcpDGAWFnx/OAWH6x09Gttd5LDve/x3ru/X8+555173xExN8n6+no4NRmkx0hlpEdID5IGk4pIl0hvkd4gHSHtJb0sEolmXRzXLeAPk2pI9evOi54/I8JZIlCRkzfLqfmA9HlSMc6tra0tLywsDE5MTAzo9fqpwcHB+f7+ftPU1NTf+H9kZOR+hUIRkpycHJqYmBgZHR0tl0gkyQEBAcH8sRbSi6TVZKUBRy0icpCAhJqzpHm412Kx/Ds9PX2lo6OjvaSkpGd5edniyPOCg4PF9fX1x5VKZWZERMQJsVj8IIYh/Yr0DBFacDsRuuEFar4gDSEC/xgMhp/Ky8svtLS03HLHHFOpVAerqqpejI+Pf44IPUSnTKSvE5nv3UKELtxHjZa0EMdGo7G7rKxM29zcPM88ILm5uaG1tbUlUqn0KX7qHGkJETI7TYQuepj7bTbcqLOz81xmZmYLuwfS3t6uSk9PV3N3u4T5SGSWdiMitkMiiD8g22w2L1ZXV5feKxIQjIUxMTYwAAvHZFNEdtzpZ1LlysqKsbi4+ExTU9PvzAuSn59/qKGh4WxQUJAUhiJ9Vuhmd7MI5oRydXX1j6Kiore9RQKCsYEBWGAo0npb14ltWOMUJjbNibWamprK8+fPzzEvCzAACzDRoZowvmTXteiCx6nRI8TSZGvIysr6kfmQtLW1naS5U8xDcyK52E2bUYtOfkPNy/Pz870U/t5lPigU/j8NDQ1FPvctETl9xxyhEwqEcTKfWaPRNDAfFWADRmDlmLdbhE4itJ6kPOliUlJSI/NhGRoaKqJ8DXkeXF+1ZRGeeebgpYe0w4sYH9jR2hRg5BM/B/mo0LVeQX92dvZXd+VOnhRgJKxXOP7XhERO488lEncPStZuh7r7uQKspzbmCF/ZzWA9ERISonI0Fd8LkY2BRKLMPbrWbUFrdwlgMpl+oPUM8sFoWESJf9CiaMDdJDwpwEqYr/PDDDFfY7PJycnrzM+EMA/y7lEQiUNvdHR0xt+IjI+PWzHLQCQave7u7t/8jUhXV5cVcxSIPMZP/ulvRIaHh//i3RBErXUHoordyOSsCMbec9SCSCSSAMoLf6GuWczuEwkghXkOyOXyoIGBgRUXf1FX3yMOiUwmsy59l2ARrLxYWlrao/5mhYSEhAO8uwgiE+ilpqY+4W9E6Me3Yp4EkVH04uLiDvsbkZiYGCvmERDp3QjEUVFP+hsRAeZrINLBQ1kyEjF/IQGswMwPL4v59wkDssi6uroUfyECrDzzHbPOEQg2HVg2ibsHRNj1ROgVYP1OuLACEUt4ePjT2BX3dWsAI2E9wTa/qXy5RYR+sRvUtGLDGFv7XsR4e0drU4CRsOJl3ko6tW07iOQjvIjj4+NzCgoKDvmqNYANGNnmB6EP79gO4unExgad0WjsCQsLe88XiczNzX0ilUqPs9026Li8Q2rChdie9DUSwMRJmDjWLRHviDA3Gf8ylZGRUajRaOJ8hURFRYUMmPhhIcfKbLqWwMU+p0aNrXy1Wv2Wt3fk8/LypDqdThsYGIhFoI5IqAVY7RLx1Q89yEKe2fOHHn4hIsNVPKCxsbG+srJS5g13wticxFVg2u2j6P3/MZRbZolbRocHKpXKNxH+YG5PuhLGwFichI5bYskuVgc2GIQFA2aDwdDqoYKBHCKwj4fYN4jAhbvgcowIvwklHJ+RvsoEJRzkBm2lpaW9zpRwaLXaY+SuWTtKOL7Ge8IjJRw7bj5Kzftse1HNEopqsPU6MjIy3d/fP9fX17c4Nja2UVQTGxu7PyUl5RGFQhEmk8kiaFGUROsJOU/FGfu/qOZjInDNASyuu4OgzGnYhTIng9fKnHYjxTYLz5BCICM4wjYLz6y/uLDwDPsEPWyz8GzGxXE32v8EGADbuW2sOaxEjQAAAABJRU5ErkJggg==') 5 | 25 25, 6 | no-drop; 7 | } 8 | 9 | .ri-container, 10 | .ri-cursor-x { 11 | cursor: 12 | url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3Q0IyNDI3M0FFMkYxMUUzOEQzQUQ5NTMxMDAwQjJGRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo3Q0IyNDI3NEFFMkYxMUUzOEQzQUQ5NTMxMDAwQjJGRCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjdDQjI0MjcxQUUyRjExRTM4RDNBRDk1MzEwMDBCMkZEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjdDQjI0MjcyQUUyRjExRTM4RDNBRDk1MzEwMDBCMkZEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+soZ1WgAABp5JREFUeNrcWn9M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') 13 | 25 25, 14 | no-drop; 15 | } 16 | 17 | .ri-wrapper .ri-clicked { 18 | box-shadow: inset 0 0 0 500px rgba(0, 0, 0, 0.2); 19 | } 20 | 21 | .ri-container { 22 | background-color: rgba(0, 0, 0, 0.8); 23 | width: 100%; 24 | height: 100%; 25 | position: fixed; 26 | top: 0px; 27 | left: 0px; 28 | overflow: hidden; 29 | z-index: 999999; 30 | margin: 0px; 31 | transition: opacity 150ms cubic-bezier(0, 0, 0.26, 1); 32 | } 33 | 34 | .ri-caption-container { 35 | font-family: Georgia, Times, 'Times New Roman', serif; 36 | position: fixed; 37 | bottom: 0px; 38 | left: 0px; 39 | padding: 20px; 40 | color: #fff; 41 | word-spacing: 0.2px; 42 | text-shadow: -1px 0px 1px rgba(0, 0, 0, 0.4); 43 | } 44 | 45 | .ri-title { 46 | margin: 0px; 47 | padding: 0px; 48 | font-weight: normal; 49 | font-size: 40px; 50 | letter-spacing: 0.5px; 51 | line-height: 35px; 52 | text-align: left; 53 | } 54 | 55 | .ri-caption { 56 | margin: 0px; 57 | padding: 0px; 58 | font-weight: normal; 59 | font-size: 20px; 60 | letter-spacing: 0.1px; 61 | max-width: 500px; 62 | text-align: left; 63 | background: none; 64 | margin-top: 5px; 65 | } 66 | 67 | .ri-trigger { 68 | width: 200px; 69 | height: 200px; 70 | background-size: cover; 71 | background-position: 50% 50%; 72 | display: flex; 73 | justify-content: center; 74 | align-items: center; 75 | } 76 | 77 | .ri-loader { 78 | display: inline-block; 79 | width: 50px; 80 | height: 50px; 81 | border: 3px solid rgba(255, 255, 255, 0.2); 82 | border-radius: 50%; 83 | border-top-color: #fff; 84 | animation: spin 1s ease-in-out infinite; 85 | -webkit-animation: spin 1s ease-in-out infinite; 86 | } 87 | @keyframes spin { 88 | to { 89 | -webkit-transform: rotate(360deg); 90 | } 91 | } 92 | @-webkit-keyframes spin { 93 | to { 94 | -webkit-transform: rotate(360deg); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/ReactIntense.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * React Intense v0.2.2 3 | * https://github.com/brycedorn/react-intense 4 | * 5 | * A React component of https://github.com/tholman/intense-images 6 | * 7 | * Released under the MIT license 8 | * https://mit-license.org 9 | */ 10 | 11 | import React, { useRef, useState } from 'react'; 12 | 13 | type Props = { 14 | caption: string; 15 | className: string; 16 | renderLoader?: () => React.ReactNode; 17 | moveSpeed?: number; 18 | onClick?: (e: React.MouseEvent) => void; 19 | src: string; 20 | thumbnailSrc: string; 21 | title: string; 22 | invert?: boolean; 23 | vertical?: boolean; 24 | }; 25 | 26 | const KEYCODE_ESC = 27; 27 | const MIN_DIST_THRESHOLD = 0.0001; 28 | const DEFAULT_MOVE_SPEED = 0.02; 29 | 30 | const initialOverflowValue = document.body.style.overflow || 'unset'; 31 | 32 | export function useIntenseMaximize(props: Props) { 33 | const { src, onClick, moveSpeed = DEFAULT_MOVE_SPEED, vertical, invert, caption, title } = props; 34 | 35 | const [distance, setDistance] = useState(0); 36 | const [loaded, setLoaded] = useState(false); 37 | const mouseDest = useRef<{ x: number; y: number }>({ 38 | x: 0, 39 | y: 0, 40 | }); 41 | const mouseCurr = useRef<{ x: number; y: number }>({ 42 | x: window.innerWidth / 2, 43 | y: window.innerHeight / 2, 44 | }); 45 | const [transform, setTransform] = useState(''); 46 | const [triggered, setTriggered] = useState(false); 47 | const intervalKey = useRef(); 48 | 49 | function positionTarget() { 50 | if (!imgRef.current) { 51 | return; 52 | } 53 | 54 | const newX = mouseCurr.current.x + (mouseDest.current.x - mouseCurr.current.x) * moveSpeed; 55 | const newY = mouseCurr.current.y + (mouseDest.current.y - mouseCurr.current.y) * moveSpeed; 56 | const dist = Math.abs(newX - mouseCurr.current.x) + Math.abs(newY - mouseCurr.current.y); 57 | 58 | if (dist > MIN_DIST_THRESHOLD) { 59 | const newMouseCurr = { 60 | x: newX, 61 | y: newY, 62 | }; 63 | 64 | let newTransform = ''; 65 | let newDistance = -1; 66 | if (vertical) { 67 | if (newY !== distance) { 68 | const overflowPosition = calcPosition(newY, window.innerHeight); 69 | const yOverflow = window.innerHeight - imgRef.current?.offsetHeight; 70 | const position = yOverflow * overflowPosition; 71 | newTransform = `translate3d(0px, ${position}px, 0px)`; 72 | newDistance = newY; 73 | } 74 | } else { 75 | if (newX !== distance) { 76 | const overflowPosition = calcPosition(newX, window.innerWidth); 77 | const xOverflow = window.innerWidth - imgRef.current?.offsetWidth; 78 | const position = xOverflow * overflowPosition; 79 | newTransform = `translate3d(${position}px, 0px, 0px)`; 80 | newDistance = newX; 81 | } 82 | } 83 | 84 | setTransform(newTransform); 85 | setDistance(newDistance); 86 | 87 | mouseCurr.current = newMouseCurr; 88 | } 89 | } 90 | 91 | function calcPosition(current: number, total: number) { 92 | return invert ? (total - current) / total : current / total; 93 | } 94 | 95 | function addEventListeners() { 96 | imgRef.current?.addEventListener('mousemove', (e) => _onMouseMove(e.clientX, e.clientY)); 97 | imgRef.current?.addEventListener('touchmove', (e) => _onTouchMove(e.touches[0])); 98 | imgRef.current?.addEventListener('click', hideViewer); 99 | window.addEventListener('keyup', _onKeyUp); 100 | loop(); 101 | } 102 | 103 | function removeEventListeners() { 104 | imgRef.current?.removeEventListener('mousemove', (e) => _onMouseMove(e.clientX, e.clientY)); 105 | imgRef.current?.removeEventListener('touchmove', (e) => _onTouchMove(e.touches[0])); 106 | imgRef.current?.removeEventListener('click', hideViewer); 107 | window.removeEventListener('keyup', _onKeyUp); 108 | } 109 | 110 | // Lock scroll on the document body. 111 | function lockBody() { 112 | document.body.style.overflow = 'hidden'; 113 | } 114 | 115 | // Unlock scroll on the document body. 116 | function unlockBody() { 117 | document.body.style.overflow = initialOverflowValue; 118 | } 119 | 120 | // Stop animation 121 | function stop() { 122 | if (intervalKey.current) { 123 | window.cancelAnimationFrame(intervalKey.current); 124 | } 125 | intervalKey.current = undefined; 126 | removeEventListeners(); 127 | unlockBody(); 128 | } 129 | 130 | // Start animation 131 | function loop() { 132 | const newIntervalKey = window.requestAnimationFrame(loop); 133 | intervalKey.current = newIntervalKey; 134 | positionTarget(); 135 | } 136 | 137 | // Events 138 | function _onClick(e: React.MouseEvent) { 139 | e.preventDefault(); 140 | 141 | if (onClick) { 142 | onClick(e); 143 | } 144 | 145 | mouseDest.current = { 146 | x: e.clientX, 147 | y: e.clientY, 148 | }; 149 | 150 | setTriggered(true); 151 | lockBody(); 152 | } 153 | 154 | function _onKeyUp(e: KeyboardEvent) { 155 | e.preventDefault(); 156 | 157 | if (e.keyCode === KEYCODE_ESC) { 158 | hideViewer(); 159 | } 160 | } 161 | 162 | function _onLoad(e: React.SyntheticEvent) { 163 | setLoaded(true); 164 | addEventListeners(); 165 | } 166 | 167 | function _onMouseMove(x: number, y: number) { 168 | mouseDest.current = { 169 | x, 170 | y, 171 | }; 172 | } 173 | 174 | function _onTouchMove(touch: Touch) { 175 | mouseDest.current = { 176 | x: touch.clientX, 177 | y: touch.clientY, 178 | }; 179 | } 180 | 181 | // View helpers 182 | function hideViewer() { 183 | setTriggered(false); 184 | stop(); 185 | } 186 | 187 | function renderViewer() { 188 | const transformStyle = { 189 | height: vertical ? '' : window.innerHeight, 190 | MozTransform: transform, 191 | transform: transform, 192 | WebkitTransform: transform, 193 | width: vertical ? window.innerWidth : '', 194 | }; 195 | 196 | return triggered ? ( 197 |
198 | 199 |
200 |

{title}

201 |

{caption}

202 |
203 |
204 | ) : null; 205 | } 206 | 207 | const imgRef = useRef(null); 208 | 209 | return { renderViewer, maximize: _onClick, triggered, transform, loaded, onLoad: _onLoad, imgRef }; 210 | } 211 | 212 | function ReactIntense(props: Props) { 213 | const { 214 | className, 215 | renderLoader = () =>
, 216 | src, 217 | thumbnailSrc, 218 | vertical, 219 | caption, 220 | title, 221 | } = props; 222 | const { triggered, transform, loaded, onLoad, maximize, imgRef } = useIntenseMaximize(props); 223 | 224 | function renderViewer() { 225 | const transformStyle = { 226 | height: vertical ? '' : window.innerHeight, 227 | MozTransform: transform, 228 | transform: transform, 229 | WebkitTransform: transform, 230 | width: vertical ? window.innerWidth : '', 231 | }; 232 | 233 | return triggered ? ( 234 |
235 | 236 |
237 |

{title}

238 |

{caption}

239 |
240 |
241 | ) : null; 242 | } 243 | 244 | return ( 245 |
246 |
251 | {triggered ? renderLoader() : null} 252 |
253 | {renderViewer()} 254 |
255 | ); 256 | } 257 | 258 | export default ReactIntense; 259 | -------------------------------------------------------------------------------- /lib/polyfills.js: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/paulirish/1579671 2 | (function () { 3 | var lastTime = 0; 4 | var vendors = ['ms', 'moz', 'webkit', 'o']; 5 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 6 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 7 | window.cancelAnimationFrame = 8 | window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; 9 | } 10 | 11 | if (!window.requestAnimationFrame) 12 | window.requestAnimationFrame = function (callback, element) { 13 | var currTime = new Date().getTime(); 14 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 15 | var id = window.setTimeout(function () { 16 | callback(currTime + timeToCall); 17 | }, timeToCall); 18 | lastTime = currTime + timeToCall; 19 | return id; 20 | }; 21 | 22 | if (!window.cancelAnimationFrame) 23 | window.cancelAnimationFrame = function (id) { 24 | clearTimeout(id); 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-intense", 3 | "version": "0.2.2", 4 | "description": "A React port of the javascript full-size image viewer", 5 | "scripts": { 6 | "build": "parcel build src/index.html --dist-dir public", 7 | "build-lib": "rm -rf dist && babel --extensions .tsx lib --out-dir dist --copy-files", 8 | "start": "parcel src/index.html --dist-dir public", 9 | "lint": "npx prettier src lib --write" 10 | }, 11 | "main": "dist/ReactIntense.js", 12 | "keywords": [ 13 | "react", 14 | "component", 15 | "intense", 16 | "react-intense", 17 | "intense-images", 18 | "image", 19 | "images", 20 | "full-size", 21 | "full-screen" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/brycedorn/react-intense" 26 | }, 27 | "author": { 28 | "name": "Bryce Dorn", 29 | "email": "brycedorn@gmail.com", 30 | "url": "https://www.bryce.io" 31 | }, 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/brycedorn/react-intense/issues" 35 | }, 36 | "homepage": "https://bryce.io/react-intense", 37 | "devDependencies": { 38 | "@babel/cli": "^7.23.0", 39 | "@babel/core": "^7.23.2", 40 | "@babel/plugin-transform-modules-commonjs": "^7.23.0", 41 | "@babel/preset-env": "^7.23.2", 42 | "@babel/preset-react": "^7.22.15", 43 | "@babel/preset-typescript": "^7.23.2", 44 | "@parcel/css": "^1.9.0", 45 | "@types/react": "^18.2.37", 46 | "@types/react-dom": "^18.2.15", 47 | "@parcel/transformer-css": "^2.10.2", 48 | "babel-preset-minify": "^0.5.2", 49 | "parcel": "^2.6.0", 50 | "prettier": "3.0.3", 51 | "process": "^0.11.10" 52 | }, 53 | "optionalDependencies": { 54 | "@parcel/watcher-linux-x64-glibc": "2.2.0" 55 | }, 56 | "targets": { 57 | "main": false 58 | }, 59 | "dependencies": { 60 | "react": "^18.2.0", 61 | "react-dom": "^18.2.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/favicon.png -------------------------------------------------------------------------------- /src/img/horse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/horse.jpg -------------------------------------------------------------------------------- /src/img/horse_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/horse_thumb.jpg -------------------------------------------------------------------------------- /src/img/rain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/rain.jpg -------------------------------------------------------------------------------- /src/img/rain_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/rain_thumb.jpg -------------------------------------------------------------------------------- /src/img/temple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/temple.jpg -------------------------------------------------------------------------------- /src/img/temple_thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brycedorn/react-intense/a604104619b8d3eab7eb27829cfdce753052e1df/src/img/temple_thumb.jpg -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.jpg'; 2 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bryce.io - Intense Images 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 47 | 48 |
49 | 50 |
51 |

React Intense Image Viewer

52 |

A React component for viewing images in a fully full screen.

53 |

Click the images to see it in action!

54 |
55 |
56 |
57 | 58 |
59 |

60 | Originally by 61 | Tim Holman 62 | 63 | React Port by 64 | bryce 65 | 66 | Source 67 |

68 |
69 | 70 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import ReactIntense, { useIntenseMaximize } from '../dist/ReactIntense'; 4 | import '../dist/polyfills'; 5 | // for local dev, use the following instead: 6 | // import ReactIntense, { useIntenseMaximize } from '../lib/ReactIntense'; 7 | // import '../lib/polyfills'; 8 | 9 | import horse from './img/horse.jpg'; 10 | import horse_thumb from './img/horse_thumb.jpg'; 11 | import rain from './img/rain.jpg'; 12 | import rain_thumb from './img/rain_thumb.jpg'; 13 | import temple from './img/temple.jpg'; 14 | import temple_thumb from './img/temple_thumb.jpg'; 15 | 16 | const images = [ 17 | { 18 | caption: 'An annual month-long festival in Kyoto', 19 | className: 'demo-image first', 20 | src: horse, 21 | thumbnailSrc: horse_thumb, 22 | title: 'Gion Matsuri', 23 | }, 24 | { 25 | caption: 'Umbrellas are key!', 26 | className: 'demo-image second', 27 | src: rain, 28 | thumbnailSrc: rain_thumb, 29 | title: 'Rainy rain', 30 | vertical: true, 31 | }, 32 | { 33 | caption: 'Ancient Buddhist temple on a cliff', 34 | className: 'demo-image third', 35 | src: temple, 36 | thumbnailSrc: temple_thumb, 37 | title: 'Kiyomizu-dera', 38 | }, 39 | ]; 40 | 41 | function HookDemo() { 42 | const { maximize, renderViewer, triggered } = useIntenseMaximize(images[0]); 43 | 44 | return ( 45 | <> 46 | 49 | {renderViewer()} 50 | 51 | ); 52 | } 53 | 54 | function IntenseDemos() { 55 | return ( 56 | <> 57 |
58 | {images.map(({ caption, className, src, thumbnailSrc, title, vertical }) => ( 59 | 68 | ))} 69 |
70 | 71 | 72 | ); 73 | } 74 | 75 | const element = document.getElementById('demos'); 76 | if (element) { 77 | const root = createRoot(element); 78 | root.render(); 79 | } 80 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import '../dist/ReactIntense.css'; 2 | 3 | * { 4 | -webkit-font-smoothing: antialiased; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | } 8 | html, 9 | body { 10 | font-family: 'Ubuntu Mono', monospace; 11 | margin: 0px; 12 | height: 100%; 13 | width: 100%; 14 | } 15 | body { 16 | padding: 20px; 17 | text-align: center; 18 | min-width: 700px; 19 | } 20 | .logo { 21 | width: 55px; 22 | height: 55px; 23 | background: #222; 24 | color: #fff; 25 | text-align: center; 26 | line-height: 56px; 27 | border-radius: 100%; 28 | font-size: 27px; 29 | letter-spacing: -7px; 30 | text-indent: -6px; 31 | margin: auto; 32 | margin-bottom: 20px; 33 | margin-top: 20px; 34 | } 35 | header h1, 36 | footer h1 { 37 | font-weight: normal; 38 | width: 604px; 39 | background: #222; 40 | color: #fff; 41 | font-size: 18px; 42 | height: 55px; 43 | line-height: 55px; 44 | margin: auto; 45 | margin-top: 0px; 46 | margin-bottom: 20px; 47 | } 48 | #demos .gallery { 49 | display: flex; 50 | align-items: center; 51 | justify-content: center; 52 | flex-wrap: wrap; 53 | gap: 16px; 54 | } 55 | .demo-image { 56 | width: 290px; 57 | height: 290px; 58 | background-size: cover; 59 | background-position: 50% 50%; 60 | } 61 | button { 62 | margin: 16px; 63 | width: 100px; 64 | height: 32px; 65 | } 66 | footer h1 { 67 | margin-top: 16px; 68 | padding-left: 20px; 69 | background: #e9e9e9; 70 | color: #222; 71 | font-size: 16px; 72 | } 73 | footer h1 a { 74 | color: #222; 75 | } 76 | iframe { 77 | margin-bottom: -5px; 78 | margin-left: 2px; 79 | } 80 | .github-link { 81 | position: fixed; 82 | z-index: 2; 83 | top: 0; 84 | right: 0; 85 | border: 0; 86 | } 87 | .bullet { 88 | font-size: 12px; 89 | } 90 | @media (max-width: 650px) { 91 | body { 92 | padding: 10px; 93 | min-width: 350px; 94 | } 95 | header h1, 96 | footer h1 { 97 | width: 100%; 98 | font-size: 13px; 99 | height: 20px; 100 | line-height: 20px; 101 | } 102 | footer h1 { 103 | padding: 0 4px; 104 | } 105 | .demo-image { 106 | width: 200px; 107 | height: 200px; 108 | } 109 | } 110 | .github-corner:hover .octo-arm { 111 | animation: octocat-wave 560ms ease-in-out; 112 | } 113 | @keyframes octocat-wave { 114 | 0%, 115 | 100% { 116 | transform: rotate(0); 117 | } 118 | 20%, 119 | 60% { 120 | transform: rotate(-25deg); 121 | } 122 | 40%, 123 | 80% { 124 | transform: rotate(10deg); 125 | } 126 | } 127 | @media (max-width: 500px) { 128 | .github-corner:hover .octo-arm { 129 | animation: none; 130 | } 131 | .github-corner .octo-arm { 132 | animation: octocat-wave 560ms ease-in-out; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es6", "dom", "es2016", "es2017"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": false, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["lib/**/*.tsx", "lib/**/*.ts", "src/**/*.tsx", "src/**/*.ts"] 20 | } 21 | --------------------------------------------------------------------------------