Browse Source

Fix range selector

master
Eduard Kuzmenko 4 years ago
parent
commit
b6a9c02c5c
  1. 66
      src/components/colorPicker.ts
  2. 80
      src/components/rangeSelector.ts
  3. 61
      src/helpers/dom/attachGrabListeners.ts
  4. 6
      src/lib/mediaPlayer.ts
  5. 1
      src/scss/partials/_ckin.scss

66
src/components/colorPicker.ts

@ -1,8 +1,8 @@
import { ColorHsla, hexaToHsla, hslaToRgba, rgbaToHexa as rgbaToHexa, rgbaToHsla } from "../helpers/color"; import { ColorHsla, hexaToHsla, hslaToRgba, rgbaToHexa as rgbaToHexa, rgbaToHsla } from "../helpers/color";
import attachGrabListeners from "../helpers/dom/attachGrabListeners";
import { clamp } from "../helpers/number"; import { clamp } from "../helpers/number";
import InputField, { InputState } from "./inputField"; import InputField, { InputState } from "./inputField";
type EventPosition = {x: number, y: number, isTouch?: boolean};
export type ColorPickerColor = { export type ColorPickerColor = {
hsl: string; hsl: string;
rgb: string; rgb: string;
@ -133,56 +133,6 @@ export default class ColorPicker {
this.attachHueListeners(); 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 = () => { private onGrabStart = () => {
document.documentElement.style.cursor = this.elements.boxDragger.style.cursor = 'grabbing'; document.documentElement.style.cursor = this.elements.boxDragger.style.cursor = 'grabbing';
}; };
@ -192,21 +142,27 @@ export default class ColorPicker {
}; };
private attachBoxListeners() { private attachBoxListeners() {
this.attachGrabListeners(this.elements.box, () => { attachGrabListeners(this.elements.box as any, () => {
this.onGrabStart();
this.boxRect = this.elements.box.getBoundingClientRect(); this.boxRect = this.elements.box.getBoundingClientRect();
//this.boxDraggerRect = this.elements.boxDragger.getBoundingClientRect(); //this.boxDraggerRect = this.elements.boxDragger.getBoundingClientRect();
}, (pos) => { }, (pos) => {
this.saturationHandler(pos.x, pos.y); this.saturationHandler(pos.x, pos.y);
}, null); }, () => {
this.onGrabEnd();
});
} }
private attachHueListeners() { private attachHueListeners() {
this.attachGrabListeners(this.elements.hue, () => { attachGrabListeners(this.elements.hue as any, () => {
this.onGrabStart();
this.hueRect = this.elements.hue.getBoundingClientRect(); this.hueRect = this.elements.hue.getBoundingClientRect();
//this.hueDraggerRect = this.elements.hueDragger.getBoundingClientRect(); //this.hueDraggerRect = this.elements.hueDragger.getBoundingClientRect();
}, (pos) => { }, (pos) => {
this.hueHandler(pos.x); this.hueHandler(pos.x);
}, null); }, () => {
this.onGrabEnd();
});
} }
public setColor(color: ColorHsla | string, updateHexInput = true, updateRgbInput = true) { public setColor(color: ColorHsla | string, updateHexInput = true, updateRgbInput = true) {

80
src/components/rangeSelector.ts

@ -4,10 +4,8 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import { isTouchSupported } from "../helpers/touchSupport";
import { clamp } from "../helpers/number"; import { clamp } from "../helpers/number";
import attachGrabListeners, { GrabEvent } from "../helpers/dom/attachGrabListeners";
type SUPEREVENT = MouseEvent | TouchEvent;
export default class RangeSelector { export default class RangeSelector {
public container: HTMLDivElement; public container: HTMLDivElement;
@ -15,12 +13,14 @@ export default class RangeSelector {
protected seek: HTMLInputElement; protected seek: HTMLInputElement;
public mousedown = false; public mousedown = false;
protected rect: DOMRect;
protected _removeListeners: () => void;
private events: Partial<{ private events: Partial<{
//onMouseMove: ProgressLine['onMouseMove'], //onMouseMove: ProgressLine['onMouseMove'],
onMouseDown: RangeSelector['onMouseDown'], onMouseDown: RangeSelector['onMouseDown'],
onMouseUp: RangeSelector['onMouseUp'], onMouseUp: RangeSelector['onMouseUp'],
onScrub: (scrubTime: number) => void onScrub: (value: number) => void
}> = {}; }> = {};
protected decimals: number; protected decimals: number;
@ -41,10 +41,6 @@ export default class RangeSelector {
seek.max = '' + this.max; seek.max = '' + this.max;
seek.value = '' + value; seek.value = '' + value;
/* this.seek.addEventListener('change', (e) => {
console.log('seek change', e);
}); */
if(value) { if(value) {
this.setProgress(value); this.setProgress(value);
} }
@ -62,33 +58,33 @@ export default class RangeSelector {
this.events = events; this.events = events;
} }
onMouseMove = (e: SUPEREVENT) => { protected onMouseMove = (event: GrabEvent) => {
this.mousedown && this.scrub(e); this.scrub(event);
}; };
onMouseDown = (e: SUPEREVENT) => { protected onMouseDown = (event: GrabEvent) => {
this.scrub(e); this.rect = this.container.getBoundingClientRect();
this.mousedown = true; 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.mousedown = false;
this.events?.onMouseUp && this.events.onMouseUp(e); this.events?.onMouseUp && this.events.onMouseUp(event);
}; };
public setListeners() { public setListeners() {
this.container.addEventListener('mousemove', this.onMouseMove); this.seek.addEventListener('input', this.onInput);
this.container.addEventListener('mousedown', this.onMouseDown); this._removeListeners = attachGrabListeners(this.container, this.onMouseDown, this.onMouseMove, this.onMouseUp);
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});
}
} }
public onInput = () => {
const value = +this.seek.value;
this.setFilled(value);
this.events?.onScrub && this.events.onScrub(value);
};
public setProgress(value: number) { public setProgress(value: number) {
this.setFilled(value); this.setFilled(value);
this.seek.value = '' + value; this.seek.value = '' + value;
@ -97,50 +93,40 @@ export default class RangeSelector {
public setFilled(value: number) { public setFilled(value: number) {
let percents = (value - this.min) / (this.max - this.min); let percents = (value - this.min) / (this.max - this.min);
percents = clamp(percents, 0, 1); percents = clamp(percents, 0, 1);
//console.log('setFilled', percents, value);
this.filled.style.width = (percents * 100) + '%'; this.filled.style.width = (percents * 100) + '%';
//this.filled.style.transform = 'scaleX(' + scaleX + ')'; //this.filled.style.transform = 'scaleX(' + scaleX + ')';
} }
protected scrub(e: SUPEREVENT) { protected scrub(event: GrabEvent) {
let offsetX: number; const offsetX = clamp(event.x - this.rect.left, 0, this.rect.width);
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);
}
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)) { if((value - this.min) < ((this.max - this.min) / 2)) {
value -= this.step / 10; value -= this.step / 10;
} }
//console.log('scrub value:', value, this.decimals, offsetX, rect.width, e);
value = +value.toFixed(this.decimals); 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); 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); this.events?.onScrub && this.events.onScrub(value);
return value; return value;
} }
public removeListeners() { public removeListeners() {
this.container.removeEventListener('mousemove', this.onMouseMove); if(this._removeListeners) {
this.container.removeEventListener('mousedown', this.onMouseDown); this._removeListeners();
this.container.removeEventListener('mouseup', this.onMouseUp); this._removeListeners = null;
if(isTouchSupported) {
this.container.removeEventListener('touchmove', this.onMouseMove);
this.container.removeEventListener('touchstart', this.onMouseDown);
this.container.removeEventListener('touchend', this.onMouseUp);
} }
this.seek.removeEventListener('input', this.onInput);
this.events = {}; this.events = {};
} }
} }

