| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- import {
- ACTION_MOVE,
- ACTION_SWITCH,
- ACTION_ZOOM,
- CLASS_INVISIBLE,
- CLASS_LOADING,
- CLASS_MOVE,
- CLASS_TRANSITION,
- DATA_ACTION,
- EVENT_CLICK,
- EVENT_DBLCLICK,
- EVENT_LOAD,
- EVENT_VIEWED,
- IS_TOUCH_DEVICE,
- } from './constants';
- import {
- addClass,
- addListener,
- assign,
- dispatchEvent,
- forEach,
- getData,
- getImageNaturalSizes,
- getPointer,
- getTransforms,
- isFunction,
- isNumber,
- removeClass,
- setStyle,
- toggleClass,
- } from './utilities';
- export default {
- click(event) {
- const { target } = event;
- const { options, imageData } = this;
- const action = getData(target, DATA_ACTION);
- // Cancel the emulated click when the native click event was triggered.
- if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) {
- clearTimeout(this.clickCanvasTimeout);
- }
- switch (action) {
- case 'mix':
- if (this.played) {
- this.stop();
- } else if (options.inline) {
- if (this.fulled) {
- this.exit();
- } else {
- this.full();
- }
- } else {
- this.hide();
- }
- break;
- case 'hide':
- this.hide();
- break;
- case 'view':
- this.view(getData(target, 'index'));
- break;
- case 'zoom-in':
- this.zoom(0.1, true);
- break;
- case 'zoom-out':
- this.zoom(-0.1, true);
- break;
- case 'one-to-one':
- this.toggle();
- break;
- case 'reset':
- this.reset();
- break;
- case 'prev':
- this.prev(options.loop);
- break;
- case 'play':
- this.play(options.fullscreen);
- break;
- case 'next':
- this.next(options.loop);
- break;
- case 'rotate-left':
- this.rotate(-90);
- break;
- case 'rotate-right':
- this.rotate(90);
- break;
- case 'flip-horizontal':
- this.scaleX(-imageData.scaleX || -1);
- break;
- case 'flip-vertical':
- this.scaleY(-imageData.scaleY || -1);
- break;
- default:
- if (this.played) {
- this.stop();
- }
- }
- },
- dblclick(event) {
- event.preventDefault();
- if (this.viewed && event.target === this.image) {
- // Cancel the emulated double click when the native dblclick event was triggered.
- if (IS_TOUCH_DEVICE && event.isTrusted) {
- clearTimeout(this.doubleClickImageTimeout);
- }
- this.toggle();
- }
- },
- load() {
- if (this.timeout) {
- clearTimeout(this.timeout);
- this.timeout = false;
- }
- const {
- element,
- options,
- image,
- index,
- viewerData,
- } = this;
- removeClass(image, CLASS_INVISIBLE);
- if (options.loading) {
- removeClass(this.canvas, CLASS_LOADING);
- }
- 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;'
- );
- this.initImage(() => {
- toggleClass(image, CLASS_MOVE, options.movable);
- toggleClass(image, CLASS_TRANSITION, options.transition);
- this.renderImage(() => {
- this.viewed = true;
- this.viewing = false;
- if (isFunction(options.viewed)) {
- addListener(element, EVENT_VIEWED, options.viewed, {
- once: true,
- });
- }
- dispatchEvent(element, EVENT_VIEWED, {
- originalImage: this.images[index],
- index,
- image,
- });
- });
- });
- },
- loadImage(event) {
- const image = event.target;
- const parent = image.parentNode;
- const parentWidth = parent.offsetWidth || 30;
- const parentHeight = parent.offsetHeight || 50;
- const filled = !!getData(image, 'filled');
- getImageNaturalSizes(image, this.options, (naturalWidth, naturalHeight) => {
- const aspectRatio = naturalWidth / naturalHeight;
- let width = parentWidth;
- let height = parentHeight;
- if (parentHeight * aspectRatio > parentWidth) {
- if (filled) {
- width = parentHeight * aspectRatio;
- } else {
- height = parentWidth / aspectRatio;
- }
- } else if (filled) {
- height = parentWidth / aspectRatio;
- } else {
- width = parentHeight * aspectRatio;
- }
- setStyle(image, assign({
- width,
- height,
- }, getTransforms({
- translateX: (parentWidth - width) / 2,
- translateY: (parentHeight - height) / 2,
- })));
- });
- },
- keydown(event) {
- const { options } = this;
- if (!this.fulled || !options.keyboard) {
- return;
- }
- switch (event.keyCode || event.which || event.charCode) {
- // Escape
- case 27:
- if (this.played) {
- this.stop();
- } else if (options.inline) {
- if (this.fulled) {
- this.exit();
- }
- } else {
- this.hide();
- }
- break;
- // Space
- case 32:
- if (this.played) {
- this.stop();
- }
- break;
- // ArrowLeft
- case 37:
- this.prev(options.loop);
- break;
- // ArrowUp
- case 38:
- // Prevent scroll on Firefox
- event.preventDefault();
- // Zoom in
- this.zoom(options.zoomRatio, true);
- break;
- // ArrowRight
- case 39:
- this.next(options.loop);
- break;
- // ArrowDown
- case 40:
- // Prevent scroll on Firefox
- event.preventDefault();
- // Zoom out
- this.zoom(-options.zoomRatio, true);
- break;
- // Ctrl + 0
- case 48:
- // Fall through
- // Ctrl + 1
- // eslint-disable-next-line no-fallthrough
- case 49:
- if (event.ctrlKey) {
- event.preventDefault();
- this.toggle();
- }
- break;
- default:
- }
- },
- dragstart(event) {
- if (event.target.tagName.toLowerCase() === 'img') {
- event.preventDefault();
- }
- },
- pointerdown(event) {
- const { options, pointers } = this;
- const { buttons, button } = event;
- if (
- !this.viewed
- || this.showing
- || this.viewing
- || this.hiding
- // Handle mouse event and pointer event and ignore touch event
- || ((
- event.type === 'mousedown'
- || (event.type === 'pointerdown' && event.pointerType === 'mouse')
- ) && (
- // No primary button (Usually the left button)
- (isNumber(buttons) && buttons !== 1)
- || (isNumber(button) && button !== 0)
- // Open context menu
- || event.ctrlKey
- ))
- ) {
- return;
- }
- // Prevent default behaviours as page zooming in touch devices.
- event.preventDefault();
- if (event.changedTouches) {
- forEach(event.changedTouches, (touch) => {
- pointers[touch.identifier] = getPointer(touch);
- });
- } else {
- pointers[event.pointerId || 0] = getPointer(event);
- }
- let action = options.movable ? ACTION_MOVE : false;
- if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
- action = ACTION_ZOOM;
- } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
- action = ACTION_SWITCH;
- }
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
- removeClass(this.image, CLASS_TRANSITION);
- }
- this.action = action;
- },
- pointermove(event) {
- const { pointers, action } = this;
- if (!this.viewed || !action) {
- return;
- }
- event.preventDefault();
- if (event.changedTouches) {
- forEach(event.changedTouches, (touch) => {
- assign(pointers[touch.identifier] || {}, getPointer(touch, true));
- });
- } else {
- assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
- }
- this.change(event);
- },
- pointerup(event) {
- const { options, action, pointers } = this;
- let pointer;
- if (event.changedTouches) {
- forEach(event.changedTouches, (touch) => {
- pointer = pointers[touch.identifier];
- delete pointers[touch.identifier];
- });
- } else {
- pointer = pointers[event.pointerId || 0];
- delete pointers[event.pointerId || 0];
- }
- if (!action) {
- return;
- }
- event.preventDefault();
- if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
- addClass(this.image, CLASS_TRANSITION);
- }
- this.action = false;
- // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
- if (
- IS_TOUCH_DEVICE
- && action !== ACTION_ZOOM
- && pointer
- && (Date.now() - pointer.timeStamp < 500)
- ) {
- clearTimeout(this.clickCanvasTimeout);
- clearTimeout(this.doubleClickImageTimeout);
- if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
- if (this.imageClicked) {
- this.imageClicked = false;
- // This timeout will be cleared later when a native dblclick event is triggering
- this.doubleClickImageTimeout = setTimeout(() => {
- dispatchEvent(this.image, EVENT_DBLCLICK);
- }, 50);
- } else {
- this.imageClicked = true;
- // The default timing of a double click in Windows is 500 ms
- this.doubleClickImageTimeout = setTimeout(() => {
- this.imageClicked = false;
- }, 500);
- }
- } else {
- this.imageClicked = false;
- if (options.backdrop && options.backdrop !== 'static' && event.target === this.canvas) {
- // This timeout will be cleared later when a native click event is triggering
- this.clickCanvasTimeout = setTimeout(() => {
- dispatchEvent(this.canvas, EVENT_CLICK);
- }, 50);
- }
- }
- }
- },
- resize() {
- if (!this.isShown || this.hiding) {
- return;
- }
- if (this.fulled) {
- this.close();
- this.initBody();
- this.open();
- }
- this.initContainer();
- this.initViewer();
- this.renderViewer();
- this.renderList();
- if (this.viewed) {
- this.initImage(() => {
- this.renderImage();
- });
- }
- if (this.played) {
- if (this.options.fullscreen && this.fulled && !(
- document.fullscreenElement
- || document.webkitFullscreenElement
- || document.mozFullScreenElement
- || document.msFullscreenElement
- )) {
- this.stop();
- return;
- }
- forEach(this.player.getElementsByTagName('img'), (image) => {
- addListener(image, EVENT_LOAD, this.loadImage.bind(this), {
- once: true,
- });
- dispatchEvent(image, EVENT_LOAD);
- });
- }
- },
- wheel(event) {
- if (!this.viewed) {
- return;
- }
- event.preventDefault();
- // Limit wheel speed to prevent zoom too fast
- if (this.wheeling) {
- return;
- }
- this.wheeling = true;
- setTimeout(() => {
- this.wheeling = false;
- }, 50);
- const ratio = Number(this.options.zoomRatio) || 0.1;
- let delta = 1;
- if (event.deltaY) {
- delta = event.deltaY > 0 ? 1 : -1;
- } else if (event.wheelDelta) {
- delta = -event.wheelDelta / 120;
- } else if (event.detail) {
- delta = event.detail > 0 ? 1 : -1;
- }
- this.zoom(-delta * ratio, true, event);
- },
- };
|