Media viewer: added zooming
This commit is contained in:
parent
1b4c51345c
commit
ab6a7e1725
@ -51,6 +51,13 @@ import setInnerHTML from "../helpers/dom/setInnerHTML";
|
||||
import { doubleRaf, fastRaf } from "../helpers/schedulers";
|
||||
import { attachClickEvent } from "../helpers/dom/clickEvent";
|
||||
import PopupDeleteMessages from "./popups/deleteMessages";
|
||||
import RangeSelector from "./rangeSelector";
|
||||
import attachGrabListeners, { GrabEvent } from "../helpers/dom/attachGrabListeners";
|
||||
|
||||
const ZOOM_STEP = 0.5;
|
||||
const ZOOM_INITIAL_VALUE = 1;
|
||||
const ZOOM_MIN_VALUE = 1;
|
||||
const ZOOM_MAX_VALUE = 4;
|
||||
|
||||
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
|
||||
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
|
||||
@ -63,8 +70,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
protected overlaysDiv: HTMLElement;
|
||||
protected author: {[k in 'container' | 'avatarEl' | 'nameEl' | 'date']: HTMLElement} = {} as any;
|
||||
protected content: {[k in 'main' | 'container' | 'media' | 'mover' | ContentAdditionType]: HTMLElement} = {} as any;
|
||||
protected buttons: {[k in 'download' | 'close' | 'prev' | 'next' | 'mobile-close' | 'zoomin' | ButtonsAdditionType]: HTMLElement} = {} as any;
|
||||
protected buttons: {[k in 'download' | 'close' | 'prev' | 'next' | 'mobile-close' | 'zoom' | ButtonsAdditionType]: HTMLElement} = {} as any;
|
||||
protected topbar: HTMLElement;
|
||||
protected moversContainer: HTMLElement;
|
||||
|
||||
protected tempId = 0;
|
||||
protected preloader: ProgressivePreloader = null;
|
||||
@ -103,6 +111,21 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
protected videoPlayer: VideoPlayer;
|
||||
|
||||
protected zoomElements: {
|
||||
container: HTMLElement,
|
||||
btnOut: HTMLElement,
|
||||
btnIn: HTMLElement,
|
||||
rangeSelector: RangeSelector
|
||||
} = {} as any;
|
||||
// protected zoomValue = ZOOM_INITIAL_VALUE;
|
||||
protected zoomSwipeHandler: SwipeHandler;
|
||||
protected zoomSwipeStartX = 0;
|
||||
protected zoomSwipeStartY = 0;
|
||||
protected zoomSwipeX = 0;
|
||||
protected zoomSwipeY = 0;
|
||||
|
||||
protected ctrlKeyDown: boolean;
|
||||
|
||||
constructor(topButtons: Array<keyof AppMediaViewerBase<ContentAdditionType, ButtonsAdditionType, TargetType>['buttons']>) {
|
||||
this.log = logger('AMV');
|
||||
this.preloader = new ProgressivePreloader();
|
||||
@ -153,12 +176,34 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
const buttonsDiv = document.createElement('div');
|
||||
buttonsDiv.classList.add(MEDIA_VIEWER_CLASSNAME + '-buttons');
|
||||
|
||||
topButtons.concat(['download', 'zoomin', 'close']).forEach(name => {
|
||||
const button = ButtonIcon(name, {noRipple: name === 'close' || undefined});
|
||||
topButtons.concat(['download', 'zoom', 'close']).forEach(name => {
|
||||
const button = ButtonIcon(name, {noRipple: true});
|
||||
this.buttons[name] = button;
|
||||
buttonsDiv.append(button);
|
||||
});
|
||||
|
||||
this.buttons.zoom.classList.add('zoom-in');
|
||||
|
||||
// * zoom
|
||||
this.zoomElements.container = document.createElement('div');
|
||||
this.zoomElements.container.classList.add('zoom-container');
|
||||
|
||||
this.zoomElements.btnOut = ButtonIcon('zoomout', {noRipple: true});
|
||||
this.zoomElements.btnOut.addEventListener('click', () => this.changeZoom(false));
|
||||
this.zoomElements.btnIn = ButtonIcon('zoomin', {noRipple: true});
|
||||
this.zoomElements.btnIn.addEventListener('click', () => this.changeZoom(true));
|
||||
|
||||
this.zoomElements.rangeSelector = new RangeSelector(ZOOM_STEP, ZOOM_INITIAL_VALUE + ZOOM_STEP, ZOOM_MIN_VALUE, ZOOM_MAX_VALUE, true);
|
||||
this.zoomElements.rangeSelector.setListeners();
|
||||
this.zoomElements.rangeSelector.setHandlers({
|
||||
onScrub: this.setZoomValue,
|
||||
onMouseUp: () => this.setZoomValue()
|
||||
});
|
||||
|
||||
this.zoomElements.container.append(this.zoomElements.btnOut, this.zoomElements.rangeSelector.container, this.zoomElements.btnIn);
|
||||
|
||||
this.wholeDiv.append(this.zoomElements.container);
|
||||
|
||||
// * content
|
||||
this.content.main = document.createElement('div');
|
||||
this.content.main.classList.add(MEDIA_VIEWER_CLASSNAME + '-content');
|
||||
@ -187,7 +232,17 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
this.buttons.next.className = `${MEDIA_VIEWER_CLASSNAME}-switcher ${MEDIA_VIEWER_CLASSNAME}-switcher-right`;
|
||||
this.buttons.next.innerHTML = `<span class="tgico-down ${MEDIA_VIEWER_CLASSNAME}-next-button"></span>`;
|
||||
|
||||
this.wholeDiv.append(this.overlaysDiv, this.buttons.prev, this.buttons.next, this.topbar);
|
||||
this.moversContainer = document.createElement('div');
|
||||
this.moversContainer.classList.add(MEDIA_VIEWER_CLASSNAME + '-movers');
|
||||
this.moversContainer.addEventListener('wheel', (e) => {
|
||||
if(this.ctrlKeyDown) {
|
||||
cancelEvent(e);
|
||||
const scrollingUp = e.deltaY < 0;
|
||||
this.changeZoom(!!scrollingUp);
|
||||
}
|
||||
});
|
||||
|
||||
this.wholeDiv.append(this.overlaysDiv, this.buttons.prev, this.buttons.next, this.topbar, this.moversContainer);
|
||||
|
||||
// * constructing html end
|
||||
|
||||
@ -224,6 +279,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
}
|
||||
});
|
||||
|
||||
this.buttons.zoom.addEventListener('click', () => this.toggleZoom());
|
||||
|
||||
this.wholeDiv.addEventListener('click', this.onClick);
|
||||
|
||||
if(isTouchSupported) {
|
||||
@ -268,6 +325,80 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
}
|
||||
}
|
||||
|
||||
protected toggleZoom(enable?: boolean) {
|
||||
const isVisible = this.isZooming();
|
||||
if(this.zoomElements.rangeSelector.mousedown || this.ctrlKeyDown) {
|
||||
enable = true;
|
||||
}
|
||||
|
||||
if(isVisible === enable) return;
|
||||
|
||||
if(enable === undefined) {
|
||||
enable = !isVisible;
|
||||
}
|
||||
|
||||
this.buttons.zoom.classList.toggle('zoom-in', !enable);
|
||||
this.zoomElements.container.classList.toggle('is-visible', enable);
|
||||
const zoomValue = enable ? ZOOM_INITIAL_VALUE + ZOOM_STEP : 1;
|
||||
this.setZoomValue(zoomValue);
|
||||
|
||||
if(enable) {
|
||||
if(!this.zoomSwipeHandler) {
|
||||
let lastDiffX: number, lastDiffY: number;
|
||||
const multiplier = -1;
|
||||
this.zoomSwipeHandler = new SwipeHandler({
|
||||
element: this.moversContainer,
|
||||
onFirstSwipe: () => {
|
||||
lastDiffX = lastDiffY = 0;
|
||||
this.moversContainer.classList.add('no-transition');
|
||||
},
|
||||
onSwipe: (xDiff, yDiff) => {
|
||||
[xDiff, yDiff] = [xDiff * multiplier, yDiff * multiplier];
|
||||
this.zoomSwipeX += xDiff - lastDiffX;
|
||||
this.zoomSwipeY += yDiff - lastDiffY;
|
||||
[lastDiffX, lastDiffY] = [xDiff, yDiff];
|
||||
|
||||
this.setZoomValue();
|
||||
},
|
||||
onReset: () => {
|
||||
this.moversContainer.classList.remove('no-transition');
|
||||
},
|
||||
cursor: 'move'
|
||||
});
|
||||
} else {
|
||||
this.zoomSwipeHandler.setListeners();
|
||||
}
|
||||
|
||||
this.zoomElements.rangeSelector.setProgress(zoomValue);
|
||||
} else if(!enable) {
|
||||
this.zoomSwipeHandler.removeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
protected changeZoom(add: boolean) {
|
||||
this.zoomElements.rangeSelector.addProgress(ZOOM_STEP * (add ? 1 : -1));
|
||||
this.setZoomValue();
|
||||
}
|
||||
|
||||
protected setZoomValue = (value = this.zoomElements.rangeSelector.value) => {
|
||||
// this.zoomValue = value;
|
||||
if(value === ZOOM_INITIAL_VALUE) {
|
||||
this.zoomSwipeX = 0;
|
||||
this.zoomSwipeY = 0;
|
||||
}
|
||||
|
||||
this.moversContainer.style.transform = `matrix(${value}, 0, 0, ${value}, ${this.zoomSwipeX}, ${this.zoomSwipeY})`;
|
||||
|
||||
this.zoomElements.btnOut.classList.toggle('inactive', value === ZOOM_MIN_VALUE);
|
||||
this.zoomElements.btnIn.classList.toggle('inactive', value === ZOOM_MAX_VALUE);
|
||||
|
||||
this.toggleZoom(value > ZOOM_INITIAL_VALUE);
|
||||
};
|
||||
|
||||
protected isZooming() {
|
||||
return this.zoomElements.container.classList.contains('is-visible');
|
||||
}
|
||||
|
||||
protected setBtnMenuToggle(buttons: ButtonMenuItemOptions[]) {
|
||||
const btnMenuToggle = ButtonMenuToggle({onlyMobile: true}, 'bottom-left', buttons);
|
||||
this.topbar.append(btnMenuToggle);
|
||||
@ -294,6 +425,11 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
this.setMoverPromise = null;
|
||||
this.tempId = -1;
|
||||
|
||||
if(this.zoomSwipeHandler) {
|
||||
this.zoomSwipeHandler.removeListeners();
|
||||
this.zoomSwipeHandler = undefined;
|
||||
}
|
||||
|
||||
/* if(appSidebarRight.historyTabIDs.slice(-1)[0] === AppSidebarRight.SLIDERITEMSIDS.forward) {
|
||||
promise.then(() => {
|
||||
appSidebarRight.forwardTab.closeBtn.click();
|
||||
@ -301,6 +437,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
} */
|
||||
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
window.removeEventListener('keyup', this.onKeyUp);
|
||||
|
||||
promise.finally(() => {
|
||||
this.wholeDiv.remove();
|
||||
@ -333,26 +470,56 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
return;
|
||||
}
|
||||
|
||||
const isZooming = this.isZooming();
|
||||
let mover: HTMLElement = null;
|
||||
['media-viewer-mover', 'media-viewer-buttons', 'media-viewer-author', 'media-viewer-caption'].find(s => {
|
||||
const classNames = ['ckin__player', 'media-viewer-buttons', 'media-viewer-author', 'media-viewer-caption', 'zoom-container'];
|
||||
if(isZooming) {
|
||||
classNames.push('media-viewer-movers');
|
||||
}
|
||||
|
||||
classNames.find(s => {
|
||||
try {
|
||||
mover = findUpClassName(target, s);
|
||||
if(mover) return true;
|
||||
} catch(err) {return false;}
|
||||
});
|
||||
|
||||
if(/* target === this.mediaViewerDiv */!mover || target.tagName === 'IMG' || target.tagName === 'image') {
|
||||
if(/* target === this.mediaViewerDiv */!mover || (!isZooming && (target.tagName === 'IMG' || target.tagName === 'image'))) {
|
||||
this.buttons.close.click();
|
||||
}
|
||||
};
|
||||
|
||||
private onKeyDown = (e: KeyboardEvent) => {
|
||||
//this.log('onKeyDown', e);
|
||||
if(rootScope.overlaysActive > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let good = true;
|
||||
if(e.key === 'ArrowRight') {
|
||||
this.buttons.next.click();
|
||||
} else if(e.key === 'ArrowLeft') {
|
||||
this.buttons.prev.click();
|
||||
} else {
|
||||
good = false;
|
||||
}
|
||||
|
||||
if(e.ctrlKey || e.metaKey) {
|
||||
this.ctrlKeyDown = true;
|
||||
}
|
||||
|
||||
if(good) {
|
||||
cancelEvent(e);
|
||||
}
|
||||
};
|
||||
|
||||
private onKeyUp = (e: KeyboardEvent) => {
|
||||
if(!(e.ctrlKey || e.metaKey)) {
|
||||
this.ctrlKeyDown = false;
|
||||
|
||||
if(this.isZooming()) {
|
||||
this.setZoomValue();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -370,7 +537,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
//mover.append(this.buttons.prev, this.buttons.next);
|
||||
}
|
||||
|
||||
this.removeCenterFromMover(mover);
|
||||
const zoomValue = this.isZooming() && closing /* && false */ ? this.zoomElements.rangeSelector.value : ZOOM_INITIAL_VALUE;
|
||||
/* if(!(zoomValue > 1 && closing)) */ this.removeCenterFromMover(mover);
|
||||
|
||||
const wasActive = fromRight !== 0;
|
||||
|
||||
@ -444,6 +612,14 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
top = rect.top;
|
||||
}
|
||||
|
||||
if(zoomValue > 1) { // 33
|
||||
// const diffX = (rect.width * zoomValue - rect.width) / 4;
|
||||
const diffX = (rect.width * zoomValue - rect.width) / 2;
|
||||
const diffY = (rect.height * zoomValue - rect.height) / 4;
|
||||
// left -= diffX;
|
||||
// top += diffY;
|
||||
}
|
||||
|
||||
transform += `translate3d(${left}px,${top}px,0) `;
|
||||
|
||||
/* if(wasActive) {
|
||||
@ -485,6 +661,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
mover.style.width = containerRect.width + 'px';
|
||||
mover.style.height = containerRect.height + 'px';
|
||||
|
||||
// const scaleX = rect.width / (containerRect.width * zoomValue);
|
||||
// const scaleY = rect.height / (containerRect.height * zoomValue);
|
||||
const scaleX = rect.width / containerRect.width;
|
||||
const scaleY = rect.height / containerRect.height;
|
||||
if(!wasActive) {
|
||||
@ -499,7 +677,17 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
}
|
||||
//let borderRadius = '0px 0px 0px 0px';
|
||||
|
||||
mover.style.transform = transform;
|
||||
if(closing && zoomValue > 1) {
|
||||
const width = this.moversContainer.scrollWidth * scaleX;
|
||||
const height = this.moversContainer.scrollHeight * scaleY;
|
||||
const willBeLeft = appPhotosManager.windowW / 2 - rect.width / 2;
|
||||
const willBeTop = appPhotosManager.windowH / 2 - rect.height / 2;
|
||||
const left = rect.left - willBeLeft/* + (width - rect.width) / 2 */;
|
||||
const top = rect.top - willBeTop/* + (height - rect.height) / 2 */;
|
||||
this.moversContainer.style.transform = `matrix(${scaleX}, 0, 0, ${scaleY}, ${left}, ${top})`;
|
||||
} else {
|
||||
mover.style.transform = transform;
|
||||
}
|
||||
|
||||
needOpacity && (mover.style.opacity = '0'/* !closing ? '0' : '' */);
|
||||
|
||||
@ -663,6 +851,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
this.wholeDiv.classList.remove('active');
|
||||
}, 0);
|
||||
|
||||
return ret;
|
||||
|
||||
setTimeout(() => {
|
||||
mover.style.borderRadius = borderRadius;
|
||||
|
||||
@ -679,10 +869,12 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
deferred.resolve();
|
||||
}, delay);
|
||||
|
||||
mover.classList.remove('opening');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
mover.classList.toggle('opening', !closing);
|
||||
mover.classList.add('opening');
|
||||
|
||||
//await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
//await new Promise((resolve) => window.requestAnimationFrame(resolve));
|
||||
@ -713,7 +905,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
}, 0/* delay / 2 */);
|
||||
|
||||
mover.dataset.timeout = '' + setTimeout(() => {
|
||||
mover.classList.remove('moving');
|
||||
mover.classList.remove('moving', 'opening');
|
||||
|
||||
if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
|
||||
if(mover.querySelector('video') || true) {
|
||||
@ -848,7 +1040,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
const oldMover = this.content.mover;
|
||||
oldMover.parentElement.append(newMover);
|
||||
} else {
|
||||
this.wholeDiv.append(newMover);
|
||||
this.moversContainer.append(newMover);
|
||||
}
|
||||
|
||||
return this.content.mover = newMover;
|
||||
@ -1014,6 +1206,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
this.setNewMover();
|
||||
} else {
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
window.addEventListener('keyup', this.onKeyUp);
|
||||
const mainColumns = this.pageEl.querySelector('#main-columns');
|
||||
this.pageEl.insertBefore(this.wholeDiv, mainColumns);
|
||||
void this.wholeDiv.offsetLeft; // reflow
|
||||
@ -1039,13 +1232,13 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
||||
|
||||
const mover = this.content.mover;
|
||||
|
||||
//const maxWidth = appPhotosManager.windowW - 16;
|
||||
const maxWidth = this.pageEl.scrollWidth;
|
||||
const maxWidth = appPhotosManager.windowW;
|
||||
//const maxWidth = this.pageEl.scrollWidth;
|
||||
// TODO: const maxHeight = mediaSizes.isMobile ? appPhotosManager.windowH : appPhotosManager.windowH - 100;
|
||||
let padding = 0;
|
||||
const windowH = appPhotosManager.windowH;
|
||||
if(windowH < 1000000 && !mediaSizes.isMobile) {
|
||||
padding = 32 + 120;
|
||||
padding = 120;
|
||||
}
|
||||
const maxHeight = windowH - 120 - padding;
|
||||
let thumbPromise: Promise<any> = Promise.resolve();
|
||||
|
@ -25,9 +25,12 @@ export default class RangeSelector {
|
||||
|
||||
protected decimals: number;
|
||||
|
||||
constructor(protected step: number, protected value: number, protected min: number, protected max: number) {
|
||||
constructor(protected step: number, value: number, protected min: number, protected max: number, withTransition = false) {
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('progress-line');
|
||||
if(withTransition) {
|
||||
this.container.classList.add('with-transition');
|
||||
}
|
||||
|
||||
this.filled = document.createElement('div');
|
||||
this.filled.classList.add('progress-line__filled');
|
||||
@ -54,6 +57,10 @@ export default class RangeSelector {
|
||||
this.container.append(this.filled, seek);
|
||||
}
|
||||
|
||||
get value() {
|
||||
return +this.seek.value;
|
||||
}
|
||||
|
||||
public setHandlers(events: RangeSelector['events']) {
|
||||
this.events = events;
|
||||
}
|
||||
@ -86,8 +93,13 @@ export default class RangeSelector {
|
||||
};
|
||||
|
||||
public setProgress(value: number) {
|
||||
this.setFilled(value);
|
||||
this.seek.value = '' + value;
|
||||
this.setFilled(+this.seek.value); // clamp
|
||||
}
|
||||
|
||||
public addProgress(value: number) {
|
||||
this.seek.value = '' + (+this.seek.value + value);
|
||||
this.setFilled(+this.seek.value); // clamp
|
||||
}
|
||||
|
||||
public setFilled(value: number) {
|
||||
|
@ -16,10 +16,11 @@ const attachGlobalListenerTo = window;
|
||||
|
||||
export default class SwipeHandler {
|
||||
private element: HTMLElement;
|
||||
private onSwipe: (xDiff: number, yDiff: number) => boolean;
|
||||
private onSwipe: (xDiff: number, yDiff: number) => boolean | void;
|
||||
private verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean;
|
||||
private onFirstSwipe: () => void;
|
||||
private onReset: () => void;
|
||||
private cursor: 'grabbing' | 'move' = 'grabbing';
|
||||
|
||||
private hadMove = false;
|
||||
private xDown: number = null;
|
||||
@ -31,9 +32,14 @@ export default class SwipeHandler {
|
||||
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
|
||||
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
|
||||
onReset?: SwipeHandler['onReset'],
|
||||
cursor?: SwipeHandler['cursor']
|
||||
}) {
|
||||
safeAssign(this, options);
|
||||
|
||||
this.setListeners();
|
||||
}
|
||||
|
||||
public setListeners() {
|
||||
if(!isTouchSupported) {
|
||||
this.element.addEventListener('mousedown', this.handleStart, false);
|
||||
attachGlobalListenerTo.addEventListener('mouseup', this.reset);
|
||||
@ -43,6 +49,16 @@ export default class SwipeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public removeListeners() {
|
||||
if(!isTouchSupported) {
|
||||
this.element.removeEventListener('mousedown', this.handleStart, false);
|
||||
attachGlobalListenerTo.removeEventListener('mouseup', this.reset);
|
||||
} else {
|
||||
this.element.removeEventListener('touchstart', this.handleStart, false);
|
||||
attachGlobalListenerTo.removeEventListener('touchend', this.reset);
|
||||
}
|
||||
}
|
||||
|
||||
reset = (e?: Event) => {
|
||||
/* if(e) {
|
||||
cancelEvent(e);
|
||||
@ -101,7 +117,7 @@ export default class SwipeHandler {
|
||||
this.hadMove = true;
|
||||
|
||||
if(!isTouchSupported) {
|
||||
this.element.style.cursor = 'grabbing';
|
||||
this.element.style.cursor = this.cursor;
|
||||
}
|
||||
|
||||
if(this.onFirstSwipe) {
|
||||
@ -124,7 +140,8 @@ export default class SwipeHandler {
|
||||
// }
|
||||
|
||||
/* reset values */
|
||||
if(this.onSwipe(xDiff, yDiff)) {
|
||||
const onSwipeResult = this.onSwipe(xDiff, yDiff);
|
||||
if(onSwipeResult !== undefined && onSwipeResult) {
|
||||
this.reset();
|
||||
}
|
||||
};
|
||||
|
@ -6,7 +6,10 @@
|
||||
|
||||
export type GrabEvent = {x: number, y: number, isTouch?: boolean};
|
||||
|
||||
export default function attachGrabListeners(element: HTMLElement, onStart: (position: GrabEvent) => void, onMove: (position: GrabEvent) => void, onEnd: (position: GrabEvent) => void) {
|
||||
export default function attachGrabListeners(element: HTMLElement,
|
||||
onStart: (position: GrabEvent) => void,
|
||||
onMove: (position: GrabEvent) => void,
|
||||
onEnd?: (position: GrabEvent) => void) {
|
||||
// * Mouse
|
||||
const onMouseMove = (event: MouseEvent) => {
|
||||
onMove({x: event.pageX, y: event.pageY});
|
||||
|
@ -488,11 +488,8 @@
|
||||
.progress-line {
|
||||
--height: 2px;
|
||||
--border-radius: 4px;
|
||||
--thumb-size: .75rem;
|
||||
flex: 1 1 auto;
|
||||
margin-left: 5px;
|
||||
|
||||
&__seek {
|
||||
--thumb-size: .75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,6 @@
|
||||
}
|
||||
|
||||
.default {
|
||||
border: 0 solid rgba(0, 0, 0, .2);
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, .2);
|
||||
position: relative;
|
||||
font-size: 0;
|
||||
//overflow: hidden;
|
||||
@ -135,7 +133,11 @@
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
&__loaded, & {
|
||||
&__loaded {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
& {
|
||||
background: rgba(255, 255, 255, .38);
|
||||
}
|
||||
|
||||
@ -241,15 +243,12 @@
|
||||
--color: #fff;
|
||||
margin: 0;
|
||||
width: 50px;
|
||||
--thumb-size: 15px;
|
||||
|
||||
// https://stackoverflow.com/a/4816050
|
||||
html.is-ios & {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__seek {
|
||||
--thumb-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,6 +267,7 @@ video::-webkit-media-controls-enclosure {
|
||||
--color: var(--primary-color);
|
||||
--height: 5px;
|
||||
--border-radius: 6px;
|
||||
--thumb-size: 13px;
|
||||
border-radius: var(--border-radius);
|
||||
height: var(--height);
|
||||
position: relative;
|
||||
@ -285,12 +285,10 @@ video::-webkit-media-controls-enclosure {
|
||||
}
|
||||
|
||||
&__seek {
|
||||
--thumb-size: 13px;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@ -300,63 +298,42 @@ video::-webkit-media-controls-enclosure {
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
/* &::-webkit-slider-runnable-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
border-radius: 1.3px;
|
||||
-webkit-appearance: none;
|
||||
} */
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
height: var(--thumb-size);
|
||||
width: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
//margin-left: -.5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
height: var(--thumb-size);
|
||||
width: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
//margin-left: -.5px;
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
height: var(--thumb-size);
|
||||
width: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
//margin-left: -.5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 8.4px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
//border-radius: 1.3px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* &::-webkit-slider-thumb,
|
||||
&::-moz-range-thumb,
|
||||
&::-moz-range-track,
|
||||
&::-webkit-slider-runnable-track {
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
width: 0;
|
||||
height: 0;
|
||||
} */
|
||||
}
|
||||
|
||||
&__filled {
|
||||
@ -366,6 +343,20 @@ video::-webkit-media-controls-enclosure {
|
||||
&:not(.progress-line__loaded) {
|
||||
background-color: var(--color);
|
||||
z-index: 1;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
height: var(--thumb-size);
|
||||
width: var(--thumb-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translate(calc(var(--thumb-size) / 2), -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,11 +365,19 @@ video::-webkit-media-controls-enclosure {
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
&__seek, &__filled, &__loaded {
|
||||
&__seek,
|
||||
&__filled,
|
||||
&__loaded {
|
||||
border-radius: var(--border-radius);
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@include animation-level(2) {
|
||||
&.with-transition .progress-line__filled {
|
||||
transition: width .2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,17 +1123,14 @@
|
||||
|
||||
.progress-line {
|
||||
--height: 2px;
|
||||
--color: var(--primary-color);
|
||||
--border-radius: 4px;
|
||||
--thumb-size: 12px;
|
||||
background-color: #e6ecf0;
|
||||
|
||||
&__filled {
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
&__seek {
|
||||
--thumb-color: var(--primary-color);
|
||||
--thumb-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
$inactive-opacity: .4;
|
||||
|
||||
.media-viewer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -119,7 +121,7 @@
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
z-index: 5;
|
||||
z-index: 4;
|
||||
bottom: .75rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@ -151,7 +153,7 @@
|
||||
|
||||
.media-viewer-whole.active & {
|
||||
html.no-touch & {
|
||||
opacity: .4;
|
||||
opacity: $inactive-opacity;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
@ -255,7 +257,7 @@
|
||||
|
||||
&-mover/* , &-canvas */ {
|
||||
position: fixed!important;
|
||||
z-index: 4;
|
||||
// z-index: 4;
|
||||
//transition: .5s all;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -435,14 +437,15 @@
|
||||
padding: 0 1.25rem;
|
||||
|
||||
.btn-icon, .media-viewer-author {
|
||||
color: #8b8b8b;
|
||||
color: #fff;
|
||||
opacity: $inactive-opacity;
|
||||
|
||||
@include animation-level(2) {
|
||||
transition: color var(--open-duration) ease-in-out;
|
||||
transition: opacity var(--open-duration) ease-in-out, color var(--open-duration) ease-in-out, background-color var(--open-duration) ease-in-out;
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
color: #fff;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -530,6 +533,9 @@
|
||||
} */
|
||||
|
||||
.btn-menu-toggle {
|
||||
color: rgba(255, 255, 255, $inactive-opacity);
|
||||
opacity: 1;
|
||||
|
||||
&.menu-open {
|
||||
color: #fff;
|
||||
background-color: rgba(112, 117, 121, .2) !important;
|
||||
@ -544,6 +550,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-movers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
|
||||
@include animation-level(2) {
|
||||
transition: transform var(--open-duration);
|
||||
}
|
||||
}
|
||||
|
||||
/* &-switchers {
|
||||
position: relative;
|
||||
width: $large-screen;
|
||||
@ -553,6 +572,74 @@
|
||||
} */
|
||||
}
|
||||
|
||||
.tgico-zoom {
|
||||
&:before {
|
||||
content: $tgico-zoomout;
|
||||
}
|
||||
|
||||
&.zoom-in:before {
|
||||
content: $tgico-zoomin;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-container {
|
||||
width: 17.125rem;
|
||||
height: 3.375rem;
|
||||
background-color: rgba(0, 0, 0, .4);
|
||||
border-radius: $border-radius-big;
|
||||
padding: .5rem;
|
||||
opacity: 1;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
position: absolute;
|
||||
bottom: 1.25rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 5;
|
||||
|
||||
@include animation-level(2) {
|
||||
transition: opacity var(--open-duration);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
color: #fff;
|
||||
|
||||
&.inactive {
|
||||
pointer-events: none;
|
||||
opacity: $inactive-opacity;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-line {
|
||||
--color: #fff;
|
||||
--height: 2px;
|
||||
flex: 1 1 auto;
|
||||
margin: 0 1px;
|
||||
|
||||
&:before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-visible),
|
||||
.media-viewer-whole:not(.active) & {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.is-visible {
|
||||
opacity: 1;
|
||||
|
||||
& ~ .media-viewer-caption {
|
||||
opacity: 0 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overlays {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
Loading…
Reference in New Issue
Block a user