61
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);
};
}

6
src/lib/mediaPlayer.ts

@ -11,8 +11,6 @@ import { isTouchSupported } from "../helpers/touchSupport";
import RangeSelector from "../components/rangeSelector"; import RangeSelector from "../components/rangeSelector";
import { onVideoLoad } from "../helpers/files"; import { onVideoLoad } from "../helpers/files";
type SUPEREVENT = MouseEvent | TouchEvent;
export class MediaProgressLine extends RangeSelector { export class MediaProgressLine extends RangeSelector {
private filledLoad: HTMLDivElement; private filledLoad: HTMLDivElement;
@ -36,7 +34,7 @@ export class MediaProgressLine extends RangeSelector {
this.setSeekMax(); this.setSeekMax();
this.setListeners(); this.setListeners();
this.setHandlers({ this.setHandlers({
onMouseDown: (e: SUPEREVENT) => { onMouseDown: () => {
//super.onMouseDown(e); //super.onMouseDown(e);
//Таймер для того, чтобы стопать видео, если зажал мышку и не отпустил клик //Таймер для того, чтобы стопать видео, если зажал мышку и не отпустил клик
@ -50,7 +48,7 @@ export class MediaProgressLine extends RangeSelector {
}, 150); }, 150);
}, },
onMouseUp: (e: SUPEREVENT) => { onMouseUp: () => {
//super.onMouseUp(e); //super.onMouseUp(e);
if(this.stopAndScrubTimeout) { if(this.stopAndScrubTimeout) {

1
src/scss/partials/_ckin.scss

@ -7,6 +7,7 @@
.ckin { .ckin {
&__player { &__player {
letter-spacing: 0.02em; letter-spacing: 0.02em;
user-select: none;
&.ckin__fullscreen { &.ckin__fullscreen {
position: fixed; position: fixed;

Loading…
Cancel
Save