使用简介

https://github.com/FNNDSC/viewerjs(需要点击遮罩层关闭弹框的复制下面内容)

https://www.jianshu.com/p/d98db3815823

viewer.js

  1. /*!
  2. * Viewer v1.0.0
  3. * https://github.com/fengyuanchen/viewer
  4. *
  5. * Copyright (c) 2015-2018 Chen Fengyuan
  6. * Released under the MIT license
  7. *
  8. * Date: 2018-04-01T06:11:06.751Z
  9. */
  10.  
  11. (function (global, factory) {
  12. typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
  13. typeof define === 'function' && define.amd ? define(['jquery'], factory) :
  14. (factory(global.jQuery));
  15. }(this, (function ($) {
  16. 'use strict';
  17.  
  18. $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
  19.  
  20. var DEFAULTS = {
  21. // Enable inline mode
  22. inline: false,
  23.  
  24. // Show the button on the top-right of the viewer
  25. button: true,
  26.  
  27. // Show the navbar
  28. //缩略图导航
  29. navbar: true,
  30.  
  31. // Show the title
  32. title: true,
  33.  
  34. // Show the toolbar
  35. //工具栏显示
  36. toolbar: true,
  37.  
  38. // Show the tooltip with image ratio (percentage) when zoom in or zoom out
  39. tooltip: true,
  40.  
  41. // Enable to move the image
  42. movable: true,
  43.  
  44. // Enable to zoom the image
  45. zoomable: true,
  46.  
  47. // Enable to rotate the image
  48. rotatable: true,
  49.  
  50. // Enable to scale the image
  51. scalable: true,
  52.  
  53. // Enable CSS3 Transition for some special elements
  54. transition: true,
  55.  
  56. // Enable to request fullscreen when play
  57. fullscreen: true,
  58.  
  59. // The amount of time to delay between automatically cycling an image when playing.
  60. interval: 5000,
  61.  
  62. // Enable keyboard support
  63. keyboard: true,
  64.  
  65. // Enable a modal backdrop, specify `static` for a backdrop which doesn't close the modal on click
  66. backdrop: true,
  67.  
  68. // Indicate if show a loading spinner when load image or not.
  69. loading: true,
  70.  
  71. // Indicate if enable loop viewing or not.
  72. loop: true,
  73.  
  74. // Min width of the viewer in inline mode
  75. minWidth: 200,
  76.  
  77. // Min height of the viewer in inline mode
  78. minHeight: 100,
  79.  
  80. // Define the ratio when zoom the image by wheeling mouse
  81. zoomRatio: 0.1,
  82.  
  83. // Define the min ratio of the image when zoom out
  84. minZoomRatio: 0.01,
  85.  
  86. // Define the max ratio of the image when zoom in
  87. maxZoomRatio: 100,
  88.  
  89. // Define the CSS `z-index` value of viewer in modal mode.
  90. zIndex: 2015,
  91.  
  92. // Define the CSS `z-index` value of viewer in inline mode.
  93. zIndexInline: 0,
  94.  
  95. // Define where to get the original image URL for viewing
  96. // Type: String (an image attribute) or Function (should return an image URL)
  97. url: 'src',
  98.  
  99. // Define where to put the viewer in modal mode.
  100. // Type: String | Element
  101. container: 'body',
  102.  
  103. // Filter the images for viewing.
  104. // Type: Function (return true if the image is viewable)
  105. filter: null,
  106.  
  107. // Event shortcuts
  108. ready: null,
  109. show: null,
  110. shown: null,
  111. hide: null,
  112. hidden: null,
  113. view: null,
  114. viewed: null
  115. };
  116.  
  117. var TEMPLATE = '<div class="viewer-container" touch-action="none">' + '<div class="viewer-canvas"></div>' + '<div class="viewer-footer">' + '<div class="viewer-title"></div>' + '<div class="viewer-toolbar"></div>' + '<div class="viewer-navbar">' + '<ul class="viewer-list"></ul>' + '</div>' + '</div>' + '<div class="viewer-tooltip"></div>' + '<div role="button" class="viewer-button" data-action="mix"></div>' + '<div class="viewer-player"></div>' + '</div>';
  118.  
  119. var IN_BROWSER = typeof window !== 'undefined';
  120. var WINDOW = IN_BROWSER ? window : {};
  121. var NAMESPACE = 'viewer';
  122.  
  123. // Actions
  124. var ACTION_MOVE = 'move';
  125. var ACTION_SWITCH = 'switch';
  126. var ACTION_ZOOM = 'zoom';
  127.  
  128. // Classes
  129. var CLASS_ACTIVE = NAMESPACE + '-active';
  130. var CLASS_CLOSE = NAMESPACE + '-close';
  131. var CLASS_FADE = NAMESPACE + '-fade';
  132. var CLASS_FIXED = NAMESPACE + '-fixed';
  133. var CLASS_FULLSCREEN = NAMESPACE + '-fullscreen';
  134. var CLASS_FULLSCREEN_EXIT = NAMESPACE + '-fullscreen-exit';
  135. var CLASS_HIDE = NAMESPACE + '-hide';
  136. var CLASS_HIDE_MD_DOWN = NAMESPACE + '-hide-md-down';
  137. var CLASS_HIDE_SM_DOWN = NAMESPACE + '-hide-sm-down';
  138. var CLASS_HIDE_XS_DOWN = NAMESPACE + '-hide-xs-down';
  139. var CLASS_IN = NAMESPACE + '-in';
  140. var CLASS_INVISIBLE = NAMESPACE + '-invisible';
  141. var CLASS_LOADING = NAMESPACE + '-loading';
  142. var CLASS_MOVE = NAMESPACE + '-move';
  143. var CLASS_OPEN = NAMESPACE + '-open';
  144. var CLASS_SHOW = NAMESPACE + '-show';
  145. var CLASS_TRANSITION = NAMESPACE + '-transition';
  146.  
  147. // Events
  148. var EVENT_READY = 'ready';
  149. var EVENT_SHOW = 'show';
  150. var EVENT_SHOWN = 'shown';
  151. var EVENT_HIDE = 'hide';
  152. var EVENT_HIDDEN = 'hidden';
  153. var EVENT_VIEW = 'view';
  154. var EVENT_VIEWED = 'viewed';
  155. var EVENT_CLICK = 'click';
  156. var EVENT_DRAG_START = 'dragstart';
  157. var EVENT_KEY_DOWN = 'keydown';
  158. var EVENT_LOAD = 'load';
  159. var EVENT_POINTER_DOWN = WINDOW.PointerEvent ? 'pointerdown' : 'touchstart mousedown';
  160. var EVENT_POINTER_MOVE = WINDOW.PointerEvent ? 'pointermove' : 'touchmove mousemove';
  161. var EVENT_POINTER_UP = WINDOW.PointerEvent ? 'pointerup pointercancel' : 'touchend touchcancel mouseup';
  162. var EVENT_RESIZE = 'resize';
  163. var EVENT_TRANSITION_END = 'transitionend';
  164. var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
  165.  
  166. var BUTTONS = ['zoom-in', 'zoom-out', 'one-to-one', 'reset', 'prev', 'play', 'next', 'rotate-left', 'rotate-right', 'flip-horizontal', 'flip-vertical'];
  167.  
  168. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  169. return typeof obj;
  170. } : function (obj) {
  171. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  172. };
  173.  
  174. var classCallCheck = function (instance, Constructor) {
  175. if (!(instance instanceof Constructor)) {
  176. throw new TypeError("Cannot call a class as a function");
  177. }
  178. };
  179.  
  180. var createClass = function () {
  181. function defineProperties(target, props) {
  182. for (var i = 0; i < props.length; i++) {
  183. var descriptor = props[i];
  184. descriptor.enumerable = descriptor.enumerable || false;
  185. descriptor.configurable = true;
  186. if ("value" in descriptor) descriptor.writable = true;
  187. Object.defineProperty(target, descriptor.key, descriptor);
  188. }
  189. }
  190.  
  191. return function (Constructor, protoProps, staticProps) {
  192. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  193. if (staticProps) defineProperties(Constructor, staticProps);
  194. return Constructor;
  195. };
  196. }();
  197.  
  198. /**
  199. * Check if the given value is a string.
  200. * @param {*} value - The value to check.
  201. * @returns {boolean} Returns `true` if the given value is a string, else `false`.
  202. */
  203. function isString(value) {
  204. return typeof value === 'string';
  205. }
  206.  
  207. /**
  208. * Check if the given value is not a number.
  209. */
  210. var isNaN = Number.isNaN || WINDOW.isNaN;
  211.  
  212. /**
  213. * Check if the given value is a number.
  214. * @param {*} value - The value to check.
  215. * @returns {boolean} Returns `true` if the given value is a number, else `false`.
  216. */
  217. function isNumber(value) {
  218. return typeof value === 'number' && !isNaN(value);
  219. }
  220.  
  221. /**
  222. * Check if the given value is undefined.
  223. * @param {*} value - The value to check.
  224. * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
  225. */
  226. function isUndefined(value) {
  227. return typeof value === 'undefined';
  228. }
  229.  
  230. /**
  231. * Check if the given value is an object.
  232. * @param {*} value - The value to check.
  233. * @returns {boolean} Returns `true` if the given value is an object, else `false`.
  234. */
  235. function isObject(value) {
  236. return (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value !== null;
  237. }
  238.  
  239. var hasOwnProperty = Object.prototype.hasOwnProperty;
  240.  
  241. /**
  242. * Check if the given value is a plain object.
  243. * @param {*} value - The value to check.
  244. * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
  245. */
  246.  
  247. function isPlainObject(value) {
  248. if (!isObject(value)) {
  249. return false;
  250. }
  251.  
  252. try {
  253. var _constructor = value.constructor;
  254. var prototype = _constructor.prototype;
  255.  
  256. return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
  257. } catch (e) {
  258. return false;
  259. }
  260. }
  261.  
  262. /**
  263. * Check if the given value is a function.
  264. * @param {*} value - The value to check.
  265. * @returns {boolean} Returns `true` if the given value is a function, else `false`.
  266. */
  267. function isFunction(value) {
  268. return typeof value === 'function';
  269. }
  270.  
  271. /**
  272. * Iterate the given data.
  273. * @param {*} data - The data to iterate.
  274. * @param {Function} callback - The process function for each element.
  275. * @returns {*} The original data.
  276. */
  277. function forEach(data, callback) {
  278. if (data && isFunction(callback)) {
  279. if (Array.isArray(data) || isNumber(data.length) /* array-like */) {
  280. var length = data.length;
  281.  
  282. var i = void 0;
  283.  
  284. for (i = 0; i < length; i += 1) {
  285. if (callback.call(data, data[i], i, data) === false) {
  286. break;
  287. }
  288. }
  289. } else if (isObject(data)) {
  290. Object.keys(data).forEach(function (key) {
  291. callback.call(data, data[key], key, data);
  292. });
  293. }
  294. }
  295.  
  296. return data;
  297. }
  298.  
  299. /**
  300. * Extend the given object.
  301. * @param {*} obj - The object to be extended.
  302. * @param {*} args - The rest objects which will be merged to the first object.
  303. * @returns {Object} The extended object.
  304. */
  305. var assign = Object.assign || function assign(obj) {
  306. for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  307. args[_key - 1] = arguments[_key];
  308. }
  309.  
  310. if (isObject(obj) && args.length > 0) {
  311. args.forEach(function (arg) {
  312. if (isObject(arg)) {
  313. Object.keys(arg).forEach(function (key) {
  314. obj[key] = arg[key];
  315. });
  316. }
  317. });
  318. }
  319.  
  320. return obj;
  321. };
  322.  
  323. var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;
  324.  
  325. /**
  326. * Apply styles to the given element.
  327. * @param {Element} element - The target element.
  328. * @param {Object} styles - The styles for applying.
  329. */
  330. function setStyle(element, styles) {
  331. var style = element.style;
  332.  
  333. forEach(styles, function (value, property) {
  334. if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
  335. value += 'px';
  336. }
  337.  
  338. style[property] = value;
  339. });
  340. }
  341.  
  342. /**
  343. * Check if the given element has a special class.
  344. * @param {Element} element - The element to check.
  345. * @param {string} value - The class to search.
  346. * @returns {boolean} Returns `true` if the special class was found.
  347. */
  348. function hasClass(element, value) {
  349. return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
  350. }
  351.  
  352. /**
  353. * Add classes to the given element.
  354. * @param {Element} element - The target element.
  355. * @param {string} value - The classes to be added.
  356. */
  357. function addClass(element, value) {
  358. if (!value) {
  359. return;
  360. }
  361.  
  362. if (isNumber(element.length)) {
  363. forEach(element, function (elem) {
  364. addClass(elem, value);
  365. });
  366. return;
  367. }
  368.  
  369. if (element.classList) {
  370. element.classList.add(value);
  371. return;
  372. }
  373.  
  374. var className = element.className.trim();
  375.  
  376. if (!className) {
  377. element.className = value;
  378. } else if (className.indexOf(value) < 0) {
  379. element.className = className + ' ' + value;
  380. }
  381. }
  382.  
  383. /**
  384. * Remove classes from the given element.
  385. * @param {Element} element - The target element.
  386. * @param {string} value - The classes to be removed.
  387. */
  388. function removeClass(element, value) {
  389. if (!value) {
  390. return;
  391. }
  392.  
  393. if (isNumber(element.length)) {
  394. forEach(element, function (elem) {
  395. removeClass(elem, value);
  396. });
  397. return;
  398. }
  399.  
  400. if (element.classList) {
  401. element.classList.remove(value);
  402. return;
  403. }
  404.  
  405. if (element.className.indexOf(value) >= 0) {
  406. element.className = element.className.replace(value, '');
  407. }
  408. }
  409.  
  410. /**
  411. * Add or remove classes from the given element.
  412. * @param {Element} element - The target element.
  413. * @param {string} value - The classes to be toggled.
  414. * @param {boolean} added - Add only.
  415. */
  416. function toggleClass(element, value, added) {
  417. if (!value) {
  418. return;
  419. }
  420.  
  421. if (isNumber(element.length)) {
  422. forEach(element, function (elem) {
  423. toggleClass(elem, value, added);
  424. });
  425. return;
  426. }
  427.  
  428. // IE10-11 doesn't support the second parameter of `classList.toggle`
  429. if (added) {
  430. addClass(element, value);
  431. } else {
  432. removeClass(element, value);
  433. }
  434. }
  435.  
  436. var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;
  437.  
  438. /**
  439. * Transform the given string from camelCase to kebab-case
  440. * @param {string} value - The value to transform.
  441. * @returns {string} The transformed value.
  442. */
  443. function hyphenate(value) {
  444. return value.replace(REGEXP_HYPHENATE, '$1-$2').toLowerCase();
  445. }
  446.  
  447. /**
  448. * Get data from the given element.
  449. * @param {Element} element - The target element.
  450. * @param {string} name - The data key to get.
  451. * @returns {string} The data value.
  452. */
  453. function getData(element, name) {
  454. if (isObject(element[name])) {
  455. return element[name];
  456. } else if (element.dataset) {
  457. return element.dataset[name];
  458. }
  459.  
  460. return element.getAttribute('data-' + hyphenate(name));
  461. }
  462.  
  463. /**
  464. * Set data to the given element.
  465. * @param {Element} element - The target element.
  466. * @param {string} name - The data key to set.
  467. * @param {string} data - The data value.
  468. */
  469. function setData(element, name, data) {
  470. if (isObject(data)) {
  471. element[name] = data;
  472. } else if (element.dataset) {
  473. element.dataset[name] = data;
  474. } else {
  475. element.setAttribute('data-' + hyphenate(name), data);
  476. }
  477. }
  478.  
  479. /**
  480. * Remove data from the given element.
  481. * @param {Element} element - The target element.
  482. * @param {string} name - The data key to remove.
  483. */
  484. function removeData(element, name) {
  485. if (isObject(element[name])) {
  486. try {
  487. delete element[name];
  488. } catch (e) {
  489. element[name] = undefined;
  490. }
  491. } else if (element.dataset) {
  492. // #128 Safari not allows to delete dataset property
  493. try {
  494. delete element.dataset[name];
  495. } catch (e) {
  496. element.dataset[name] = undefined;
  497. }
  498. } else {
  499. element.removeAttribute('data-' + hyphenate(name));
  500. }
  501. }
  502.  
  503. var REGEXP_SPACES = /\s\s*/;
  504. var onceSupported = function () {
  505. var supported = false;
  506.  
  507. if (IN_BROWSER) {
  508. var once = false;
  509. var listener = function listener() { };
  510. var options = Object.defineProperty({}, 'once', {
  511. get: function get$$1() {
  512. supported = true;
  513. return once;
  514. },
  515.  
  516. /**
  517. * This setter can fix a `TypeError` in strict mode
  518. * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
  519. * @param {boolean} value - The value to set
  520. */
  521. set: function set$$1(value) {
  522. once = value;
  523. }
  524. });
  525.  
  526. WINDOW.addEventListener('test', listener, options);
  527. WINDOW.removeEventListener('test', listener, options);
  528. }
  529.  
  530. return supported;
  531. }();
  532.  
  533. /**
  534. * Remove event listener from the target element.
  535. * @param {Element} element - The event target.
  536. * @param {string} type - The event type(s).
  537. * @param {Function} listener - The event listener.
  538. * @param {Object} options - The event options.
  539. */
  540. function removeListener(element, type, listener) {
  541. var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  542.  
  543. var handler = listener;
  544.  
  545. type.trim().split(REGEXP_SPACES).forEach(function (event) {
  546. if (!onceSupported) {
  547. var listeners = element.listeners;
  548.  
  549. if (listeners && listeners[event] && listeners[event][listener]) {
  550. handler = listeners[event][listener];
  551. delete listeners[event][listener];
  552.  
  553. if (Object.keys(listeners[event]).length === 0) {
  554. delete listeners[event];
  555. }
  556.  
  557. if (Object.keys(listeners).length === 0) {
  558. delete element.listeners;
  559. }
  560. }
  561. }
  562.  
  563. element.removeEventListener(event, handler, options);
  564. });
  565. }
  566.  
  567. /**
  568. * Add event listener to the target element.
  569. * @param {Element} element - The event target.
  570. * @param {string} type - The event type(s).
  571. * @param {Function} listener - The event listener.
  572. * @param {Object} options - The event options.
  573. */
  574. function addListener(element, type, listener) {
  575. var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  576.  
  577. var _handler = listener;
  578.  
  579. type.trim().split(REGEXP_SPACES).forEach(function (event) {
  580. if (options.once && !onceSupported) {
  581. var _element$listeners = element.listeners,
  582. listeners = _element$listeners === undefined ? {} : _element$listeners;
  583.  
  584. _handler = function handler() {
  585. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  586. args[_key2] = arguments[_key2];
  587. }
  588.  
  589. delete listeners[event][listener];
  590. element.removeEventListener(event, _handler, options);
  591. listener.apply(element, args);
  592. };
  593.  
  594. if (!listeners[event]) {
  595. listeners[event] = {};
  596. }
  597.  
  598. if (listeners[event][listener]) {
  599. element.removeEventListener(event, listeners[event][listener], options);
  600. }
  601.  
  602. listeners[event][listener] = _handler;
  603. element.listeners = listeners;
  604. }
  605.  
  606. element.addEventListener(event, _handler, options);
  607. });
  608. }
  609.  
  610. /**
  611. * Dispatch event on the target element.
  612. * @param {Element} element - The event target.
  613. * @param {string} type - The event type(s).
  614. * @param {Object} data - The additional event data.
  615. * @returns {boolean} Indicate if the event is default prevented or not.
  616. */
  617. function dispatchEvent(element, type, data) {
  618. var event = void 0;
  619.  
  620. // Event and CustomEvent on IE9-11 are global objects, not constructors
  621. if (isFunction(Event) && isFunction(CustomEvent)) {
  622. event = new CustomEvent(type, {
  623. detail: data,
  624. bubbles: true,
  625. cancelable: true
  626. });
  627. } else {
  628. event = document.createEvent('CustomEvent');
  629. event.initCustomEvent(type, true, true, data);
  630. }
  631.  
  632. return element.dispatchEvent(event);
  633. }
  634.  
  635. /**
  636. * Get the offset base on the document.
  637. * @param {Element} element - The target element.
  638. * @returns {Object} The offset data.
  639. */
  640. function getOffset(element) {
  641. var box = element.getBoundingClientRect();
  642.  
  643. return {
  644. left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
  645. top: box.top + (window.pageYOffset - document.documentElement.clientTop)
  646. };
  647. }
  648.  
  649. /**
  650. * Get transforms base on the given object.
  651. * @param {Object} obj - The target object.
  652. * @returns {string} A string contains transform values.
  653. */
  654. function getTransforms(_ref) {
  655. var rotate = _ref.rotate,
  656. scaleX = _ref.scaleX,
  657. scaleY = _ref.scaleY,
  658. translateX = _ref.translateX,
  659. translateY = _ref.translateY;
  660.  
  661. var values = [];
  662.  
  663. if (isNumber(translateX) && translateX !== 0) {
  664. values.push('translateX(' + translateX + 'px)');
  665. }
  666.  
  667. if (isNumber(translateY) && translateY !== 0) {
  668. values.push('translateY(' + translateY + 'px)');
  669. }
  670.  
  671. // Rotate should come first before scale to match orientation transform
  672. if (isNumber(rotate) && rotate !== 0) {
  673. values.push('rotate(' + rotate + 'deg)');
  674. }
  675.  
  676. if (isNumber(scaleX) && scaleX !== 1) {
  677. values.push('scaleX(' + scaleX + ')');
  678. }
  679.  
  680. if (isNumber(scaleY) && scaleY !== 1) {
  681. values.push('scaleY(' + scaleY + ')');
  682. }
  683.  
  684. var transform = values.length ? values.join(' ') : 'none';
  685.  
  686. return {
  687. WebkitTransform: transform,
  688. msTransform: transform,
  689. transform: transform
  690. };
  691. }
  692.  
  693. /**
  694. * Get an image name from an image url.
  695. * @param {string} url - The target url.
  696. * @example
  697. * // picture.jpg
  698. * getImageNameFromURL('http://domain.com/path/to/picture.jpg?size=1280×960')
  699. * @returns {string} A string contains the image name.
  700. */
  701. function getImageNameFromURL(url) {
  702. return isString(url) ? url.replace(/^.*\//, '').replace(/[?&#].*$/, '') : '';
  703. }
  704.  
  705. var IS_SAFARI = WINDOW.navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(WINDOW.navigator.userAgent);
  706.  
  707. /**
  708. * Get an image's natural sizes.
  709. * @param {string} image - The target image.
  710. * @param {Function} callback - The callback function.
  711. * @returns {HTMLImageElement} The new image.
  712. */
  713. function getImageNaturalSizes(image, callback) {
  714. var newImage = document.createElement('img');
  715.  
  716. // Modern browsers (except Safari)
  717. if (image.naturalWidth && !IS_SAFARI) {
  718. callback(image.naturalWidth, image.naturalHeight);
  719. return newImage;
  720. }
  721.  
  722. var body = document.body || document.documentElement;
  723.  
  724. newImage.onload = function () {
  725. callback(newImage.width, newImage.height);
  726.  
  727. if (!IS_SAFARI) {
  728. body.removeChild(newImage);
  729. }
  730. };
  731.  
  732. newImage.src = image.src;
  733.  
  734. // iOS Safari will convert the image automatically
  735. // with its orientation once append it into DOM
  736. if (!IS_SAFARI) {
  737. newImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';
  738. body.appendChild(newImage);
  739. }
  740.  
  741. return newImage;
  742. }
  743.  
  744. /**
  745. * Get the related class name of a responsive type number.
  746. * @param {string} type - The responsive type.
  747. * @returns {string} The related class name.
  748. */
  749. function getResponsiveClass(type) {
  750. switch (type) {
  751. case 2:
  752. return CLASS_HIDE_XS_DOWN;
  753.  
  754. case 3:
  755. return CLASS_HIDE_SM_DOWN;
  756.  
  757. case 4:
  758. return CLASS_HIDE_MD_DOWN;
  759.  
  760. default:
  761. return '';
  762. }
  763. }
  764.  
  765. /**
  766. * Get the max ratio of a group of pointers.
  767. * @param {string} pointers - The target pointers.
  768. * @returns {number} The result ratio.
  769. */
  770. function getMaxZoomRatio(pointers) {
  771. var pointers2 = assign({}, pointers);
  772. var ratios = [];
  773.  
  774. forEach(pointers, function (pointer, pointerId) {
  775. delete pointers2[pointerId];
  776.  
  777. forEach(pointers2, function (pointer2) {
  778. var x1 = Math.abs(pointer.startX - pointer2.startX);
  779. var y1 = Math.abs(pointer.startY - pointer2.startY);
  780. var x2 = Math.abs(pointer.endX - pointer2.endX);
  781. var y2 = Math.abs(pointer.endY - pointer2.endY);
  782. var z1 = Math.sqrt(x1 * x1 + y1 * y1);
  783. var z2 = Math.sqrt(x2 * x2 + y2 * y2);
  784. var ratio = (z2 - z1) / z1;
  785.  
  786. ratios.push(ratio);
  787. });
  788. });
  789.  
  790. ratios.sort(function (a, b) {
  791. return Math.abs(a) < Math.abs(b);
  792. });
  793.  
  794. return ratios[0];
  795. }
  796.  
  797. /**
  798. * Get a pointer from an event object.
  799. * @param {Object} event - The target event object.
  800. * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
  801. * @returns {Object} The result pointer contains start and/or end point coordinates.
  802. */
  803. function getPointer(_ref2, endOnly) {
  804. var pageX = _ref2.pageX,
  805. pageY = _ref2.pageY;
  806.  
  807. var end = {
  808. endX: pageX,
  809. endY: pageY
  810. };
  811.  
  812. return endOnly ? end : assign({
  813. startX: pageX,
  814. startY: pageY
  815. }, end);
  816. }
  817.  
  818. /**
  819. * Get the center point coordinate of a group of pointers.
  820. * @param {Object} pointers - The target pointers.
  821. * @returns {Object} The center point coordinate.
  822. */
  823. function getPointersCenter(pointers) {
  824. var pageX = 0;
  825. var pageY = 0;
  826. var count = 0;
  827.  
  828. forEach(pointers, function (_ref3) {
  829. var startX = _ref3.startX,
  830. startY = _ref3.startY;
  831.  
  832. pageX += startX;
  833. pageY += startY;
  834. count += 1;
  835. });
  836.  
  837. pageX /= count;
  838. pageY /= count;
  839.  
  840. return {
  841. pageX: pageX,
  842. pageY: pageY
  843. };
  844. }
  845.  
  846. var render = {
  847. render: function render() {
  848. this.initContainer();
  849. this.initViewer();
  850. this.initList();
  851. this.renderViewer();
  852. },
  853. initContainer: function initContainer() {
  854. this.containerData = {
  855. width: window.innerWidth,
  856. height: window.innerHeight
  857. };
  858. },
  859. initViewer: function initViewer() {
  860. var options = this.options,
  861. parent = this.parent;
  862.  
  863. var viewerData = void 0;
  864.  
  865. if (options.inline) {
  866. viewerData = {
  867. width: Math.max(parent.offsetWidth, options.minWidth),
  868. height: Math.max(parent.offsetHeight, options.minHeight)
  869. };
  870.  
  871. this.parentData = viewerData;
  872. }
  873.  
  874. if (this.fulled || !viewerData) {
  875. viewerData = this.containerData;
  876. }
  877.  
  878. this.viewerData = assign({}, viewerData);
  879. },
  880. renderViewer: function renderViewer() {
  881. if (this.options.inline && !this.fulled) {
  882. setStyle(this.viewer, this.viewerData);
  883. }
  884. },
  885. initList: function initList() {
  886. var _this = this;
  887.  
  888. var element = this.element,
  889. options = this.options,
  890. list = this.list;
  891.  
  892. var items = [];
  893.  
  894. forEach(this.images, function (image, i) {
  895. var src = image.src;
  896.  
  897. var alt = image.alt || getImageNameFromURL(src);
  898. var url = options.url;
  899.  
  900. if (isString(url)) {
  901. url = image.getAttribute(url);
  902. } else if (isFunction(url)) {
  903. url = url.call(_this, image);
  904. }
  905.  
  906. if (src || url) {
  907. items.push('<li>' + '<img' + (' src="' + (src || url) + '"') + ' role="button"' + ' data-action="view"' + (' data-index="' + i + '"') + (' data-original-url="' + (url || src) + '"') + (' alt="' + alt + '"') + '>' + '</li>');
  908. }
  909. });
  910.  
  911. list.innerHTML = items.join('');
  912. this.items = list.getElementsByTagName('li');
  913. forEach(this.items, function (item) {
  914. var image = item.firstElementChild;
  915.  
  916. setData(image, 'filled', true);
  917.  
  918. if (options.loading) {
  919. addClass(item, CLASS_LOADING);
  920. }
  921.  
  922. addListener(image, EVENT_LOAD, function (event) {
  923. if (options.loading) {
  924. removeClass(item, CLASS_LOADING);
  925. }
  926.  
  927. _this.loadImage(event);
  928. }, {
  929. once: true
  930. });
  931. });
  932.  
  933. if (options.transition) {
  934. addListener(element, EVENT_VIEWED, function () {
  935. addClass(list, CLASS_TRANSITION);
  936. }, {
  937. once: true
  938. });
  939. }
  940. },
  941. renderList: function renderList(index) {
  942. var i = index || this.index;
  943. var width = this.items[i].offsetWidth || 30;
  944. var outerWidth = width + 1; // 1 pixel of `margin-left` width
  945.  
  946. // Place the active item in the center of the screen
  947. setStyle(this.list, assign({
  948. width: outerWidth * this.length
  949. }, getTransforms({
  950. translateX: (this.viewerData.width - width) / 2 - outerWidth * i
  951. })));
  952. },
  953. resetList: function resetList() {
  954. var list = this.list;
  955.  
  956. list.innerHTML = '';
  957. removeClass(list, CLASS_TRANSITION);
  958. setStyle(list, getTransforms({
  959. translateX: 0
  960. }));
  961. },
  962. initImage: function initImage(done) {
  963. var _this2 = this;
  964.  
  965. var options = this.options,
  966. image = this.image,
  967. viewerData = this.viewerData;
  968.  
  969. var footerHeight = this.footer.offsetHeight;
  970. var viewerWidth = viewerData.width;
  971. var viewerHeight = Math.max(viewerData.height - footerHeight, footerHeight);
  972. var oldImageData = this.imageData || {};
  973. var sizingImage = void 0;
  974.  
  975. this.imageInitializing = {
  976. abort: function abort() {
  977. sizingImage.onload = null;
  978. }
  979. };
  980.  
  981. sizingImage = getImageNaturalSizes(image, function (naturalWidth, naturalHeight) {
  982. var aspectRatio = naturalWidth / naturalHeight;
  983. var width = viewerWidth;
  984. var height = viewerHeight;
  985.  
  986. _this2.imageInitializing = false;
  987.  
  988. if (viewerHeight * aspectRatio > viewerWidth) {
  989. height = viewerWidth / aspectRatio;
  990. } else {
  991. width = viewerHeight * aspectRatio;
  992. }
  993.  
  994. width = Math.min(width * 0.9, naturalWidth);
  995. height = Math.min(height * 0.9, naturalHeight);
  996.  
  997. var imageData = {
  998. naturalWidth: naturalWidth,
  999. naturalHeight: naturalHeight,
  1000. aspectRatio: aspectRatio,
  1001. ratio: width / naturalWidth,
  1002. width: width,
  1003. height: height,
  1004. left: (viewerWidth - width) / 2,
  1005. top: (viewerHeight - height) / 2
  1006. };
  1007. var initialImageData = assign({}, imageData);
  1008.  
  1009. if (options.rotatable) {
  1010. imageData.rotate = oldImageData.rotate || 0;
  1011. initialImageData.rotate = 0;
  1012. }
  1013.  
  1014. if (options.scalable) {
  1015. imageData.scaleX = oldImageData.scaleX || 1;
  1016. imageData.scaleY = oldImageData.scaleY || 1;
  1017. initialImageData.scaleX = 1;
  1018. initialImageData.scaleY = 1;
  1019. }
  1020.  
  1021. _this2.imageData = imageData;
  1022. _this2.initialImageData = initialImageData;
  1023.  
  1024. if (done) {
  1025. done();
  1026. }
  1027. });
  1028. },
  1029. renderImage: function renderImage(done) {
  1030. var _this3 = this;
  1031.  
  1032. var image = this.image,
  1033. imageData = this.imageData;
  1034.  
  1035. setStyle(image, assign({
  1036. width: imageData.width,
  1037. height: imageData.height,
  1038. marginLeft: imageData.left,
  1039. marginTop: imageData.top
  1040. }, getTransforms(imageData)));
  1041.  
  1042. if (done) {
  1043. if (this.viewing && this.options.transition) {
  1044. var onTransitionEnd = function onTransitionEnd() {
  1045. _this3.imageRendering = false;
  1046. done();
  1047. };
  1048.  
  1049. this.imageRendering = {
  1050. abort: function abort() {
  1051. removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
  1052. }
  1053. };
  1054.  
  1055. addListener(image, EVENT_TRANSITION_END, onTransitionEnd, {
  1056. once: true
  1057. });
  1058. } else {
  1059. done();
  1060. }
  1061. }
  1062. },
  1063. resetImage: function resetImage() {
  1064. // this.image only defined after viewed
  1065. if (this.viewing || this.viewed) {
  1066. var image = this.image;
  1067.  
  1068. if (this.viewing) {
  1069. this.viewing.abort();
  1070. }
  1071.  
  1072. image.parentNode.removeChild(image);
  1073. this.image = null;
  1074. }
  1075. }
  1076. };
  1077.  
  1078. var events = {
  1079. bind: function bind() {
  1080. var element = this.element,
  1081. viewer = this.viewer;
  1082.  
  1083. addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
  1084. addListener(viewer, EVENT_WHEEL, this.onWheel = this.wheel.bind(this));
  1085. addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
  1086. addListener(this.canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
  1087. addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
  1088. addListener(element.ownerDocument, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
  1089. addListener(element.ownerDocument, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this));
  1090. addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
  1091. },
  1092. unbind: function unbind() {
  1093. var element = this.element,
  1094. viewer = this.viewer;
  1095.  
  1096. removeListener(viewer, EVENT_CLICK, this.onClick);
  1097. removeListener(viewer, EVENT_WHEEL, this.onWheel);
  1098. removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
  1099. removeListener(this.canvas, EVENT_POINTER_DOWN, this.onPointerDown);
  1100. removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onPointerMove);
  1101. removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onPointerUp);
  1102. removeListener(element.ownerDocument, EVENT_KEY_DOWN, this.onKeyDown);
  1103. removeListener(window, EVENT_RESIZE, this.onResize);
  1104. }
  1105. };
  1106.  
  1107. var handlers = {
  1108. click: function click(_ref) {
  1109. var target = _ref.target;
  1110. var options = this.options,
  1111. imageData = this.imageData;
  1112.  
  1113. var action = getData(target, 'action');
  1114.  
  1115. switch (action) {
  1116. case 'mix':
  1117. if (this.played) {
  1118. this.stop();
  1119. } else if (options.inline) {
  1120. if (this.fulled) {
  1121. this.exit();
  1122. } else {
  1123. this.full();
  1124. }
  1125. } else {
  1126. this.hide();
  1127. }
  1128.  
  1129. break;
  1130.  
  1131. case 'hide':
  1132. this.hide();
  1133. break;
  1134.  
  1135. case 'view':
  1136. this.view(getData(target, 'index'));
  1137. break;
  1138.  
  1139. case 'zoom-in':
  1140. this.zoom(0.1, true);
  1141. break;
  1142.  
  1143. case 'zoom-out':
  1144. this.zoom(-0.1, true);
  1145. break;
  1146.  
  1147. case 'one-to-one':
  1148. this.toggle();
  1149. break;
  1150.  
  1151. case 'reset':
  1152. this.reset();
  1153. break;
  1154.  
  1155. case 'prev':
  1156. this.prev(options.loop);
  1157. break;
  1158.  
  1159. case 'play':
  1160. this.play(options.fullscreen);
  1161. break;
  1162.  
  1163. case 'next':
  1164. this.next(options.loop);
  1165. break;
  1166.  
  1167. case 'rotate-left':
  1168. this.rotate(-90);
  1169. break;
  1170.  
  1171. case 'rotate-right':
  1172. this.rotate(90);
  1173. break;
  1174.  
  1175. case 'flip-horizontal':
  1176. this.scaleX(-imageData.scaleX || -1);
  1177. break;
  1178.  
  1179. case 'flip-vertical':
  1180. this.scaleY(-imageData.scaleY || -1);
  1181. break;
  1182.  
  1183. default:
  1184. if (this.played) {
  1185. this.stop();
  1186. }
  1187. }
  1188. },
  1189. load: function load() {
  1190. var _this = this;
  1191.  
  1192. if (this.timeout) {
  1193. clearTimeout(this.timeout);
  1194. this.timeout = false;
  1195. }
  1196.  
  1197. var element = this.element,
  1198. options = this.options,
  1199. image = this.image,
  1200. index = this.index,
  1201. viewerData = this.viewerData;
  1202.  
  1203. removeClass(image, CLASS_INVISIBLE);
  1204.  
  1205. if (options.loading) {
  1206. removeClass(this.canvas, CLASS_LOADING);
  1207. }
  1208.  
  1209. image.style.cssText = 'height:0;' + ('margin-left:' + viewerData.width / 2 + 'px;') + ('margin-top:' + viewerData.height / 2 + 'px;') + 'max-width:none!important;' + 'position:absolute;' + 'width:0;';
  1210.  
  1211. this.initImage(function () {
  1212. toggleClass(image, CLASS_MOVE, options.movable);
  1213. toggleClass(image, CLASS_TRANSITION, options.transition);
  1214.  
  1215. _this.renderImage(function () {
  1216. _this.viewed = true;
  1217. _this.viewing = false;
  1218.  
  1219. if (isFunction(options.viewed)) {
  1220. addListener(element, EVENT_VIEWED, options.viewed, {
  1221. once: true
  1222. });
  1223. }
  1224.  
  1225. dispatchEvent(element, EVENT_VIEWED, {
  1226. originalImage: _this.images[index],
  1227. index: index,
  1228. image: image
  1229. });
  1230. });
  1231. });
  1232. },
  1233. loadImage: function loadImage(e) {
  1234. var image = e.target;
  1235. var parent = image.parentNode;
  1236. var parentWidth = parent.offsetWidth || 30;
  1237. var parentHeight = parent.offsetHeight || 50;
  1238. var filled = !!getData(image, 'filled');
  1239.  
  1240. getImageNaturalSizes(image, function (naturalWidth, naturalHeight) {
  1241. var aspectRatio = naturalWidth / naturalHeight;
  1242. var width = parentWidth;
  1243. var height = parentHeight;
  1244.  
  1245. if (parentHeight * aspectRatio > parentWidth) {
  1246. if (filled) {
  1247. width = parentHeight * aspectRatio;
  1248. } else {
  1249. height = parentWidth / aspectRatio;
  1250. }
  1251. } else if (filled) {
  1252. height = parentWidth / aspectRatio;
  1253. } else {
  1254. width = parentHeight * aspectRatio;
  1255. }
  1256.  
  1257. setStyle(image, assign({
  1258. width: width,
  1259. height: height
  1260. }, getTransforms({
  1261. translateX: (parentWidth - width) / 2,
  1262. translateY: (parentHeight - height) / 2
  1263. })));
  1264. });
  1265. },
  1266. keydown: function keydown(e) {
  1267. var options = this.options;
  1268.  
  1269. if (!this.fulled || !options.keyboard) {
  1270. return;
  1271. }
  1272.  
  1273. switch (e.keyCode || e.which || e.charCode) {
  1274. // Escape
  1275. case 27:
  1276. if (this.played) {
  1277. this.stop();
  1278. } else if (options.inline) {
  1279. if (this.fulled) {
  1280. this.exit();
  1281. }
  1282. } else {
  1283. this.hide();
  1284. }
  1285.  
  1286. break;
  1287.  
  1288. // Space
  1289. case 32:
  1290. if (this.played) {
  1291. this.stop();
  1292. }
  1293.  
  1294. break;
  1295.  
  1296. // ArrowLeft
  1297. case 37:
  1298. this.prev(options.loop);
  1299. break;
  1300.  
  1301. // ArrowUp
  1302. case 38:
  1303. // Prevent scroll on Firefox
  1304. e.preventDefault();
  1305.  
  1306. // Zoom in
  1307. this.zoom(options.zoomRatio, true);
  1308. break;
  1309.  
  1310. // ArrowRight
  1311. case 39:
  1312. this.next(options.loop);
  1313. break;
  1314.  
  1315. // ArrowDown
  1316. case 40:
  1317. // Prevent scroll on Firefox
  1318. e.preventDefault();
  1319.  
  1320. // Zoom out
  1321. this.zoom(-options.zoomRatio, true);
  1322. break;
  1323.  
  1324. // Ctrl + 0
  1325. case 48:
  1326. // Fall through
  1327.  
  1328. // Ctrl + 1
  1329. // eslint-disable-next-line no-fallthrough
  1330. case 49:
  1331. if (e.ctrlKey) {
  1332. e.preventDefault();
  1333. this.toggle();
  1334. }
  1335.  
  1336. break;
  1337.  
  1338. default:
  1339. }
  1340. },
  1341. dragstart: function dragstart(e) {
  1342. if (e.target.tagName.toLowerCase() === 'img') {
  1343. e.preventDefault();
  1344. }
  1345. },
  1346. pointerdown: function pointerdown(e) {
  1347. var options = this.options,
  1348. pointers = this.pointers;
  1349.  
  1350. if (!this.viewed || this.showing || this.viewing || this.hiding) {
  1351. return;
  1352. }
  1353.  
  1354. if (e.changedTouches) {
  1355. forEach(e.changedTouches, function (touch) {
  1356. pointers[touch.identifier] = getPointer(touch);
  1357. });
  1358. } else {
  1359. pointers[e.pointerId || 0] = getPointer(e);
  1360. }
  1361.  
  1362. var action = options.movable ? ACTION_MOVE : false;
  1363.  
  1364. if (Object.keys(pointers).length > 1) {
  1365. action = ACTION_ZOOM;
  1366. } else if ((e.pointerType === 'touch' || e.type === 'touchstart') && this.isSwitchable()) {
  1367. action = ACTION_SWITCH;
  1368. }
  1369.  
  1370. this.action = action;
  1371. },
  1372. pointermove: function pointermove(e) {
  1373. var options = this.options,
  1374. pointers = this.pointers,
  1375. action = this.action,
  1376. image = this.image;
  1377.  
  1378. if (!this.viewed || !action) {
  1379. return;
  1380. }
  1381.  
  1382. e.preventDefault();
  1383.  
  1384. if (e.changedTouches) {
  1385. forEach(e.changedTouches, function (touch) {
  1386. assign(pointers[touch.identifier], getPointer(touch, true));
  1387. });
  1388. } else {
  1389. assign(pointers[e.pointerId || 0], getPointer(e, true));
  1390. }
  1391.  
  1392. if (action === ACTION_MOVE && options.transition && hasClass(image, CLASS_TRANSITION)) {
  1393. removeClass(image, CLASS_TRANSITION);
  1394. }
  1395.  
  1396. this.change(e);
  1397. },
  1398. pointerup: function pointerup(e) {
  1399. var action = this.action,
  1400. pointers = this.pointers;
  1401.  
  1402. if (e.changedTouches) {
  1403. forEach(e.changedTouches, function (touch) {
  1404. delete pointers[touch.identifier];
  1405. });
  1406. } else {
  1407. delete pointers[e.pointerId || 0];
  1408. }
  1409.  
  1410. if (!action) {
  1411. return;
  1412. }
  1413.  
  1414. if (action === ACTION_MOVE && this.options.transition) {
  1415. addClass(this.image, CLASS_TRANSITION);
  1416. }
  1417.  
  1418. this.action = false;
  1419. },
  1420. resize: function resize() {
  1421. var _this2 = this;
  1422.  
  1423. if (!this.isShown || this.hiding) {
  1424. return;
  1425. }
  1426.  
  1427. this.initContainer();
  1428. this.initViewer();
  1429. this.renderViewer();
  1430. this.renderList();
  1431.  
  1432. if (this.viewed) {
  1433. this.initImage(function () {
  1434. _this2.renderImage();
  1435. });
  1436. }
  1437.  
  1438. if (this.played) {
  1439. if (this.options.fullscreen && this.fulled && !document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
  1440. this.stop();
  1441. return;
  1442. }
  1443.  
  1444. forEach(this.player.getElementsByTagName('img'), function (image) {
  1445. addListener(image, EVENT_LOAD, _this2.loadImage.bind(_this2), {
  1446. once: true
  1447. });
  1448. dispatchEvent(image, EVENT_LOAD);
  1449. });
  1450. }
  1451. },
  1452. wheel: function wheel(e) {
  1453. var _this3 = this;
  1454.  
  1455. if (!this.viewed) {
  1456. return;
  1457. }
  1458.  
  1459. e.preventDefault();
  1460.  
  1461. // Limit wheel speed to prevent zoom too fast
  1462. if (this.wheeling) {
  1463. return;
  1464. }
  1465.  
  1466. this.wheeling = true;
  1467.  
  1468. setTimeout(function () {
  1469. _this3.wheeling = false;
  1470. }, 50);
  1471.  
  1472. var ratio = Number(this.options.zoomRatio) || 0.1;
  1473. var delta = 1;
  1474.  
  1475. if (e.deltaY) {
  1476. delta = e.deltaY > 0 ? 1 : -1;
  1477. } else if (e.wheelDelta) {
  1478. delta = -e.wheelDelta / 120;
  1479. } else if (e.detail) {
  1480. delta = e.detail > 0 ? 1 : -1;
  1481. }
  1482.  
  1483. this.zoom(-delta * ratio, true, e);
  1484. }
  1485. };
  1486.  
  1487. var methods = {
  1488. /** Show the viewer (only available in modal mode)
  1489. * @param {boolean} [immediate=false] - Indicates if show the viewer immediately or not.
  1490. * @returns {Viewer} this
  1491. */
  1492. show: function show() {
  1493. var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1494. var element = this.element,
  1495. options = this.options;
  1496.  
  1497. if (options.inline || this.showing || this.isShown || this.showing) {
  1498. return this;
  1499. }
  1500.  
  1501. if (!this.ready) {
  1502. this.build();
  1503.  
  1504. if (this.ready) {
  1505. this.show(immediate);
  1506. }
  1507.  
  1508. return this;
  1509. }
  1510.  
  1511. if (isFunction(options.show)) {
  1512. addListener(element, EVENT_SHOW, options.show, {
  1513. once: true
  1514. });
  1515. }
  1516.  
  1517. if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
  1518. return this;
  1519. }
  1520.  
  1521. if (this.hiding) {
  1522. this.transitioning.abort();
  1523. }
  1524.  
  1525. this.showing = true;
  1526. this.open();
  1527.  
  1528. var viewer = this.viewer;
  1529.  
  1530. removeClass(viewer, CLASS_HIDE);
  1531.  
  1532. if (options.transition && !immediate) {
  1533. var shown = this.shown.bind(this);
  1534.  
  1535. this.transitioning = {
  1536. abort: function abort() {
  1537. removeListener(viewer, EVENT_TRANSITION_END, shown);
  1538. removeClass(viewer, CLASS_IN);
  1539. }
  1540. };
  1541.  
  1542. addClass(viewer, CLASS_TRANSITION);
  1543.  
  1544. // Force reflow to enable CSS3 transition
  1545. // eslint-disable-next-line
  1546. viewer.offsetWidth;
  1547. addListener(viewer, EVENT_TRANSITION_END, shown, {
  1548. once: true
  1549. });
  1550. addClass(viewer, CLASS_IN);
  1551. } else {
  1552. addClass(viewer, CLASS_IN);
  1553. this.shown();
  1554. }
  1555.  
  1556. return this;
  1557. },
  1558.  
  1559. /**
  1560. * Hide the viewer (only available in modal mode)
  1561. * @param {boolean} [immediate=false] - Indicates if hide the viewer immediately or not.
  1562. * @returns {Viewer} this
  1563. */
  1564. hide: function hide() {
  1565. var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1566. var element = this.element,
  1567. options = this.options;
  1568.  
  1569. if (options.inline || this.hiding || !(this.isShown || this.showing)) {
  1570. return this;
  1571. }
  1572.  
  1573. if (isFunction(options.hide)) {
  1574. addListener(element, EVENT_HIDE, options.hide, {
  1575. once: true
  1576. });
  1577. }
  1578.  
  1579. if (dispatchEvent(element, EVENT_HIDE) === false) {
  1580. return this;
  1581. }
  1582.  
  1583. if (this.showing) {
  1584. this.transitioning.abort();
  1585. }
  1586.  
  1587. this.hiding = true;
  1588.  
  1589. if (this.played) {
  1590. this.stop();
  1591. } else if (this.viewing) {
  1592. this.viewing.abort();
  1593. }
  1594.  
  1595. var viewer = this.viewer;
  1596.  
  1597. if (options.transition && !immediate) {
  1598. var hidden = this.hidden.bind(this);
  1599. var hide = function hide() {
  1600. addListener(viewer, EVENT_TRANSITION_END, hidden, {
  1601. once: true
  1602. });
  1603. removeClass(viewer, CLASS_IN);
  1604. };
  1605.  
  1606. this.transitioning = {
  1607. abort: function abort() {
  1608. if (this.viewed) {
  1609. removeListener(this.image, EVENT_TRANSITION_END, hide);
  1610. } else {
  1611. removeListener(viewer, EVENT_TRANSITION_END, hidden);
  1612. }
  1613. }
  1614. };
  1615.  
  1616. if (this.viewed) {
  1617. addListener(this.image, EVENT_TRANSITION_END, hide, {
  1618. once: true
  1619. });
  1620. this.zoomTo(0, false, false, true);
  1621. } else {
  1622. hide();
  1623. }
  1624. } else {
  1625. removeClass(viewer, CLASS_IN);
  1626. this.hidden();
  1627. }
  1628.  
  1629. return this;
  1630. },
  1631.  
  1632. /**
  1633. * View one of the images with image's index
  1634. * @param {number} index - The index of the image to view.
  1635. * @returns {Viewer} this
  1636. */
  1637. view: function view() {
  1638. var _this = this;
  1639.  
  1640. var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  1641.  
  1642. index = Number(index) || 0;
  1643.  
  1644. if (!this.isShown) {
  1645. this.index = index;
  1646. return this.show();
  1647. }
  1648.  
  1649. if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
  1650. return this;
  1651. }
  1652.  
  1653. if (this.viewing) {
  1654. this.viewing.abort();
  1655. }
  1656.  
  1657. var element = this.element,
  1658. options = this.options,
  1659. title = this.title,
  1660. canvas = this.canvas;
  1661.  
  1662. var item = this.items[index];
  1663. var img = item.querySelector('img');
  1664. var url = getData(img, 'originalUrl');
  1665. var alt = img.getAttribute('alt');
  1666. var image = document.createElement('img');
  1667.  
  1668. image.src = url;
  1669. image.alt = alt;
  1670.  
  1671. if (isFunction(options.view)) {
  1672. addListener(element, EVENT_VIEW, options.view, {
  1673. once: true
  1674. });
  1675. }
  1676.  
  1677. if (dispatchEvent(element, EVENT_VIEW, {
  1678. originalImage: this.images[index],
  1679. index: index,
  1680. image: image
  1681. }) === false || !this.isShown || this.hiding || this.played) {
  1682. return this;
  1683. }
  1684.  
  1685. this.image = image;
  1686. removeClass(this.items[this.index], CLASS_ACTIVE);
  1687. addClass(item, CLASS_ACTIVE);
  1688. this.viewed = false;
  1689. this.index = index;
  1690. this.imageData = {};
  1691. addClass(image, CLASS_INVISIBLE);
  1692.  
  1693. if (options.loading) {
  1694. addClass(canvas, CLASS_LOADING);
  1695. }
  1696.  
  1697. canvas.innerHTML = '';
  1698. canvas.appendChild(image);
  1699.  
  1700. // Center current item
  1701. this.renderList();
  1702.  
  1703. // Clear title
  1704. title.innerHTML = '';
  1705.  
  1706. // Generate title after viewed
  1707. var onViewed = function onViewed() {
  1708. var imageData = _this.imageData;
  1709.  
  1710. title.textContent = alt + ' (' + imageData.naturalWidth + ' \xD7 ' + imageData.naturalHeight + ')';
  1711. };
  1712. var onLoad = void 0;
  1713.  
  1714. addListener(element, EVENT_VIEWED, onViewed, {
  1715. once: true
  1716. });
  1717.  
  1718. this.viewing = {
  1719. abort: function abort() {
  1720. removeListener(element, EVENT_VIEWED, onViewed);
  1721.  
  1722. if (image.complete) {
  1723. if (this.imageRendering) {
  1724. this.imageRendering.abort();
  1725. } else if (this.imageInitializing) {
  1726. this.imageInitializing.abort();
  1727. }
  1728. } else {
  1729. removeListener(image, EVENT_LOAD, onLoad);
  1730.  
  1731. if (this.timeout) {
  1732. clearTimeout(this.timeout);
  1733. }
  1734. }
  1735. }
  1736. };
  1737.  
  1738. if (image.complete) {
  1739. this.load();
  1740. } else {
  1741. addListener(image, EVENT_LOAD, onLoad = this.load.bind(this), {
  1742. once: true
  1743. });
  1744.  
  1745. if (this.timeout) {
  1746. clearTimeout(this.timeout);
  1747. }
  1748.  
  1749. // Make the image visible if it fails to load within 1s
  1750. this.timeout = setTimeout(function () {
  1751. removeClass(image, CLASS_INVISIBLE);
  1752. _this.timeout = false;
  1753. }, 1000);
  1754. }
  1755.  
  1756. return this;
  1757. },
  1758.  
  1759. /**
  1760. * View the previous image
  1761. * @param {boolean} [loop=false] - Indicate if view the last one
  1762. * when it is the first one at present.
  1763. * @returns {Viewer} this
  1764. */
  1765. prev: function prev() {
  1766. var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1767.  
  1768. var index = this.index - 1;
  1769.  
  1770. if (index < 0) {
  1771. index = loop ? this.length - 1 : 0;
  1772. }
  1773.  
  1774. this.view(index);
  1775. return this;
  1776. },
  1777.  
  1778. /**
  1779. * View the next image
  1780. * @param {boolean} [loop=false] - Indicate if view the first one
  1781. * when it is the last one at present.
  1782. * @returns {Viewer} this
  1783. */
  1784. next: function next() {
  1785. var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  1786.  
  1787. var maxIndex = this.length - 1;
  1788. var index = this.index + 1;
  1789.  
  1790. if (index > maxIndex) {
  1791. index = loop ? 0 : maxIndex;
  1792. }
  1793.  
  1794. this.view(index);
  1795. return this;
  1796. },
  1797.  
  1798. /**
  1799. * Move the image with relative offsets.
  1800. * @param {number} offsetX - The relative offset distance on the x-axis.
  1801. * @param {number} offsetY - The relative offset distance on the y-axis.
  1802. * @returns {Viewer} this
  1803. */
  1804. move: function move(offsetX, offsetY) {
  1805. var imageData = this.imageData;
  1806.  
  1807. this.moveTo(isUndefined(offsetX) ? offsetX : imageData.left + Number(offsetX), isUndefined(offsetY) ? offsetY : imageData.top + Number(offsetY));
  1808.  
  1809. return this;
  1810. },
  1811.  
  1812. /**
  1813. * Move the image to an absolute point.
  1814. * @param {number} x - The x-axis coordinate.
  1815. * @param {number} [y=x] - The y-axis coordinate.
  1816. * @returns {Viewer} this
  1817. */
  1818. moveTo: function moveTo(x) {
  1819. var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
  1820. var imageData = this.imageData;
  1821.  
  1822. x = Number(x);
  1823. y = Number(y);
  1824.  
  1825. if (this.viewed && !this.played && this.options.movable) {
  1826. var changed = false;
  1827.  
  1828. if (isNumber(x)) {
  1829. imageData.left = x;
  1830. changed = true;
  1831. }
  1832.  
  1833. if (isNumber(y)) {
  1834. imageData.top = y;
  1835. changed = true;
  1836. }
  1837.  
  1838. if (changed) {
  1839. this.renderImage();
  1840. }
  1841. }
  1842.  
  1843. return this;
  1844. },
  1845.  
  1846. /**
  1847. * Zoom the image with a relative ratio.
  1848. * @param {number} ratio - The target ratio.
  1849. * @param {boolean} [hasTooltip=false] - Indicates if it has a tooltip or not.
  1850. * @param {Event} [_originalEvent=null] - The original event if any.
  1851. * @returns {Viewer} this
  1852. */
  1853. zoom: function zoom(ratio) {
  1854. var hasTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  1855.  
  1856. var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  1857.  
  1858. var imageData = this.imageData;
  1859.  
  1860. ratio = Number(ratio);
  1861.  
  1862. if (ratio < 0) {
  1863. ratio = 1 / (1 - ratio);
  1864. } else {
  1865. ratio = 1 + ratio;
  1866. }
  1867.  
  1868. this.zoomTo(imageData.width * ratio / imageData.naturalWidth, hasTooltip, _originalEvent);
  1869.  
  1870. return this;
  1871. },
  1872.  
  1873. /**
  1874. * Zoom the image to an absolute ratio.
  1875. * @param {number} ratio - The target ratio.
  1876. * @param {boolean} [hasTooltip=false] - Indicates if it has a tooltip or not.
  1877. * @param {Event} [_originalEvent=null] - The original event if any.
  1878. * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
  1879. * @returns {Viewer} this
  1880. */
  1881. zoomTo: function zoomTo(ratio) {
  1882. var hasTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  1883.  
  1884. var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
  1885.  
  1886. var _zoomable = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
  1887.  
  1888. var options = this.options,
  1889. pointers = this.pointers,
  1890. imageData = this.imageData;
  1891.  
  1892. ratio = Math.max(0, ratio);
  1893.  
  1894. if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
  1895. if (!_zoomable) {
  1896. var minZoomRatio = Math.max(0.01, options.minZoomRatio);
  1897. var maxZoomRatio = Math.min(100, options.maxZoomRatio);
  1898.  
  1899. ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
  1900. }
  1901.  
  1902. if (_originalEvent && ratio > 0.95 && ratio < 1.05) {
  1903. ratio = 1;
  1904. }
  1905.  
  1906. var newWidth = imageData.naturalWidth * ratio;
  1907. var newHeight = imageData.naturalHeight * ratio;
  1908.  
  1909. if (_originalEvent) {
  1910. var offset = getOffset(this.viewer);
  1911. var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {
  1912. pageX: _originalEvent.pageX,
  1913. pageY: _originalEvent.pageY
  1914. };
  1915.  
  1916. // Zoom from the triggering point of the event
  1917. imageData.left -= (newWidth - imageData.width) * ((center.pageX - offset.left - imageData.left) / imageData.width);
  1918. imageData.top -= (newHeight - imageData.height) * ((center.pageY - offset.top - imageData.top) / imageData.height);
  1919. } else {
  1920. // Zoom from the center of the image
  1921. imageData.left -= (newWidth - imageData.width) / 2;
  1922. imageData.top -= (newHeight - imageData.height) / 2;
  1923. }
  1924.  
  1925. imageData.width = newWidth;
  1926. imageData.height = newHeight;
  1927. imageData.ratio = ratio;
  1928. this.renderImage();
  1929.  
  1930. if (hasTooltip) {
  1931. this.tooltip();
  1932. }
  1933. }
  1934.  
  1935. return this;
  1936. },
  1937.  
  1938. /**
  1939. * Rotate the image with a relative degree.
  1940. * @param {number} degree - The rotate degree.
  1941. * @returns {Viewer} this
  1942. */
  1943. rotate: function rotate(degree) {
  1944. this.rotateTo((this.imageData.rotate || 0) + Number(degree));
  1945.  
  1946. return this;
  1947. },
  1948.  
  1949. /**
  1950. * Rotate the image to an absolute degree.
  1951. * @param {number} degree - The rotate degree.
  1952. * @returns {Viewer} this
  1953. */
  1954. rotateTo: function rotateTo(degree) {
  1955. var imageData = this.imageData;
  1956.  
  1957. degree = Number(degree);
  1958.  
  1959. if (isNumber(degree) && this.viewed && !this.played && this.options.rotatable) {
  1960. imageData.rotate = degree;
  1961. this.renderImage();
  1962. }
  1963.  
  1964. return this;
  1965. },
  1966.  
  1967. /**
  1968. * Scale the image on the x-axis.
  1969. * @param {number} scaleX - The scale ratio on the x-axis.
  1970. * @returns {Viewer} this
  1971. */
  1972. scaleX: function scaleX(_scaleX) {
  1973. this.scale(_scaleX, this.imageData.scaleY);
  1974.  
  1975. return this;
  1976. },
  1977.  
  1978. /**
  1979. * Scale the image on the y-axis.
  1980. * @param {number} scaleY - The scale ratio on the y-axis.
  1981. * @returns {Viewer} this
  1982. */
  1983. scaleY: function scaleY(_scaleY) {
  1984. this.scale(this.imageData.scaleX, _scaleY);
  1985.  
  1986. return this;
  1987. },
  1988.  
  1989. /**
  1990. * Scale the image.
  1991. * @param {number} scaleX - The scale ratio on the x-axis.
  1992. * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
  1993. * @returns {Viewer} this
  1994. */
  1995. scale: function scale(scaleX) {
  1996. var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
  1997. var imageData = this.imageData;
  1998.  
  1999. scaleX = Number(scaleX);
  2000. scaleY = Number(scaleY);
  2001.  
  2002. if (this.viewed && !this.played && this.options.scalable) {
  2003. var changed = false;
  2004.  
  2005. if (isNumber(scaleX)) {
  2006. imageData.scaleX = scaleX;
  2007. changed = true;
  2008. }
  2009.  
  2010. if (isNumber(scaleY)) {
  2011. imageData.scaleY = scaleY;
  2012. changed = true;
  2013. }
  2014.  
  2015. if (changed) {
  2016. this.renderImage();
  2017. }
  2018. }
  2019.  
  2020. return this;
  2021. },
  2022.  
  2023. /**
  2024. * Play the images
  2025. * @param {boolean} [fullscreen=false] - Indicate if request fullscreen or not.
  2026. * @returns {Viewer} this
  2027. */
  2028. play: function play() {
  2029. var _this2 = this;
  2030.  
  2031. var fullscreen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  2032.  
  2033. if (!this.isShown || this.played) {
  2034. return this;
  2035. }
  2036.  
  2037. var options = this.options,
  2038. player = this.player;
  2039.  
  2040. var onLoad = this.loadImage.bind(this);
  2041. var list = [];
  2042. var total = 0;
  2043. var index = 0;
  2044.  
  2045. this.played = true;
  2046. this.onLoadWhenPlay = onLoad;
  2047.  
  2048. if (fullscreen) {
  2049. this.requestFullscreen();
  2050. }
  2051.  
  2052. addClass(player, CLASS_SHOW);
  2053. forEach(this.items, function (item, i) {
  2054. var img = item.querySelector('img');
  2055. var image = document.createElement('img');
  2056.  
  2057. image.src = getData(img, 'originalUrl');
  2058. image.alt = img.getAttribute('alt');
  2059. total += 1;
  2060. addClass(image, CLASS_FADE);
  2061. toggleClass(image, CLASS_TRANSITION, options.transition);
  2062.  
  2063. if (hasClass(item, CLASS_ACTIVE)) {
  2064. addClass(image, CLASS_IN);
  2065. index = i;
  2066. }
  2067.  
  2068. list.push(image);
  2069. addListener(image, EVENT_LOAD, onLoad, {
  2070. once: true
  2071. });
  2072. player.appendChild(image);
  2073. });
  2074.  
  2075. if (isNumber(options.interval) && options.interval > 0) {
  2076. var play = function play() {
  2077. _this2.playing = setTimeout(function () {
  2078. removeClass(list[index], CLASS_IN);
  2079. index += 1;
  2080. index = index < total ? index : 0;
  2081. addClass(list[index], CLASS_IN);
  2082. play();
  2083. }, options.interval);
  2084. };
  2085.  
  2086. if (total > 1) {
  2087. play();
  2088. }
  2089. }
  2090.  
  2091. return this;
  2092. },
  2093.  
  2094. // Stop play
  2095. stop: function stop() {
  2096. var _this3 = this;
  2097.  
  2098. if (!this.played) {
  2099. return this;
  2100. }
  2101.  
  2102. var player = this.player;
  2103.  
  2104. this.played = false;
  2105. clearTimeout(this.playing);
  2106. forEach(player.getElementsByTagName('img'), function (image) {
  2107. removeListener(image, EVENT_LOAD, _this3.onLoadWhenPlay);
  2108. });
  2109. removeClass(player, CLASS_SHOW);
  2110. player.innerHTML = '';
  2111. this.exitFullscreen();
  2112.  
  2113. return this;
  2114. },
  2115.  
  2116. // Enter modal mode (only available in inline mode)
  2117. full: function full() {
  2118. var _this4 = this;
  2119.  
  2120. var options = this.options,
  2121. viewer = this.viewer,
  2122. image = this.image,
  2123. list = this.list;
  2124.  
  2125. if (!this.isShown || this.played || this.fulled || !options.inline) {
  2126. return this;
  2127. }
  2128.  
  2129. this.fulled = true;
  2130. this.open();
  2131. addClass(this.button, CLASS_FULLSCREEN_EXIT);
  2132.  
  2133. if (options.transition) {
  2134. removeClass(list, CLASS_TRANSITION);
  2135.  
  2136. if (this.viewed) {
  2137. removeClass(image, CLASS_TRANSITION);
  2138. }
  2139. }
  2140.  
  2141. addClass(viewer, CLASS_FIXED);
  2142. viewer.setAttribute('style', '');
  2143. setStyle(viewer, {
  2144. zIndex: options.zIndex
  2145. });
  2146.  
  2147. this.initContainer();
  2148. this.viewerData = assign({}, this.containerData);
  2149. this.renderList();
  2150.  
  2151. if (this.viewed) {
  2152. this.initImage(function () {
  2153. _this4.renderImage(function () {
  2154. if (options.transition) {
  2155. setTimeout(function () {
  2156. addClass(image, CLASS_TRANSITION);
  2157. addClass(list, CLASS_TRANSITION);
  2158. }, 0);
  2159. }
  2160. });
  2161. });
  2162. }
  2163.  
  2164. return this;
  2165. },
  2166.  
  2167. // Exit modal mode (only available in inline mode)
  2168. exit: function exit() {
  2169. var _this5 = this;
  2170.  
  2171. var options = this.options,
  2172. viewer = this.viewer,
  2173. image = this.image,
  2174. list = this.list;
  2175.  
  2176. if (!this.isShown || this.played || !this.fulled || !options.inline) {
  2177. return this;
  2178. }
  2179.  
  2180. this.fulled = false;
  2181. this.close();
  2182. removeClass(this.button, CLASS_FULLSCREEN_EXIT);
  2183.  
  2184. if (options.transition) {
  2185. removeClass(list, CLASS_TRANSITION);
  2186.  
  2187. if (this.viewed) {
  2188. removeClass(image, CLASS_TRANSITION);
  2189. }
  2190. }
  2191.  
  2192. removeClass(viewer, CLASS_FIXED);
  2193. setStyle(viewer, {
  2194. zIndex: options.zIndexInline
  2195. });
  2196.  
  2197. this.viewerData = assign({}, this.parentData);
  2198. this.renderViewer();
  2199. this.renderList();
  2200.  
  2201. if (this.viewed) {
  2202. this.initImage(function () {
  2203. _this5.renderImage(function () {
  2204. if (options.transition) {
  2205. setTimeout(function () {
  2206. addClass(image, CLASS_TRANSITION);
  2207. addClass(list, CLASS_TRANSITION);
  2208. }, 0);
  2209. }
  2210. });
  2211. });
  2212. }
  2213.  
  2214. return this;
  2215. },
  2216.  
  2217. // Show the current ratio of the image with percentage
  2218. tooltip: function tooltip() {
  2219. var _this6 = this;
  2220.  
  2221. var options = this.options,
  2222. tooltipBox = this.tooltipBox,
  2223. imageData = this.imageData;
  2224.  
  2225. if (!this.viewed || this.played || !options.tooltip) {
  2226. return this;
  2227. }
  2228.  
  2229. tooltipBox.textContent = Math.round(imageData.ratio * 100) + '%';
  2230.  
  2231. if (!this.tooltipping) {
  2232. if (options.transition) {
  2233. if (this.fading) {
  2234. dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
  2235. }
  2236.  
  2237. addClass(tooltipBox, CLASS_SHOW);
  2238. addClass(tooltipBox, CLASS_FADE);
  2239. addClass(tooltipBox, CLASS_TRANSITION);
  2240.  
  2241. // Force reflow to enable CSS3 transition
  2242. // eslint-disable-next-line
  2243. tooltipBox.offsetWidth;
  2244. addClass(tooltipBox, CLASS_IN);
  2245. } else {
  2246. addClass(tooltipBox, CLASS_SHOW);
  2247. }
  2248. } else {
  2249. clearTimeout(this.tooltipping);
  2250. }
  2251.  
  2252. this.tooltipping = setTimeout(function () {
  2253. if (options.transition) {
  2254. addListener(tooltipBox, EVENT_TRANSITION_END, function () {
  2255. removeClass(tooltipBox, CLASS_SHOW);
  2256. removeClass(tooltipBox, CLASS_FADE);
  2257. removeClass(tooltipBox, CLASS_TRANSITION);
  2258. _this6.fading = false;
  2259. }, {
  2260. once: true
  2261. });
  2262.  
  2263. removeClass(tooltipBox, CLASS_IN);
  2264. _this6.fading = true;
  2265. } else {
  2266. removeClass(tooltipBox, CLASS_SHOW);
  2267. }
  2268.  
  2269. _this6.tooltipping = false;
  2270. }, 1000);
  2271.  
  2272. return this;
  2273. },
  2274.  
  2275. // Toggle the image size between its natural size and initial size
  2276. toggle: function toggle() {
  2277. if (this.imageData.ratio === 1) {
  2278. this.zoomTo(this.initialImageData.ratio, true);
  2279. } else {
  2280. this.zoomTo(1, true);
  2281. }
  2282.  
  2283. return this;
  2284. },
  2285.  
  2286. // Reset the image to its initial state
  2287. reset: function reset() {
  2288. if (this.viewed && !this.played) {
  2289. this.imageData = assign({}, this.initialImageData);
  2290. this.renderImage();
  2291. }
  2292.  
  2293. return this;
  2294. },
  2295.  
  2296. // Update viewer when images changed
  2297. update: function update() {
  2298. var element = this.element,
  2299. options = this.options,
  2300. isImg = this.isImg;
  2301.  
  2302. // Destroy viewer if the target image was deleted
  2303.  
  2304. if (isImg && !element.parentNode) {
  2305. return this.destroy();
  2306. }
  2307.  
  2308. var images = [];
  2309.  
  2310. forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
  2311. if (options.filter) {
  2312. if (options.filter(image)) {
  2313. images.push(image);
  2314. }
  2315. } else {
  2316. images.push(image);
  2317. }
  2318. });
  2319.  
  2320. if (!images.length) {
  2321. return this;
  2322. }
  2323.  
  2324. this.images = images;
  2325. this.length = images.length;
  2326.  
  2327. if (this.ready) {
  2328. var indexes = [];
  2329.  
  2330. forEach(this.items, function (item, i) {
  2331. var img = item.querySelector('img');
  2332. var image = images[i];
  2333.  
  2334. if (image) {
  2335. if (image.src !== img.src) {
  2336. indexes.push(i);
  2337. }
  2338. } else {
  2339. indexes.push(i);
  2340. }
  2341. });
  2342.  
  2343. setStyle(this.list, {
  2344. width: 'auto'
  2345. });
  2346.  
  2347. this.initList();
  2348.  
  2349. if (this.isShown) {
  2350. if (this.length) {
  2351. if (this.viewed) {
  2352. var index = indexes.indexOf(this.index);
  2353.  
  2354. if (index >= 0) {
  2355. this.viewed = false;
  2356. this.view(Math.max(this.index - (index + 1), 0));
  2357. } else {
  2358. addClass(this.items[this.index], CLASS_ACTIVE);
  2359. }
  2360. }
  2361. } else {
  2362. this.image = null;
  2363. this.viewed = false;
  2364. this.index = 0;
  2365. this.imageData = null;
  2366. this.canvas.innerHTML = '';
  2367. this.title.innerHTML = '';
  2368. }
  2369. }
  2370. } else {
  2371. this.build();
  2372. }
  2373.  
  2374. return this;
  2375. },
  2376.  
  2377. // Destroy the viewer
  2378. destroy: function destroy() {
  2379. var element = this.element,
  2380. options = this.options;
  2381.  
  2382. if (!getData(element, NAMESPACE)) {
  2383. return this;
  2384. }
  2385.  
  2386. this.destroyed = true;
  2387.  
  2388. if (this.ready) {
  2389. if (this.played) {
  2390. this.stop();
  2391. }
  2392.  
  2393. if (options.inline) {
  2394. if (this.fulled) {
  2395. this.exit();
  2396. }
  2397.  
  2398. this.unbind();
  2399. } else if (this.isShown) {
  2400. if (this.viewing) {
  2401. if (this.imageRendering) {
  2402. this.imageRendering.abort();
  2403. } else if (this.imageInitializing) {
  2404. this.imageInitializing.abort();
  2405. }
  2406. }
  2407.  
  2408. if (this.hiding) {
  2409. this.transitioning.abort();
  2410. }
  2411.  
  2412. this.hidden();
  2413. } else if (this.showing) {
  2414. this.transitioning.abort();
  2415. this.hidden();
  2416. }
  2417.  
  2418. this.ready = false;
  2419. this.viewer.parentNode.removeChild(this.viewer);
  2420. } else if (options.inline) {
  2421. if (this.delaying) {
  2422. this.delaying.abort();
  2423. } else if (this.initializing) {
  2424. this.initializing.abort();
  2425. }
  2426. }
  2427.  
  2428. if (!options.inline) {
  2429. removeListener(element, EVENT_CLICK, this.onStart);
  2430. }
  2431.  
  2432. removeData(element, NAMESPACE);
  2433. return this;
  2434. }
  2435. };
  2436.  
  2437. var others = {
  2438. open: function open() {
  2439. var body = this.body;
  2440.  
  2441. addClass(body, CLASS_OPEN);
  2442.  
  2443. body.style.paddingRight = this.scrollbarWidth + (parseFloat(this.initialBodyPaddingRight) || 0) + 'px';
  2444. },
  2445. close: function close() {
  2446. var body = this.body;
  2447.  
  2448. removeClass(body, CLASS_OPEN);
  2449. body.style.paddingRight = this.initialBodyPaddingRight;
  2450. },
  2451. shown: function shown() {
  2452. var element = this.element,
  2453. options = this.options;
  2454.  
  2455. this.fulled = true;
  2456. this.isShown = true;
  2457. this.render();
  2458. this.bind();
  2459. this.showing = false;
  2460.  
  2461. if (isFunction(options.shown)) {
  2462. addListener(element, EVENT_SHOWN, options.shown, {
  2463. once: true
  2464. });
  2465. }
  2466.  
  2467. if (dispatchEvent(element, EVENT_SHOWN) === false) {
  2468. return;
  2469. }
  2470.  
  2471. if (this.ready && this.isShown && !this.hiding) {
  2472. this.view(this.index);
  2473. }
  2474. },
  2475. hidden: function hidden() {
  2476. var element = this.element,
  2477. options = this.options;
  2478.  
  2479. this.fulled = false;
  2480. this.viewed = false;
  2481. this.isShown = false;
  2482. this.close();
  2483. this.unbind();
  2484. addClass(this.viewer, CLASS_HIDE);
  2485. this.resetList();
  2486. this.resetImage();
  2487. this.hiding = false;
  2488.  
  2489. if (!this.destroyed) {
  2490. if (isFunction(options.hidden)) {
  2491. addListener(element, EVENT_HIDDEN, options.hidden, {
  2492. once: true
  2493. });
  2494. }
  2495.  
  2496. dispatchEvent(element, EVENT_HIDDEN);
  2497. }
  2498. },
  2499. requestFullscreen: function requestFullscreen() {
  2500. var document = this.element.ownerDocument;
  2501.  
  2502. if (this.fulled && !document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
  2503. var documentElement = document.documentElement;
  2504.  
  2505. if (documentElement.requestFullscreen) {
  2506. documentElement.requestFullscreen();
  2507. } else if (documentElement.msRequestFullscreen) {
  2508. documentElement.msRequestFullscreen();
  2509. } else if (documentElement.mozRequestFullScreen) {
  2510. documentElement.mozRequestFullScreen();
  2511. } else if (documentElement.webkitRequestFullscreen) {
  2512. documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  2513. }
  2514. }
  2515. },
  2516. exitFullscreen: function exitFullscreen() {
  2517. if (this.fulled) {
  2518. var document = this.element.ownerDocument;
  2519.  
  2520. if (document.exitFullscreen) {
  2521. document.exitFullscreen();
  2522. } else if (document.msExitFullscreen) {
  2523. document.msExitFullscreen();
  2524. } else if (document.mozCancelFullScreen) {
  2525. document.mozCancelFullScreen();
  2526. } else if (document.webkitExitFullscreen) {
  2527. document.webkitExitFullscreen();
  2528. }
  2529. }
  2530. },
  2531. change: function change(e) {
  2532. var options = this.options,
  2533. pointers = this.pointers;
  2534.  
  2535. var pointer = pointers[Object.keys(pointers)[0]];
  2536. var offsetX = pointer.endX - pointer.startX;
  2537. var offsetY = pointer.endY - pointer.startY;
  2538.  
  2539. switch (this.action) {
  2540. // Move the current image
  2541. case ACTION_MOVE:
  2542. this.move(offsetX, offsetY);
  2543. break;
  2544.  
  2545. // Zoom the current image
  2546. case ACTION_ZOOM:
  2547. this.zoom(getMaxZoomRatio(pointers), false, e);
  2548. break;
  2549.  
  2550. case ACTION_SWITCH:
  2551. this.action = 'switched';
  2552.  
  2553. // Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
  2554. this.pointers = {};
  2555.  
  2556. if (Math.abs(offsetX) > Math.abs(offsetY)) {
  2557. if (offsetX > 1) {
  2558. this.prev(options.loop);
  2559. } else if (offsetX < -1) {
  2560. this.next(options.loop);
  2561. }
  2562. }
  2563.  
  2564. break;
  2565.  
  2566. default:
  2567. }
  2568.  
  2569. // Override
  2570. forEach(pointers, function (p) {
  2571. p.startX = p.endX;
  2572. p.startY = p.endY;
  2573. });
  2574. },
  2575. isSwitchable: function isSwitchable() {
  2576. var imageData = this.imageData,
  2577. viewerData = this.viewerData;
  2578.  
  2579. return this.length > 1 && imageData.left >= 0 && imageData.top >= 0 && imageData.width <= viewerData.width && imageData.height <= viewerData.height;
  2580. }
  2581. };
  2582.  
  2583. var AnotherViewer = WINDOW.Viewer;
  2584.  
  2585. var Viewer = function () {
  2586. /**
  2587. * Create a new Viewer.
  2588. * @param {Element} element - The target element for viewing.
  2589. * @param {Object} [options={}] - The configuration options.
  2590. */
  2591. function Viewer(element) {
  2592. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  2593. classCallCheck(this, Viewer);
  2594.  
  2595. if (!element || element.nodeType !== 1) {
  2596. throw new Error('The first argument is required and must be an element.');
  2597. }
  2598.  
  2599. this.element = element;
  2600. this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
  2601. this.action = false;
  2602. this.fading = false;
  2603. this.fulled = false;
  2604. this.hiding = false;
  2605. this.index = 0;
  2606. this.isImg = false;
  2607. this.length = 0;
  2608. this.played = false;
  2609. this.playing = false;
  2610. this.pointers = {};
  2611. this.ready = false;
  2612. this.showing = false;
  2613. this.timeout = false;
  2614. this.tooltipping = false;
  2615. this.viewed = false;
  2616. this.viewing = false;
  2617. this.isShown = false;
  2618. this.wheeling = false;
  2619. this.init();
  2620. }
  2621.  
  2622. createClass(Viewer, [{
  2623. key: 'init',
  2624. value: function init() {
  2625. var _this = this;
  2626.  
  2627. var element = this.element,
  2628. options = this.options;
  2629.  
  2630. if (getData(element, NAMESPACE)) {
  2631. return;
  2632. }
  2633.  
  2634. setData(element, NAMESPACE, this);
  2635.  
  2636. var isImg = element.tagName.toLowerCase() === 'img';
  2637. var images = [];
  2638.  
  2639. forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
  2640. if (isFunction(options.filter)) {
  2641. if (options.filter.call(_this, image)) {
  2642. images.push(image);
  2643. }
  2644. } else {
  2645. images.push(image);
  2646. }
  2647. });
  2648.  
  2649. if (!images.length) {
  2650. return;
  2651. }
  2652.  
  2653. this.isImg = isImg;
  2654. this.length = images.length;
  2655. this.images = images;
  2656.  
  2657. var ownerDocument = element.ownerDocument;
  2658.  
  2659. var body = ownerDocument.body || ownerDocument.documentElement;
  2660.  
  2661. this.body = body;
  2662. this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
  2663. this.initialBodyPaddingRight = window.getComputedStyle(body).paddingRight;
  2664.  
  2665. // Override `transition` option if it is not supported
  2666. if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
  2667. options.transition = false;
  2668. }
  2669.  
  2670. if (options.inline) {
  2671. var count = 0;
  2672. var progress = function progress() {
  2673. count += 1;
  2674.  
  2675. if (count === _this.length) {
  2676. var timeout = void 0;
  2677.  
  2678. _this.initializing = false;
  2679. _this.delaying = {
  2680. abort: function abort() {
  2681. clearTimeout(timeout);
  2682. }
  2683. };
  2684.  
  2685. // build asynchronously to keep `this.viewer` is accessible in `ready` event handler.
  2686. timeout = setTimeout(function () {
  2687. _this.delaying = false;
  2688. _this.build();
  2689. }, 0);
  2690. }
  2691. };
  2692.  
  2693. this.initializing = {
  2694. abort: function abort() {
  2695. forEach(images, function (image) {
  2696. if (!image.complete) {
  2697. removeListener(image, EVENT_LOAD, progress);
  2698. }
  2699. });
  2700. }
  2701. };
  2702.  
  2703. forEach(images, function (image) {
  2704. if (image.complete) {
  2705. progress();
  2706. } else {
  2707. addListener(image, EVENT_LOAD, progress, {
  2708. once: true
  2709. });
  2710. }
  2711. });
  2712. } else {
  2713. addListener(element, EVENT_CLICK, this.onStart = function (_ref) {
  2714. var target = _ref.target;
  2715.  
  2716. if (target.tagName.toLowerCase() === 'img') {
  2717. _this.view(_this.images.indexOf(target));
  2718. }
  2719. });
  2720. }
  2721. }
  2722. }, {
  2723. key: 'build',
  2724. value: function build() {
  2725. if (this.ready) {
  2726. return;
  2727. }
  2728.  
  2729. var element = this.element,
  2730. options = this.options;
  2731.  
  2732. var parent = element.parentNode;
  2733. var template = document.createElement('div');
  2734.  
  2735. template.innerHTML = TEMPLATE;
  2736.  
  2737. var viewer = template.querySelector('.' + NAMESPACE + '-container');
  2738. var title = viewer.querySelector('.' + NAMESPACE + '-title');
  2739. var toolbar = viewer.querySelector('.' + NAMESPACE + '-toolbar');
  2740. var navbar = viewer.querySelector('.' + NAMESPACE + '-navbar');
  2741. var button = viewer.querySelector('.' + NAMESPACE + '-button');
  2742. var canvas = viewer.querySelector('.' + NAMESPACE + '-canvas');
  2743.  
  2744. this.parent = parent;
  2745. this.viewer = viewer;
  2746. this.title = title;
  2747. this.toolbar = toolbar;
  2748. this.navbar = navbar;
  2749. this.button = button;
  2750. this.canvas = canvas;
  2751. this.footer = viewer.querySelector('.' + NAMESPACE + '-footer');
  2752. this.tooltipBox = viewer.querySelector('.' + NAMESPACE + '-tooltip');
  2753. this.player = viewer.querySelector('.' + NAMESPACE + '-player');
  2754. this.list = viewer.querySelector('.' + NAMESPACE + '-list');
  2755.  
  2756. addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(options.title));
  2757. addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
  2758. toggleClass(button, CLASS_HIDE, !options.button);
  2759.  
  2760. if (options.backdrop) {
  2761. addClass(viewer, NAMESPACE + '-backdrop');
  2762.  
  2763. if (!options.inline && options.backdrop === true) {
  2764. setData(canvas, 'action', 'hide');
  2765. }
  2766. }
  2767.  
  2768. if (options.toolbar) {
  2769. var list = document.createElement('ul');
  2770. var custom = isPlainObject(options.toolbar);
  2771. var zoomButtons = BUTTONS.slice(0, 3);
  2772. var rotateButtons = BUTTONS.slice(7, 9);
  2773. var scaleButtons = BUTTONS.slice(9);
  2774.  
  2775. if (!custom) {
  2776. addClass(toolbar, getResponsiveClass(options.toolbar));
  2777. }
  2778.  
  2779. forEach(custom ? options.toolbar : BUTTONS, function (value, index) {
  2780. var deep = custom && isPlainObject(value);
  2781. var name = custom ? hyphenate(index) : value;
  2782. var show = deep && !isUndefined(value.show) ? value.show : value;
  2783.  
  2784. if (!show || !options.zoomable && zoomButtons.indexOf(name) !== -1 || !options.rotatable && rotateButtons.indexOf(name) !== -1 || !options.scalable && scaleButtons.indexOf(name) !== -1) {
  2785. return;
  2786. }
  2787.  
  2788. var size = deep && !isUndefined(value.size) ? value.size : value;
  2789. var click = deep && !isUndefined(value.click) ? value.click : value;
  2790. var item = document.createElement('li');
  2791.  
  2792. item.setAttribute('role', 'button');
  2793. addClass(item, NAMESPACE + '-' + name);
  2794.  
  2795. if (!isFunction(click)) {
  2796. setData(item, 'action', name);
  2797. }
  2798.  
  2799. if (isNumber(show)) {
  2800. addClass(item, getResponsiveClass(show));
  2801. }
  2802.  
  2803. if (['small', 'large'].indexOf(size) !== -1) {
  2804. addClass(item, NAMESPACE + '-' + size);
  2805. } else if (name === 'play') {
  2806. addClass(item, NAMESPACE + '-large');
  2807. }
  2808.  
  2809. if (isFunction(click)) {
  2810. addListener(item, EVENT_CLICK, click);
  2811. }
  2812.  
  2813. list.appendChild(item);
  2814. });
  2815.  
  2816. toolbar.appendChild(list);
  2817. } else {
  2818. addClass(toolbar, CLASS_HIDE);
  2819. }
  2820.  
  2821. if (!options.rotatable) {
  2822. var rotates = toolbar.querySelectorAll('li[class*="rotate"]');
  2823.  
  2824. addClass(rotates, CLASS_INVISIBLE);
  2825. forEach(rotates, function (rotate) {
  2826. toolbar.appendChild(rotate);
  2827. });
  2828. }
  2829.  
  2830. if (options.inline) {
  2831. addClass(button, CLASS_FULLSCREEN);
  2832. setStyle(viewer, {
  2833. zIndex: options.zIndexInline
  2834. });
  2835.  
  2836. if (window.getComputedStyle(parent).position === 'static') {
  2837. setStyle(parent, {
  2838. position: 'relative'
  2839. });
  2840. }
  2841.  
  2842. parent.insertBefore(viewer, element.nextSibling);
  2843. } else {
  2844. addClass(button, CLASS_CLOSE);
  2845. addClass(viewer, CLASS_FIXED);
  2846. addClass(viewer, CLASS_FADE);
  2847. addClass(viewer, CLASS_HIDE);
  2848.  
  2849. setStyle(viewer, {
  2850. zIndex: options.zIndex
  2851. });
  2852.  
  2853. var container = options.container;
  2854.  
  2855. if (isString(container)) {
  2856. container = element.ownerDocument.querySelector(container);
  2857. }
  2858.  
  2859. if (!container) {
  2860. container = this.body;
  2861. }
  2862.  
  2863. container.appendChild(viewer);
  2864. }
  2865.  
  2866. if (options.inline) {
  2867. this.render();
  2868. this.bind();
  2869. this.isShown = true;
  2870. }
  2871.  
  2872. this.ready = true;
  2873.  
  2874. if (isFunction(options.ready)) {
  2875. addListener(element, EVENT_READY, options.ready, {
  2876. once: true
  2877. });
  2878. }
  2879.  
  2880. if (dispatchEvent(element, EVENT_READY) === false) {
  2881. this.ready = false;
  2882. return;
  2883. }
  2884.  
  2885. if (this.ready && options.inline) {
  2886. this.view();
  2887. }
  2888. }
  2889.  
  2890. /**
  2891. * Get the no conflict viewer class.
  2892. * @returns {Viewer} The viewer class.
  2893. */
  2894.  
  2895. }], [{
  2896. key: 'noConflict',
  2897. value: function noConflict() {
  2898. window.Viewer = AnotherViewer;
  2899. return Viewer;
  2900. }
  2901.  
  2902. /**
  2903. * Change the default options.
  2904. * @param {Object} options - The new default options.
  2905. */
  2906.  
  2907. }, {
  2908. key: 'setDefaults',
  2909. value: function setDefaults(options) {
  2910. assign(DEFAULTS, isPlainObject(options) && options);
  2911. }
  2912. }]);
  2913. return Viewer;
  2914. }();
  2915.  
  2916. assign(Viewer.prototype, render, events, handlers, methods, others);
  2917.  
  2918. if ($.fn) {
  2919. var AnotherViewer$1 = $.fn.viewer;
  2920. var NAMESPACE$1 = 'viewer';
  2921.  
  2922. $.fn.viewer = function jQueryViewer(option) {
  2923. for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  2924. args[_key - 1] = arguments[_key];
  2925. }
  2926.  
  2927. var result = void 0;
  2928.  
  2929. this.each(function (i, element) {
  2930. var $element = $(element);
  2931. var isDestroy = option === 'destroy';
  2932. var viewer = $element.data(NAMESPACE$1);
  2933.  
  2934. if (!viewer) {
  2935. if (isDestroy) {
  2936. return;
  2937. }
  2938.  
  2939. var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);
  2940.  
  2941. viewer = new Viewer(element, options);
  2942. $element.data(NAMESPACE$1, viewer);
  2943. }
  2944.  
  2945. if (typeof option === 'string') {
  2946. var fn = viewer[option];
  2947.  
  2948. if ($.isFunction(fn)) {
  2949. result = fn.apply(viewer, args);
  2950.  
  2951. if (result === viewer) {
  2952. result = undefined;
  2953. }
  2954.  
  2955. if (isDestroy) {
  2956. $element.removeData(NAMESPACE$1);
  2957. }
  2958. }
  2959. }
  2960. });
  2961.  
  2962. return result !== undefined ? result : this;
  2963. };
  2964.  
  2965. $.fn.viewer.Constructor = Viewer;
  2966. $.fn.viewer.setDefaults = Viewer.setDefaults;
  2967. $.fn.viewer.noConflict = function noConflict() {
  2968. $.fn.viewer = AnotherViewer$1;
  2969. return this;
  2970. };
  2971. }
  2972.  
  2973. })));

