UNPKG

page-flip

Version:

Powerful, simple and flexible JS Library for creating realistic and beautiful page turning effect

287 lines (233 loc) 9.07 kB
import { PageFlip } from '../PageFlip'; import { Point } from '../BasicTypes'; import { FlipSetting, SizeType } from '../Settings'; import { FlipCorner, FlippingState } from '../Flip/Flip'; import { Orientation } from '../Render/Render'; type SwipeData = { point: Point; time: number; }; /** * UI Class, represents work with DOM */ export abstract class UI { protected readonly parentElement: HTMLElement; protected readonly app: PageFlip; protected readonly wrapper: HTMLElement; protected distElement: HTMLElement; private touchPoint: SwipeData = null; private readonly swipeTimeout = 250; private readonly swipeDistance: number; private onResize = (): void => { this.update(); }; /** * @constructor * * @param {HTMLElement} inBlock - Root HTML Element * @param {PageFlip} app - PageFlip instanse * @param {FlipSetting} setting - Configuration object */ protected constructor(inBlock: HTMLElement, app: PageFlip, setting: FlipSetting) { this.parentElement = inBlock; inBlock.classList.add('stf__parent'); // Add first wrapper inBlock.insertAdjacentHTML('afterbegin', '<div class="stf__wrapper"></div>'); this.wrapper = inBlock.querySelector('.stf__wrapper'); this.app = app; const k = this.app.getSettings().usePortrait ? 1 : 2; // Setting block sizes based on configuration inBlock.style.minWidth = setting.minWidth * k + 'px'; inBlock.style.minHeight = setting.minHeight + 'px'; if (setting.size === SizeType.FIXED) { inBlock.style.minWidth = setting.width * k + 'px'; inBlock.style.minHeight = setting.height + 'px'; } if (setting.autoSize) { inBlock.style.width = '100%'; inBlock.style.maxWidth = setting.maxWidth * 2 + 'px'; } inBlock.style.display = 'block'; window.addEventListener('resize', this.onResize, false); this.swipeDistance = setting.swipeDistance; } /** * Destructor. Remove all HTML elements and all event handlers */ public destroy(): void { if (this.app.getSettings().useMouseEvents) this.removeHandlers(); this.distElement.remove(); this.wrapper.remove(); } /** * Updating child components when resizing */ public abstract update(): void; /** * Get parent element for book * * @returns {HTMLElement} */ public getDistElement(): HTMLElement { return this.distElement; } /** * Get wrapper element * * @returns {HTMLElement} */ public getWrapper(): HTMLElement { return this.wrapper; } /** * Updates styles and sizes based on book orientation * * @param {Orientation} orientation - New book orientation */ public setOrientationStyle(orientation: Orientation): void { this.wrapper.classList.remove('--portrait', '--landscape'); if (orientation === Orientation.PORTRAIT) { if (this.app.getSettings().autoSize) this.wrapper.style.paddingBottom = (this.app.getSettings().height / this.app.getSettings().width) * 100 + '%'; this.wrapper.classList.add('--portrait'); } else { if (this.app.getSettings().autoSize) this.wrapper.style.paddingBottom = (this.app.getSettings().height / (this.app.getSettings().width * 2)) * 100 + '%'; this.wrapper.classList.add('--landscape'); } this.update(); } protected removeHandlers(): void { window.removeEventListener('resize', this.onResize); this.distElement.removeEventListener('mousedown', this.onMouseDown); this.distElement.removeEventListener('touchstart', this.onTouchStart); window.removeEventListener('mousemove', this.onMouseMove); window.removeEventListener('touchmove', this.onTouchMove); window.removeEventListener('mouseup', this.onMouseUp); window.removeEventListener('touchend', this.onTouchEnd); } protected setHandlers(): void { window.addEventListener('resize', this.onResize, false); if (!this.app.getSettings().useMouseEvents) return; this.distElement.addEventListener('mousedown', this.onMouseDown); this.distElement.addEventListener('touchstart', this.onTouchStart); window.addEventListener('mousemove', this.onMouseMove); window.addEventListener('touchmove', this.onTouchMove, { passive: !this.app.getSettings().mobileScrollSupport, }); window.addEventListener('mouseup', this.onMouseUp); window.addEventListener('touchend', this.onTouchEnd); } /** * Convert global coordinates to relative book coordinates * * @param x * @param y */ private getMousePos(x: number, y: number): Point { const rect = this.distElement.getBoundingClientRect(); return { x: x - rect.left, y: y - rect.top, }; } private checkTarget(targer: EventTarget): boolean { if (!this.app.getSettings().clickEventForward) return true; if (['a', 'button'].includes((targer as HTMLElement).tagName.toLowerCase())) { return false; } return true; } private onMouseDown = (e: MouseEvent): void => { if (this.checkTarget(e.target)) { const pos = this.getMousePos(e.clientX, e.clientY); this.app.startUserTouch(pos); e.preventDefault(); } }; private onTouchStart = (e: TouchEvent): void => { if (this.checkTarget(e.target)) { if (e.changedTouches.length > 0) { const t = e.changedTouches[0]; const pos = this.getMousePos(t.clientX, t.clientY); this.touchPoint = { point: pos, time: Date.now(), }; // part of swipe detection setTimeout(() => { if (this.touchPoint !== null) { this.app.startUserTouch(pos); } }, this.swipeTimeout); if (!this.app.getSettings().mobileScrollSupport) e.preventDefault(); } } }; private onMouseUp = (e: MouseEvent): void => { const pos = this.getMousePos(e.clientX, e.clientY); this.app.userStop(pos); }; private onMouseMove = (e: MouseEvent): void => { const pos = this.getMousePos(e.clientX, e.clientY); this.app.userMove(pos, false); }; private onTouchMove = (e: TouchEvent): void => { if (e.changedTouches.length > 0) { const t = e.changedTouches[0]; const pos = this.getMousePos(t.clientX, t.clientY); if (this.app.getSettings().mobileScrollSupport) { if (this.touchPoint !== null) { if ( Math.abs(this.touchPoint.point.x - pos.x) > 10 || this.app.getState() !== FlippingState.READ ) { if (e.cancelable) this.app.userMove(pos, true); } } if (this.app.getState() !== FlippingState.READ) { e.preventDefault(); } } else { this.app.userMove(pos, true); } } }; private onTouchEnd = (e: TouchEvent): void => { if (e.changedTouches.length > 0) { const t = e.changedTouches[0]; const pos = this.getMousePos(t.clientX, t.clientY); let isSwipe = false; // swipe detection if (this.touchPoint !== null) { const dx = pos.x - this.touchPoint.point.x; const distY = Math.abs(pos.y - this.touchPoint.point.y); if ( Math.abs(dx) > this.swipeDistance && distY < this.swipeDistance * 2 && Date.now() - this.touchPoint.time < this.swipeTimeout ) { if (dx > 0) { this.app.flipPrev( this.touchPoint.point.y < this.app.getRender().getRect().height / 2 ? FlipCorner.TOP : FlipCorner.BOTTOM ); } else { this.app.flipNext( this.touchPoint.point.y < this.app.getRender().getRect().height / 2 ? FlipCorner.TOP : FlipCorner.BOTTOM ); } isSwipe = true; } this.touchPoint = null; } this.app.userStop(pos, isSwipe); } }; }