diff --git a/src/components/colorPicker.ts b/src/components/colorPicker.ts index 506933f2..9a38cd59 100644 --- a/src/components/colorPicker.ts +++ b/src/components/colorPicker.ts @@ -1,8 +1,8 @@ import { ColorHsla, hexaToHsla, hslaToRgba, rgbaToHexa as rgbaToHexa, rgbaToHsla } from "../helpers/color"; +import attachGrabListeners from "../helpers/dom/attachGrabListeners"; import { clamp } from "../helpers/number"; import InputField, { InputState } from "./inputField"; -type EventPosition = {x: number, y: number, isTouch?: boolean}; export type ColorPickerColor = { hsl: string; rgb: string; @@ -133,56 +133,6 @@ export default class ColorPicker { this.attachHueListeners(); } - private attachGrabListeners(element: SVGSVGElement, onStart: (position: EventPosition) => void, onMove: (position: EventPosition) => void, onEnd: (position: EventPosition) => void) { - // * Mouse - const onMouseMove = (event: MouseEvent) => { - onMove({x: event.pageX, y: event.pageY}); - }; - - const onMouseDown = (event: MouseEvent) => { - if(event.button !== 0) { - element.addEventListener('mousedown', onMouseDown, {once: true}); - return; - } - - this.onGrabStart(); - onStart({x: event.pageX, y: event.pageY}); - onMouseMove(event); - - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', () => { - document.removeEventListener('mousemove', onMouseMove); - element.addEventListener('mousedown', onMouseDown, {once: true}); - this.onGrabEnd(); - onEnd && onEnd({x: event.pageX, y: event.pageY}); - }, {once: true}); - }; - - element.addEventListener('mousedown', onMouseDown, {once: true}); - - // * Touch - const onTouchMove = (event: TouchEvent) => { - event.preventDefault(); - onMove({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); - }; - - const onTouchStart = (event: TouchEvent) => { - this.onGrabStart(); - onStart({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); - onTouchMove(event); - - document.addEventListener('touchmove', onTouchMove, {passive: false}); - document.addEventListener('touchend', (event) => { - document.removeEventListener('touchmove', onTouchMove); - element.addEventListener('touchstart', onTouchStart, {passive: true, once: true}); - this.onGrabEnd(); - onEnd && onEnd({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); - }, {passive: true, once: true}); - }; - - element.addEventListener('touchstart', onTouchStart, {passive: true, once: true}); - } - private onGrabStart = () => { document.documentElement.style.cursor = this.elements.boxDragger.style.cursor = 'grabbing'; }; @@ -192,21 +142,27 @@ export default class ColorPicker { }; private attachBoxListeners() { - this.attachGrabListeners(this.elements.box, () => { + attachGrabListeners(this.elements.box as any, () => { + this.onGrabStart(); this.boxRect = this.elements.box.getBoundingClientRect(); //this.boxDraggerRect = this.elements.boxDragger.getBoundingClientRect(); }, (pos) => { this.saturationHandler(pos.x, pos.y); - }, null); + }, () => { + this.onGrabEnd(); + }); } private attachHueListeners() { - this.attachGrabListeners(this.elements.hue, () => { + attachGrabListeners(this.elements.hue as any, () => { + this.onGrabStart(); this.hueRect = this.elements.hue.getBoundingClientRect(); //this.hueDraggerRect = this.elements.hueDragger.getBoundingClientRect(); }, (pos) => { this.hueHandler(pos.x); - }, null); + }, () => { + this.onGrabEnd(); + }); } public setColor(color: ColorHsla | string, updateHexInput = true, updateRgbInput = true) { diff --git a/src/components/rangeSelector.ts b/src/components/rangeSelector.ts index e7ad2d60..ed5743a8 100644 --- a/src/components/rangeSelector.ts +++ b/src/components/rangeSelector.ts @@ -4,10 +4,8 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { isTouchSupported } from "../helpers/touchSupport"; import { clamp } from "../helpers/number"; - -type SUPEREVENT = MouseEvent | TouchEvent; +import attachGrabListeners, { GrabEvent } from "../helpers/dom/attachGrabListeners"; export default class RangeSelector { public container: HTMLDivElement; @@ -15,12 +13,14 @@ export default class RangeSelector { protected seek: HTMLInputElement; public mousedown = false; + protected rect: DOMRect; + protected _removeListeners: () => void; private events: Partial<{ //onMouseMove: ProgressLine['onMouseMove'], onMouseDown: RangeSelector['onMouseDown'], onMouseUp: RangeSelector['onMouseUp'], - onScrub: (scrubTime: number) => void + onScrub: (value: number) => void }> = {}; protected decimals: number; @@ -41,10 +41,6 @@ export default class RangeSelector { seek.max = '' + this.max; seek.value = '' + value; - /* this.seek.addEventListener('change', (e) => { - console.log('seek change', e); - }); */ - if(value) { this.setProgress(value); } @@ -62,33 +58,33 @@ export default class RangeSelector { this.events = events; } - onMouseMove = (e: SUPEREVENT) => { - this.mousedown && this.scrub(e); + protected onMouseMove = (event: GrabEvent) => { + this.scrub(event); }; - onMouseDown = (e: SUPEREVENT) => { - this.scrub(e); + protected onMouseDown = (event: GrabEvent) => { + this.rect = this.container.getBoundingClientRect(); this.mousedown = true; - this.events?.onMouseDown && this.events.onMouseDown(e); + this.scrub(event); + this.events?.onMouseDown && this.events.onMouseDown(event); }; - onMouseUp = (e: SUPEREVENT) => { + protected onMouseUp = (event: GrabEvent) => { this.mousedown = false; - this.events?.onMouseUp && this.events.onMouseUp(e); + this.events?.onMouseUp && this.events.onMouseUp(event); }; public setListeners() { - this.container.addEventListener('mousemove', this.onMouseMove); - this.container.addEventListener('mousedown', this.onMouseDown); - this.container.addEventListener('mouseup', this.onMouseUp); - - if(isTouchSupported) { - this.container.addEventListener('touchmove', this.onMouseMove, {passive: true}); - this.container.addEventListener('touchstart', this.onMouseDown, {passive: true}); - this.container.addEventListener('touchend', this.onMouseUp, {passive: true}); - } + this.seek.addEventListener('input', this.onInput); + this._removeListeners = attachGrabListeners(this.container, this.onMouseDown, this.onMouseMove, this.onMouseUp); } + public onInput = () => { + const value = +this.seek.value; + this.setFilled(value); + this.events?.onScrub && this.events.onScrub(value); + }; + public setProgress(value: number) { this.setFilled(value); this.seek.value = '' + value; @@ -97,50 +93,40 @@ export default class RangeSelector { public setFilled(value: number) { let percents = (value - this.min) / (this.max - this.min); percents = clamp(percents, 0, 1); - //console.log('setFilled', percents, value); + this.filled.style.width = (percents * 100) + '%'; //this.filled.style.transform = 'scaleX(' + scaleX + ')'; } - protected scrub(e: SUPEREVENT) { - let offsetX: number; - const rect = (e.target as HTMLElement).getBoundingClientRect(); - if(e instanceof MouseEvent) { - offsetX = e.pageX - Math.round(rect.left); - } else { // touch - offsetX = e.targetTouches[0].pageX - Math.round(rect.left); - } + protected scrub(event: GrabEvent) { + const offsetX = clamp(event.x - this.rect.left, 0, this.rect.width); - let value = this.min + (offsetX / Math.round(rect.width) * (this.max - this.min)); + let value = this.min + (offsetX / this.rect.width * (this.max - this.min)); if((value - this.min) < ((this.max - this.min) / 2)) { value -= this.step / 10; } - //console.log('scrub value:', value, this.decimals, offsetX, rect.width, e); value = +value.toFixed(this.decimals); - //const dotIndex = ('' + value).indexOf('.'); - //value = +('' + value).slice(0, this.decimals ? dotIndex + this.decimals : dotIndex); - value = clamp(value, this.min, this.max); - this.setFilled(value); + //this.seek.value = '' + value; + //this.onInput(); + this.setProgress(value); this.events?.onScrub && this.events.onScrub(value); + return value; } public removeListeners() { - this.container.removeEventListener('mousemove', this.onMouseMove); - this.container.removeEventListener('mousedown', this.onMouseDown); - this.container.removeEventListener('mouseup', this.onMouseUp); - - if(isTouchSupported) { - this.container.removeEventListener('touchmove', this.onMouseMove); - this.container.removeEventListener('touchstart', this.onMouseDown); - this.container.removeEventListener('touchend', this.onMouseUp); + if(this._removeListeners) { + this._removeListeners(); + this._removeListeners = null; } + this.seek.removeEventListener('input', this.onInput); + this.events = {}; } -} \ No newline at end of file +} diff --git a/src/helpers/dom/attachGrabListeners.ts b/src/helpers/dom/attachGrabListeners.ts new file mode 100644 index 00000000..78b4c855 --- /dev/null +++ b/src/helpers/dom/attachGrabListeners.ts @@ -0,0 +1,61 @@ +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) { + // * Mouse + const onMouseMove = (event: MouseEvent) => { + onMove({x: event.pageX, y: event.pageY}); + }; + + const onMouseUp = (event: MouseEvent) => { + document.removeEventListener('mousemove', onMouseMove); + element.addEventListener('mousedown', onMouseDown, {once: true}); + onEnd && onEnd({x: event.pageX, y: event.pageY}); + }; + + const onMouseDown = (event: MouseEvent) => { + if(event.button !== 0) { + element.addEventListener('mousedown', onMouseDown, {once: true}); + return; + } + + onStart({x: event.pageX, y: event.pageY}); + onMouseMove(event); + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp, {once: true}); + }; + + element.addEventListener('mousedown', onMouseDown, {once: true}); + + // * Touch + const onTouchMove = (event: TouchEvent) => { + event.preventDefault(); + onMove({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); + }; + + const onTouchEnd = (event: TouchEvent) => { + document.removeEventListener('touchmove', onTouchMove); + element.addEventListener('touchstart', onTouchStart, {passive: true, once: true}); + onEnd && onEnd({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); + }; + + const onTouchStart = (event: TouchEvent) => { + onStart({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); + onTouchMove(event); + + document.addEventListener('touchmove', onTouchMove, {passive: false}); + document.addEventListener('touchend', onTouchEnd, {passive: true, once: true}); + }; + + element.addEventListener('touchstart', onTouchStart, {passive: true, once: true}); + + return () => { + element.removeEventListener('mousedown', onMouseDown); + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + + element.removeEventListener('touchstart', onTouchStart); + document.removeEventListener('touchmove', onTouchMove); + document.removeEventListener('touchend', onTouchEnd); + }; +} diff --git a/src/lib/mediaPlayer.ts b/src/lib/mediaPlayer.ts index d6ec43cb..1ce46442 100644 --- a/src/lib/mediaPlayer.ts +++ b/src/lib/mediaPlayer.ts @@ -11,8 +11,6 @@ import { isTouchSupported } from "../helpers/touchSupport"; import RangeSelector from "../components/rangeSelector"; import { onVideoLoad } from "../helpers/files"; -type SUPEREVENT = MouseEvent | TouchEvent; - export class MediaProgressLine extends RangeSelector { private filledLoad: HTMLDivElement; @@ -36,7 +34,7 @@ export class MediaProgressLine extends RangeSelector { this.setSeekMax(); this.setListeners(); this.setHandlers({ - onMouseDown: (e: SUPEREVENT) => { + onMouseDown: () => { //super.onMouseDown(e); //Таймер для того, чтобы стопать видео, если зажал мышку и не отпустил клик @@ -50,7 +48,7 @@ export class MediaProgressLine extends RangeSelector { }, 150); }, - onMouseUp: (e: SUPEREVENT) => { + onMouseUp: () => { //super.onMouseUp(e); if(this.stopAndScrubTimeout) { diff --git a/src/scss/partials/_ckin.scss b/src/scss/partials/_ckin.scss index 9a014a86..4d5845cf 100644 --- a/src/scss/partials/_ckin.scss +++ b/src/scss/partials/_ckin.scss @@ -7,6 +7,7 @@ .ckin { &__player { letter-spacing: 0.02em; + user-select: none; &.ckin__fullscreen { position: fixed;