view.css

  1. /*!
  2. * Viewer v1.0.0
  3. * https://github.com/fengyuanchen/viewer
  4. *
  5. * Copyright (c) 2015-2018 Chen Fengyuan
  6. * Released under the MIT license
  7. *
  8. * Date: 2018-04-01T06:09:55.194Z
  9. */
  10.  
  11. .viewer-zoom-in::before,
  12. .viewer-zoom-out::before,
  13. .viewer-one-to-one::before,
  14. .viewer-reset::before,
  15. .viewer-prev::before,
  16. .viewer-play::before,
  17. .viewer-next::before,
  18. .viewer-rotate-left::before,
  19. .viewer-rotate-right::before,
  20. .viewer-flip-horizontal::before,
  21. .viewer-flip-vertical::before,
  22. .viewer-fullscreen::before,
  23. .viewer-fullscreen-exit::before,
  24. .viewer-close::before {
  25. background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC');
  26. background-repeat: no-repeat;
  27. color: transparent;
  28. display: block;
  29. font-size: 0;
  30. height: 20px;
  31. line-height: 0;
  32. width: 20px;
  33. }
  34.  
  35. .viewer-zoom-in::before {
  36. background-position: 0 0;
  37. content: 'Zoom In';
  38. }
  39.  
  40. .viewer-zoom-out::before {
  41. background-position: -20px 0;
  42. content: 'Zoom Out';
  43. }
  44.  
  45. .viewer-one-to-one::before {
  46. background-position: -40px 0;
  47. content: 'One to One';
  48. }
  49.  
  50. .viewer-reset::before {
  51. background-position: -60px 0;
  52. content: 'Reset';
  53. }
  54.  
  55. .viewer-prev::before {
  56. background-position: -80px 0;
  57. content: 'Previous';
  58. }
  59.  
  60. .viewer-play::before {
  61. background-position: -100px 0;
  62. content: 'Play';
  63. }
  64.  
  65. .viewer-next::before {
  66. background-position: -120px 0;
  67. content: 'Next';
  68. }
  69.  
  70. .viewer-rotate-left::before {
  71. background-position: -140px 0;
  72. content: 'Rotate Left';
  73. }
  74.  
  75. .viewer-rotate-right::before {
  76. background-position: -160px 0;
  77. content: 'Rotate Right';
  78. }
  79.  
  80. .viewer-flip-horizontal::before {
  81. background-position: -180px 0;
  82. content: 'Flip Horizontal';
  83. }
  84.  
  85. .viewer-flip-vertical::before {
  86. background-position: -200px 0;
  87. content: 'Flip Vertical';
  88. }
  89.  
  90. .viewer-fullscreen::before {
  91. background-position: -220px 0;
  92. content: 'Enter Full Screen';
  93. }
  94.  
  95. .viewer-fullscreen-exit::before {
  96. background-position: -240px 0;
  97. content: 'Exit Full Screen';
  98. }
  99.  
  100. .viewer-close::before {
  101. background-position: -260px 0;
  102. content: 'Close';
  103. }
  104.  
  105. .viewer-container {
  106. bottom: 0;
  107. direction: ltr;
  108. font-size: 0;
  109. left: 0;
  110. line-height: 0;
  111. overflow: hidden;
  112. position: absolute;
  113. right: 0;
  114. -webkit-tap-highlight-color: transparent;
  115. top: 0;
  116. -webkit-touch-callout: none;
  117. -ms-touch-action: none;
  118. touch-action: none;
  119. -webkit-user-select: none;
  120. -moz-user-select: none;
  121. -ms-user-select: none;
  122. user-select: none;
  123. }
  124.  
  125. .viewer-container::-moz-selection,
  126. .viewer-container *::-moz-selection {
  127. background-color: transparent;
  128. }
  129.  
  130. .viewer-container::selection,
  131. .viewer-container *::selection {
  132. background-color: transparent;
  133. }
  134.  
  135. .viewer-container img {
  136. display: block;
  137. height: auto;
  138. max-height: none !important;
  139. max-width: none !important;
  140. min-height: 0 !important;
  141. min-width: 0 !important;
  142. width: 100%;
  143. }
  144.  
  145. .viewer-canvas {
  146. bottom: 0;
  147. left: 0;
  148. overflow: hidden;
  149. position: absolute;
  150. right: 0;
  151. top: 0;
  152. }
  153.  
  154. .viewer-canvas > img {
  155. height: auto;
  156. margin: 15px auto;
  157. max-width: 90% !important;
  158. width: auto;
  159. }
  160.  
  161. .viewer-footer {
  162. bottom: 0;
  163. left: 0;
  164. overflow: hidden;
  165. position: absolute;
  166. right: 0;
  167. text-align: center;
  168. }
  169.  
  170. .viewer-navbar {
  171. background-color: rgba(0, 0, 0, .5);
  172. overflow: hidden;
  173. }
  174.  
  175. .viewer-list {
  176. -webkit-box-sizing: content-box;
  177. box-sizing: content-box;
  178. height: 50px;
  179. margin: 0;
  180. overflow: hidden;
  181. padding: 1px 0;
  182. }
  183.  
  184. .viewer-list > li {
  185. color: transparent;
  186. cursor: pointer;
  187. float: left;
  188. font-size: 0;
  189. height: 50px;
  190. line-height: 0;
  191. opacity: .5;
  192. overflow: hidden;
  193. -webkit-transition: opacity .15s;
  194. transition: opacity .15s;
  195. width: 30px;
  196. }
  197.  
  198. .viewer-list > li:hover {
  199. opacity: .75;
  200. }
  201.  
  202. .viewer-list > li + li {
  203. margin-left: 1px;
  204. }
  205.  
  206. .viewer-list > .viewer-loading {
  207. position: relative;
  208. }
  209.  
  210. .viewer-list > .viewer-loading::after {
  211. border-width: 2px;
  212. height: 20px;
  213. margin-left: -10px;
  214. margin-top: -10px;
  215. width: 20px;
  216. }
  217.  
  218. .viewer-list > .viewer-active,
  219. .viewer-list > .viewer-active:hover {
  220. opacity: 1;
  221. }
  222.  
  223. .viewer-player {
  224. background-color: #000;
  225. bottom: 0;
  226. cursor: none;
  227. display: none;
  228. left: 0;
  229. position: absolute;
  230. right: 0;
  231. top: 0;
  232. }
  233.  
  234. .viewer-player > img {
  235. left: 0;
  236. position: absolute;
  237. top: 0;
  238. }
  239.  
  240. .viewer-toolbar ul {
  241. display: inline-block;
  242. margin: 0 auto 5px;
  243. overflow: hidden;
  244. padding: 3px 0;
  245. }
  246.  
  247. .viewer-toolbar li {
  248. background-color: rgba(0, 0, 0, .5);
  249. border-radius: 50%;
  250. cursor: pointer;
  251. float: left;
  252. height: 24px;
  253. overflow: hidden;
  254. -webkit-transition: background-color .15s;
  255. transition: background-color .15s;
  256. width: 24px;
  257. }
  258.  
  259. .viewer-toolbar li:hover {
  260. background-color: rgba(0, 0, 0, .8);
  261. }
  262.  
  263. .viewer-toolbar li::before {
  264. margin: 2px;
  265. }
  266.  
  267. .viewer-toolbar li + li {
  268. margin-left: 1px;
  269. }
  270.  
  271. .viewer-toolbar .viewer-small {
  272. height: 18px;
  273. margin-bottom: 3px;
  274. margin-top: 3px;
  275. width: 18px;
  276. }
  277.  
  278. .viewer-toolbar .viewer-small::before {
  279. margin: -1px;
  280. }
  281.  
  282. .viewer-toolbar .viewer-large {
  283. height: 30px;
  284. margin-bottom: -3px;
  285. margin-top: -3px;
  286. width: 30px;
  287. }
  288.  
  289. .viewer-toolbar .viewer-large::before {
  290. margin: 5px;
  291. }
  292.  
  293. .viewer-tooltip {
  294. background-color: rgba(0, 0, 0, .8);
  295. border-radius: 10px;
  296. color: #fff;
  297. display: none;
  298. font-size: 12px;
  299. height: 20px;
  300. left: 50%;
  301. line-height: 20px;
  302. margin-left: -25px;
  303. margin-top: -10px;
  304. position: absolute;
  305. text-align: center;
  306. top: 50%;
  307. width: 50px;
  308. }
  309.  
  310. .viewer-title {
  311. color: #ccc;
  312. display: inline-block;
  313. font-size: 12px;
  314. line-height: 1;
  315. margin: 0 5% 5px;
  316. max-width: 90%;
  317. opacity: .8;
  318. overflow: hidden;
  319. text-overflow: ellipsis;
  320. -webkit-transition: opacity .15s;
  321. transition: opacity .15s;
  322. white-space: nowrap;
  323. }
  324.  
  325. .viewer-title:hover {
  326. opacity: 1;
  327. }
  328.  
  329. .viewer-button {
  330. background-color: rgba(0, 0, 0, .5);
  331. border-radius: 50%;
  332. cursor: pointer;
  333. height: 80px;
  334. overflow: hidden;
  335. position: absolute;
  336. right: -40px;
  337. top: -40px;
  338. -webkit-transition: background-color .15s;
  339. transition: background-color .15s;
  340. width: 80px;
  341. }
  342.  
  343. .viewer-button:focus,
  344. .viewer-button:hover {
  345. background-color: rgba(0, 0, 0, .8);
  346. }
  347.  
  348. .viewer-button::before {
  349. bottom: 15px;
  350. left: 15px;
  351. position: absolute;
  352. }
  353.  
  354. .viewer-fixed {
  355. position: fixed;
  356. }
  357.  
  358. .viewer-open {
  359. overflow: hidden;
  360. }
  361.  
  362. .viewer-show {
  363. display: block;
  364. }
  365.  
  366. .viewer-hide {
  367. display: none;
  368. }
  369.  
  370. .viewer-backdrop {
  371. background-color: rgba(0, 0, 0, .5);
  372. }
  373.  
  374. .viewer-invisible {
  375. visibility: hidden;
  376. }
  377.  
  378. .viewer-move {
  379. cursor: move;
  380. cursor: -webkit-grab;
  381. cursor: grab;
  382. }
  383.  
  384. .viewer-fade {
  385. opacity: 0;
  386. }
  387.  
  388. .viewer-in {
  389. opacity: 1;
  390. }
  391.  
  392. .viewer-transition {
  393. -webkit-transition: all .3s;
  394. transition: all .3s;
  395. }
  396.  
  397. @-webkit-keyframes viewer-spinner {
  398. 0% {
  399. -webkit-transform: rotate(0deg);
  400. transform: rotate(0deg);
  401. }
  402.  
  403. 100% {
  404. -webkit-transform: rotate(360deg);
  405. transform: rotate(360deg);
  406. }
  407. }
  408.  
  409. @keyframes viewer-spinner {
  410. 0% {
  411. -webkit-transform: rotate(0deg);
  412. transform: rotate(0deg);
  413. }
  414.  
  415. 100% {
  416. -webkit-transform: rotate(360deg);
  417. transform: rotate(360deg);
  418. }
  419. }
  420.  
  421. .viewer-loading::after {
  422. -webkit-animation: viewer-spinner 1s linear infinite;
  423. animation: viewer-spinner 1s linear infinite;
  424. border: 4px solid rgba(255, 255, 255, .1);
  425. border-left-color: rgba(255, 255, 255, .5);
  426. border-radius: 50%;
  427. content: '';
  428. display: inline-block;
  429. height: 40px;
  430. left: 50%;
  431. margin-left: -20px;
  432. margin-top: -20px;
  433. position: absolute;
  434. top: 50%;
  435. width: 40px;
  436. z-index: 1;
  437. }
  438.  
  439. @media (max-width: 767px) {
  440. .viewer-hide-xs-down {
  441. display: none;
  442. }
  443. }
  444.  
  445. @media (max-width: 991px) {
  446. .viewer-hide-sm-down {
  447. display: none;
  448. }
  449. }
  450.  
  451. @media (max-width: 1199px) {
  452. .viewer-hide-md-down {
  453. display: none;
  454. }
  455. }

