The most easy to use responsive image viewer especially for mobile devices
See https://appleple.github.io/SmartPhoto/ for complete docs and demos
If you are Japasese, See here https://www.appleple.com/blog/javascript/smartphoto-js.html instead.
- Intuitive gestures such as pinch-in/pinch-out/drag/swipe
- Use Accelerometer to move images
- Accessible from keyboards and screen-readers
- Show pictures via URL hash
- Can make photo groups
via npm
npm install smartphoto --saveor yarn
yarn add smartphotorequire
constSmartPhoto=require('smartphoto');smartphoto.js
document.addEventListener('DOMContentLoaded',function(){newSmartPhoto(".js-smartphoto");});jquery-smartphoto.js
$(function(){$(".js-smartphoto").SmartPhoto();});<ahref="./assets/large-bear.jpg" class="js-smartphoto" data-caption="bear" data-id="bear" data-group="0"><imgsrc="./assets/bear.jpg" width="360"/></a><ahref="./assets/large-camel.jpg" class="js-smartphoto" data-caption="camel" data-id="camel" data-group="0"><imgsrc="./assets/camel.jpg" width="360"/></a><ahref="./assets/large-rhinoceros.jpg" class="js-smartphoto" data-caption="rhinoceros" data-id="sai" data-group="0"><imgsrc="./assets/rhinoceros.jpg" width="360"/></a><linkrel="stylesheet" href="./css/smartphoto.min.css"><scriptsrc="./js/smartphoto.js"></script><script>document.addEventListener('DOMContentLoaded',function(){newSmartPhoto(".js-smartphoto");});</script>When SmartPhoto is constructed with a CSS selector string (as above), clicks are handled via a single delegated listener, so elements added to the page after construction (e.g. by Ajax/infinite scroll) are picked up automatically just by clicking them — no need to call addItem()/addNewItem() manually. Right before a photo is opened, SmartPhoto also reconciles that photo's group against the current DOM: newly appended matching elements are added, and elements that have since been removed from the DOM are dropped from the group (remaining indices are recalculated). This auto-detection only applies to the selector-string form; when a NodeList/Element[] is passed (or in data source mode below), add/remove items explicitly via addItem()/addNewItem().
A few things to keep in mind:
- Reconciliation only happens right before a photo is opened (via a click,
show(), or hash restoration) — not continuously. If an element disappears from the DOM while the viewer is already open and younext()/prev()through the same session, that removal isn't reflected until the viewer is opened again. - It does not support replacing an entire container's
innerHTML(which recreates existing elements too, as brand-new DOM nodes) — that produces duplicate items, since the old and new elements aren't recognized as the same one. Only appending/removing individual elements is supported; if you regenerate the whole container, calldestroy()and construct a new instance instead. - Changing
data-groupon an element that has already been opened/registered has no effect (the group is fixed at first registration). Changing it before the element is first interacted with is picked up correctly. - If multiple instances are built with overlapping selectors, avoid relying on distinguishing exactly which instance handles a click for elements that could match either.
Instead of scanning <a> elements in the page, you can pass an array of slide objects directly (inspired by yet-another-react-lightbox). This is useful when your images come from an API or a JS-rendered list.
constphoto=newSmartPhoto([{src: "/img/bear-large.jpg",thumb: "/img/bear.jpg",caption: "bear",id: "bear"},{src: "/img/camel-large.jpg",thumb: "/img/camel.jpg",caption: "camel",id: "camel",width: 1200,height: 800},]);photo.show(0);// open by indexphoto.show("camel");// or by idphoto.next();photo.prev();photo.hide();photo.on("change",()=>{/* ... */});// same event contract as HTML modeSlide fields:
| field | required | description |
|---|---|---|
| src | yes | full-size image URL (equivalent to href in HTML mode) |
| thumb | no | thumbnail URL used in the nav strip. Falls back to src |
| caption | no | caption text (equivalent to data-caption) |
| alt | no | image alt text. Falls back to caption, then src |
| id | no | identifier used by show(id) and the URL hash. Falls back to the index |
| group | no | group name (equivalent to data-group). Falls back to "nogroup" |
| width / height | no | natural image size in px. When given, SmartPhoto skips the preload used to measure the image |
show(indexOrId, options) also accepts options.group (which group to open) and options.trigger (the element to animate from / return focus to). Both HTML mode and data source mode share the exact same public API, options, and events.
| variable | description | default |
|---|---|---|
| arrows | prev/next arrows | true |
| nav | navigation images at the bottom | true |
| showAnimation | animate the open/close transition | true |
| verticalGravity | apply device-tilt gravity to the vertical axis too (in addition to horizontal) | false |
| useOrientationApi | use the accelerometer (deviceorientation) to move a zoomed image | false |
| useHistoryApi | update the URL hash (#group=…&photo=…) via the History API | true |
| swipeTopToClose | close the viewer on an upward swipe | false |
| swipeBottomToClose | close the viewer on a downward swipe | true |
| swipeOffset | minimum swipe distance (px) to trigger navigation/close | 100 |
| swipeVelocity | minimum swipe speed (px/ms) that triggers navigation even below swipeOffset (fast flicks) | 0.5 |
| headerHeight | height (px) reserved for the header when fitting images | 60 |
| footerHeight | height (px) reserved for the footer when fitting images | 60 |
| resizeStyle | resize images to fill/fit on the screen | 'fit' |
| animationSpeed | animation speed (ms) when switching/opening/closing images | 450 |
| forceInterval | frequency (ms) to apply force to images | 10 |
| registance | friction applied to the inertia scroll of a zoomed image | 0.5 |
| loadOffset | number of neighboring slides to preload around the current one | 2 |
| lazyAttribute | attribute read for a lazy-loaded thumbnail (HTML mode only) | 'data-src' |
| classNames | override any of the generated CSS class names | see source |
| message | override screen-reader text (gotoNextImage / gotoPrevImage / closeDialog / carouselLabel) | see source |
document.addEventListener('DOMContentLoaded',function(){newSmartPhoto(".js-smartphoto",{arrows: false,nav: false});});You can choose if you want to scale images to fit/fill
document.addEventListener('DOMContentLoaded',function(){newSmartPhoto(".js-smartphoto",{resizeStyle: 'fit'});});// when the modal openedphoto.on('open',function(){console.log('open');});// when the modal closedphoto.on('close',function(){console.log('close');});// when all images are loadedphoto.on('loadall',function(){console.log('loadall');});// when photo is changedphoto.on('change',function(){console.log('change');});// when swipe startedphoto.on('swipestart',function(){console.log('swipestart');});// when swipe endedphoto.on('swipeend',function(){console.log('swipeend');});// when zoomed inphoto.on('zoomin',function(){console.log('zoomin');});// when zoomed outphoto.on('zoomout',function(){console.log('zoomout');});| method | description |
|---|---|
on(event, listener) | subscribe to one of the events listed above |
destroy() | remove the viewer and all of its event listeners |
[Symbol.dispose]() | same as destroy(). Lets a using declaration destroy the instance automatically when it goes out of scope: { using photo = new SmartPhoto(...); } |
gotoSlide(index) | go to the slide at index within the current group |
hidePhoto(dir?) | close the viewer. dir is 'bottom' (default) or 'top' and controls the close animation direction |
zoomPhoto() / zoomOutPhoto() | zoom the current image in/out programmatically |
addNewItem(element) | register a new <a> thumbnail element (HTML mode). Only needed when constructed with a NodeList/Element[] or otherwise not using a selector string — with a selector string, elements added later are auto-detected on click (see above) |
show(indexOrId?, options?) | open the viewer, by index or id. Works in both HTML mode and data source mode. options.group picks the group; options.trigger sets the element to animate from and to return focus to |
hide() | alias of hidePhoto() |
next() / prev() | go to the next/previous slide. No-op at the start/end of the group |
addItem(slideOrElement) | add a new item. Accepts a slide object (data source mode, where explicit registration is always required) or an Element (HTML mode, same caveat as addNewItem) |
currentIndex | (getter) the index currently displayed within its group |
| property | description | default |
|---|---|---|
| --smartphoto-animation-speed | animation speed when switching/opening/closing images. Overridden per-instance by the animationSpeed JS option | 450ms |
| --smartphoto-animation-function | easing function used for animations | ease-out |
| --smartphoto-backdrop-color | backdrop color when viewing images | rgba(0, 0, 0, 1) |
| --smartphoto-header-color | header color | rgba(0, 0, 0, .2) |
Set these on .smartphoto (or :root) to override the defaults, no rebuild required:
.smartphoto {
--smartphoto-animation-speed:450ms;
--smartphoto-animation-function: ease-in-out;
--smartphoto-backdrop-color:rgba(0,0,0,0.9);
--smartphoto-header-color:rgba(0,0,0,0.4);
}https://github.com/appleple/SmartPhoto
Code and documentation copyright 2017 by appleple, Inc. Code released under the MIT License.