diff --git a/examples/index.html b/examples/index.html index cd8113b..e14fcdc 100644 --- a/examples/index.html +++ b/examples/index.html @@ -28,7 +28,7 @@
- +
@@ -39,6 +39,10 @@
+
+ +
+ diff --git a/range-slider-element.js b/range-slider-element.js index b12f39c..8726e16 100644 --- a/range-slider-element.js +++ b/range-slider-element.js @@ -1,7 +1,7 @@ import * as style from './styles.css'; const UPDATE_EVENTS = ['input', 'change']; -const REFLECTED_ATTRIBUTES = ['min', 'max', 'step', 'value', 'disabled']; +const REFLECTED_ATTRIBUTES = ['min', 'max', 'step', 'value', 'disabled', 'value-precision']; class RangeSliderElement extends HTMLElement { constructor() { @@ -25,12 +25,14 @@ class RangeSliderElement extends HTMLElement { get max() { return this.getAttribute('max') || '100'; } get step() { return this.getAttribute('step') || '1'; } get value() { return this.getAttribute('value') || this._defaultValue; } + get disabled() { return this.getAttribute('disabled') || false } get valuePrecision() { return this.getAttribute('label-precision') || ''; } set min(min) { this.setAttribute('min', min); } set max(max) { this.setAttribute('max', max); } set step(step) { this.setAttribute('step', step); } set value(value) { this.setAttribute('value', value); } + set disabled(disabled) { this.setAttribute('disabled', disabled); } set valuePrecision(precision) { this.setAttribute('label-precision', precision); } connectedCallback() { @@ -61,13 +63,16 @@ class RangeSliderElement extends HTMLElement { } _startHandler = e => { + this.classList.add('touch-active'); + + // Click and drag this.setPointerCapture(e.pointerId); this.addEventListener('pointermove', this._moveHandler, false); - this.classList.add('touch-active'); - console.log(e); - if (e.target.matches('.thumb')) return; - this._reflectValue(e); + // Click jump + if (!e.target.matches('.thumb')) { + this._reflectValue(e); + } } _moveHandler = e => { @@ -76,9 +81,9 @@ class RangeSliderElement extends HTMLElement { } _endHandler = e => { + this.classList.remove('touch-active'); this.releasePointerCapture(e.pointerId); this.removeEventListener('pointermove', this._moveHandler, false); - this.classList.remove('touch-active'); // TODO: check if value changed this.dispatchEvent(new Event('change', { bubbles: true })); @@ -88,19 +93,28 @@ class RangeSliderElement extends HTMLElement { const min = Number(this.min); const max = Number(this.max); const step = Number(this.step); - const oldValue = Number(this.value); + const oldValue = this.value; const valuePrecision = Number(this.valuePrecision) || getPrescision(this.step) || 0; const fullWidth = event.target.offsetWidth; - const offsetX = event.offsetX; - const stepInPixels = (max > 0) ? fullWidth / max : fullWidth / min; - const tmpValue = Math.min(Math.max(offsetX / stepInPixels, min), max); - const nearestValue = Math.ceil(tmpValue / step) * step; // Rounding in steps - const newValue = valuePrecision - ? nearestValue.toFixed(valuePrecision) - : Math.round(nearestValue).toString() - ; + const offsetX = Math.min(Math.max(event.offsetX, 0), fullWidth); + const percentComplete = offsetX / fullWidth; + + // TODO: RTL support + // if (this.adapter_.isRTL()) { + // percentComplete = 1 - percentComplete; + // } - console.log({ fullWidth, offsetX, stepInPixels, oldValue, newValue}); + // Fit the percentage complete between the range [min,max] + // by remapping from [0, 1] to [min, min+(max-min)]. + const computedValue = min + percentComplete * (max - min); + + // Rounding in steps + const nearestValue = Math.round(computedValue / step) * step; + + // Value precision + const newValue = valuePrecision ? nearestValue.toFixed(valuePrecision) : Math.round(nearestValue).toString(); + + console.log({ fullWidth, offsetX, oldValue, computedValue, newValue}); if (oldValue !== newValue) { this.value = newValue; @@ -108,11 +122,19 @@ class RangeSliderElement extends HTMLElement { } } + _stepUp(amount = this.step) { + this.value += amount; + } + + _stepDown(amount = this.step) { + this.value -= amount; + } + _update() { const min = Number(this.min); const max = Number(this.max); const value = Number(this.value); - const percent = (max > 0) ? (100 * (value - min)) / (max - min) : (100 * (value - max)) / (min - max); + const percent = (100 * (value - min)) / (max - min); if (this._valueDisplay) { this._valueDisplay.textContent = value; diff --git a/styles.css b/styles.css index 80e32c9..90846cd 100644 --- a/styles.css +++ b/styles.css @@ -2,7 +2,8 @@ range-slider { position: relative; display: flex; height: 24px; - width: 130px; + width: 100%; + min-width: 130px; margin: 2px; overflow: visible; @@ -16,7 +17,7 @@ range-slider[disabled] { } range-slider::before { - --height: 3px; + --height: 5px; content: ""; display: block; @@ -24,19 +25,18 @@ range-slider::before { top: calc(50% - var(--height) / 2); left: 0; width: 100%; - height: 3px; + height: var(--height); border-radius: 1px; - box-shadow: 0 -.5px 0 rgba(0,0,0,0.3), inset 0 .5px 0 rgba(255,255,255,0.2), 0 .5px 0 rgba(255,255,255,0.3); - background: linear-gradient(#FF0080, #FF0080) 0/ var(--value-percent, 0%) 100% no-repeat #eee; + /* box-shadow: 0 -.5px 0 rgba(0,0,0,0.3), inset 0 .5px 0 rgba(255,255,255,0.2), 0 .5px 0 rgba(255,255,255,0.3); */ + background: linear-gradient(#6221ea, #6221ea) 0/ var(--value-percent, 0%) 100% no-repeat #c6afe5; } .thumb { - pointer-events: none; position: absolute; bottom: 6px; left: var(--value-percent, 0%); margin-left: -6px; - background: url('data:image/svg+xml,') center no-repeat #FF0080; + background: url('data:image/svg+xml,') center no-repeat #6221ea; border-radius: 50%; width: 12px; height: 12px;