图片查看器——viewer.js的更多相关文章

  1. 强大的jQuery图片查看器插件Viewer.js

    简介 Viewer.js 是一款强大的图片查看器 Viewer.js 有以下特点: 支持移动设备触摸事件 支持响应式 支持放大/缩小 支持旋转(类似微博的图片旋转) 支持水平/垂直翻转 支持图片移动 ...

  2. Viewer.js – 强大的JS/jQuery图片查看器

    简介 Viewer.js 是一款强大的图片查看器,像门户网站一般都会有各自的图片查看器,如果您正需要一款强大的图片查看器,也许 Viewer.js 是一个很好的选择.Viewer.js 有以下特点: ...

  3. js手写图片查看器(图片的缩放、旋转、拖拽)

    在做一次代码编辑任务中,要查看图片器.在时间允许的条件下,放弃了已经封装好的图片jq插件,现在自己手写js实现图片的缩放.旋转.推拽功能! 具体代码如下: <!DOCTYPE html> ...

  4. js实现图片查看器(图片的缩放、旋转、拖拽)

    一.关于图片查看器. 目前网络上能找到的图片查看器很多,谁便一搜就能出来.如:jquery.iviewer.js.Viewer.js这两个js文件,其中功能也足够满足大部分开发需求.但是单纯的就想实现 ...

  5. 网页中的图片查看器viewjs使用

    需求分析: 对于网页中的图片进行连续放大(便于用户清晰查看内容).缩小,旋转等操作,可以使用viewjs图片查看器插件实现. viewjs官方网址:https://github.com/fengyua ...

  6. jQuery功能强大的图片查看器插件

    简要教程 viewer是一款功能强大的图片查看器jQuery插件.它可以实现ACDsee等看图软件的部分功能.它可以对图片进行移动,缩放,旋转,翻转,可以前后浏览一组图片.该图片查看器还支持移动设备, ...

  7. 用JQ仿造礼德财富网的图片查看器

    现在就职于一家P2P平台,自然也会关注同行其它网站的前端技术,今天要仿造的是礼德内页的一个图片查看器效果.不过说白了,无论人人贷也好礼德财富也好,很多地方的前端都做的不尽如人意,比如忽略细节.缺乏交互 ...

  8. 基于jQuery的一个简单的图片查看器

    项目中自己diy了一个图片查看器.因为初始代码不是自己的,只是在上面改了一下也没有弄的很漂亮.等以后有时间了在重写一下样式和封装,作为备用的只是积累吧.如果有童鞋有用到,完全可以在此基础上改,比较容易 ...

  9. angularjs1 自定义图片查看器(可旋转、放大、缩小、拖拽)

    笔记: angularjs1 制作自定义图片查看器(可旋转.放大.缩小.拖拽) 2018-01-12 更新  可以在我的博客  查看我 已经封装好的 纯 js写的图片查看器插件    博客链接 懒得把 ...

随机推荐

  1. mybatis插件机制原理

    mybatis插件机制及分页插件原理 参考链接:mybatis插件机制及分页插件原理 如何编写一个自定义mybatis插件 参考链接:mybatis 自定义插件的使用

  2. Django获取请求的IP地址

    if request.META.get('HTTP_X_FORWARDED_FOR'): ip = request.META.get("HTTP_X_FORWARDED_FOR") ...

  3. http和https到底区别在哪

    一.Http和Https的基本概念 Http:超文本传输协议(Http,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议.设计Http最初的目的是为了提供一 ...

  4. stm32 中库函数、结构体、地址的强制类型转换、相应特殊功能寄存器之间的关系

    以一个挂接在APB2上的外设函数使能为例 A : RCC_APB2PeriphClockCmd():时钟使能函数 1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFI ...

  5. 【每日日报】第十八天 ----java最全排序方法

    1 今天看了Java的第三章 2 冒泡法排序: package Line; import java.util.Arrays; public class MaoPao { public static v ...

  6. msmpeng.exe阻止移动硬盘弹出

    MsMpEng.exe 占用 该进程是微软反恶意软件服务的一个可执行文件,用户无法手动停止该进程. 首先运行   eventvwr.msc打开事件查看器,找到警告信息,查看是什么进程在阻止硬盘弹出. ...

  7. java中的排序除了冒泡以来, 再给出一种方法, 举例说明

    9.5 排序:   有一种排序的方法,非常好理解,详见本题的步骤,先找出最大值和最小值,把最小值打印出来后,把它存在另一个数组b当中,再删除此最小值,之后再来一次找出最小值,打印出最小值以后,再把它存 ...

  8. 接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理)

    接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理) 1.抽象类 以下说辞可能不太准确,但是会让你醍醐灌顶 抽象类是把一些具有共同属性(包括行为)的东西抽象出来,比如: 小狗有身高,体重,颜 ...

  9. kafka快速入门到精通

    目录 1. 消息队列两种模式 1.1 消息队列作用 1.2 点对点模式(一对一,消费者主动拉取数据,消息收到后消息删除) 1.3 发布/订阅模式(一对多,消费数据之后不会删除消息) 1.4 kafka ...

  10. c++对c的拓展_指针的引用

    套用引用公式:Type & ref =val; 假设:type 类型为int * 由公式得 int * & ref = val; // int * *const ref=&va ...