Сегодня напишем с вами кастомный ползунок. Но не просто стилизуем его и напишем к нему код, но и напишем к нему административные кнопки, чтобы можно было назначать максимальную сумму и определять шаг возрастания.
Для создания данной ползунка вам надо перекопировать следующие часи кодов:
<div class="rinputRange_container"> <div class="set-range"> <div class="set-range--min" style="display:none"> <label for="min">Min</label> <input class="input input-min" type="number" id="min" name="inputMin" min="0" max="10000" value="0"> </div> <div class="set-range--max"> <label for="max">>Max</label> <input class="input input-max" type="number" id="max" name="inputMax" min="0" max="100000" value="5000"> </div> <div class="set-range--step"> <label for="step">Step</label> <input class="input input-step" type="number" id="step" name="inputStep" min="1" max="10000" value="50"> </div> </div> <div class="range-container"> <div class="total"></div> <div class="range-track"> <div class="thumb thumb-left" data-direction="left"> <div class="thumb-left--value"></div> </div> <div class="range-fill"></div> <div class="thumb thumb-right" data-direction="right"> <div class="thumb-right--value"></div> </div> <div class="range"> <div class="range-min"></div> <div class="range-mid"></div> <div class="range-max"></div> </div> </div> </div> </div>
:root { --white0: #f6f9fb; --white1: #ebf1f6; --white2: #c0d7ed; --black0: #0e1011; --black1: #131516; --black2: #242a2d; --black3: #191c1d; --grey1: #212529; --grey2: #697680; --darkgrey1: #1f2223; --darkgrey2: #111314; --primary1: #2874ae; --primary2: #04508a; --primary3: rgba(40, 116, 174, 0.9); --primary4: #022b41; --z-track: 1; --z-fill: 100; --z-thumb: 200; --z-thumb-value: 300; } .rinputRange_container { background-color: #000; color: #fff; padding: 20px 10px; } .set-range { display: flex; max-width: 32rem; width: 100%; height: 4rem; column-gap: 1rem; } .set-range div { display: flex; flex-direction: column; row-gap: .25rem; column-gap: .5rem; } .set-range div label { font-size: .75rem; padding: 0 .25rem; color: var(--white3); } .set-range div input { width: 100%; height: 1.5rem; border: none; border-radius: 4px; background: var(--black2); color: var(--white2); font-size: .75rem; outline: none; padding: 0 .5rem; } .range-container { position: relative; width: 100%; max-width: 32rem; height: 9rem; display: flex; justify-content: center; align-items: center; } .total { position: absolute; top: .375rem; left: 0; width: 100%; user-select: none; font-size: .75rem; margin-left: 2rem; padding: 0 .5rem; line-height: .75rem; } .total::before { position: absolute; content: "TOTAL"; line-height: .75rem; font-weight: 600; font-size: .5rem; letter-spacing: 1px; color: var(--white3); transform: translateX(-120%); } .range-track { width: 100%; min-width: 100%; height: 1rem; position: relative; border-radius: 6px; background: var(--black2); } .range-track::after { position: absolute; content: ""; width: 100%; margin: 0 auto; height: .5rem; top: 1.5rem; left: 0; border: none; background-image: linear-gradient(to left, var(--grey2) .125rem, transparent 0); background-size: 10% 100%; } .thumb-left, .thumb-right { position: absolute; top: 0; height: 2rem; width: .25rem; background: var(--primary1); z-index: var(--z-thumb); border-radius: 4px; } .thumb-left::before, .thumb-right::before { position: absolute; top: 0; left: 0; width: .5rem; background: transparent; height: 100%; content: ""; border-radius: 4px; transform: translateX(-.125rem); } .thumb-left:hover::before, .thumb-right:hover::before { background: var(--primary3); } .thumb-left { transform: translateY(-25%); } .thumb-right { transform: translate(-2px, -25%); } .range-fill { position: absolute; top: 0; background: var(--primary1); height: 100%; z-index: var(--z-fill); } .thumb-left--value, .thumb-right--value { position: relative; width: fit-content; padding: 0 .5rem; height: 1rem; background: var(--primary1); color: var(--white0); border-radius: 4px; z-index: var(--z-fill-value); display: flex; justify-content: center; align-items: center; font-size: .625rem; user-select: none; pointer-events: none; } .thumb-left--value { top: -1.8rem; transform: translateX(calc(-50% + 2px)); } .thumb-right--value { top: 2.8rem; transform: translateX(calc(-50% + 2px)); } .thumb-left--value::after, .thumb-right--value::after { content: ""; position: absolute; bottom: 0; left: 0; right: 0; width: 0; height: 0; margin: 0 auto; border: 0.25rem solid transparent; } .thumb-left--value::after { transform: translateY(0.15rem); border-top-color: var(--primary1); border-bottom: 0; } .thumb-right--value::after { transform: translateY(-0.95rem); border-bottom-color: var(--primary1); border-top: 0; } .range { position: absolute; width: calc(100% - .125rem); height: 5rem; display: flex; align-items: center; font-size: .625rem; top: 0; left: 0; z-index: var(--z-fill-value); box-sizing: border-box; } .range div { user-select: none; pointer-events: none; height: 100%; display: flex; width: 100%; align-items: flex-end; position: relative; } .range div::after { content: ""; position: absolute; bottom: 0; margin: 0 auto; width: .125rem; background: var(--grey2); height: calc(100% - 3.875rem); transform: translateY(-2.5rem); border-radius: 4px; } .range-mid { justify-content: center; } .range-max { justify-content: flex-end; } .range-min::after { left: 0; right: 100%; } .range-mid::after { left: 0; right: 0; } .range-max::after { right: 0; left: 100%; } @media screen and (max-width: 500px) { html { font-size: 16px; } } @media screen and (max-width: 380px) { html { font-size: 12px; } }
const $ = document.querySelector.bind(document); class Range { constructor() { this.track = $(".range-track"); this.thumbLeft = $(".thumb-left"); this.thumbRight = $(".thumb-right"); this.thumbLeftValue = $(".thumb-left--value"); this.thumbRightValue = $(".thumb-right--value"); this.rangeFill = $(".range-fill"); this.rangeMin = $(".range-min"); this.rangeMid = $(".range-mid"); this.rangeMax = $(".range-max"); this.inputContainer = $(".set-range"); this.inputMin = $(".input-min"); this.inputMax = $(".input-max"); this.inputStep = $(".input-step"); this.total = $(".total"); } init() { this.setLeft(+this.inputMin.value); this.setRight(+this.getMidValue()); this.calcFill(); this.setTotal(); this.setRangeValues(); this.setDefaultInputSteps(); } getInputMin() { return +this.inputMin.value; } getInputMax() { return +this.inputMax.value; } getInputStep() { return +this.inputStep.value; } getMidValue() { return Math.floor((this.getInputMax() - this.getInputMin()) / 2); } getLeftValue() { return parseInt(this.thumbLeft.getAttribute("data-value")); } getRightValue() { return parseInt(this.thumbRight.getAttribute("data-value")); } getLeft() { return +this.thumbLeft.style.left.slice(0, -1); } getRight() { return +this.thumbRight.style.left.slice(0, -1); } getWidth() { return this.getRight() - this.getLeft(); } setInputStep(e) { const [min, max] = [this.getInputMin(), this.getInputMax()]; const val = +e.target.value; if (min % val !== 0) { this.inputMin.value = Math.round(min / val) * val; } if (max % val !== 0) { this.inputMax.value = Math.round(max / val) * val; } this.init(); } setDefaultInputSteps() { const step = this.getInputStep(); this.inputMin.step = step; this.inputMax.step = step; } setRangeValues() { this.rangeMin.textContent = `$${this.getInputMin()}`; this.rangeMid.textContent = `$${this.getMidValue()}`; this.rangeMax.textContent = `$${this.getInputMax()}`; } setLeft(val) { this.thumbLeftValue.textContent = `$${val}`; this.thumbLeft.style.left = `${this.calcPercent(parseInt(val))}%`; this.thumbLeft.setAttribute("data-value", val); } setRight(val) { this.thumbRightValue.textContent = `$${val}`; this.thumbRight.style.left = `${this.calcPercent(parseInt(val))}%`; this.thumbRight.setAttribute("data-value", val); } setTotal() { this.total.textContent = `$ ${this.getRightValue() - this.getLeftValue()}`; } calcPercent(val) { const [min, max] = [this.getInputMin(), this.getInputMax()]; return ((val - min) / (max - min)) * 100; } calcFill() { const [left, right] = [this.getLeft(), this.getRight()]; this.rangeFill.style.width = `${right - left}%`; this.rangeFill.style.left = `${left}%`; } calcValue(percent) { const [min, max] = [this.getInputMin(), this.getInputMax()]; return Math.round((max - min) * percent) + min; } move(e, direction) { const parentRect = this.track.getBoundingClientRect(); const [parentWidth, parentLeft] = [parentRect.width, parentRect.left]; const [min, max] = [this.getInputMin(), this.getInputMax()]; const [left, right] = [this.getLeftValue(), this.getRightValue()]; const step = this.getInputStep(); const handlemove = (e) => { const offsetLeft = e.clientX - parentLeft; const percent = offsetLeft / parentWidth; const val = this.calcValue(percent); if (direction === "left" && (val <= min || val > right - step)) return; if (direction === "right" && (val < (left + step)) || val > max) return; const stepVal = Math.round(val / step) * step; if (direction === "left") { this.setLeft(stepVal); } else { this.setRight(stepVal); } this.calcFill(); this.setTotal(); }; const handleup = () => { document.removeEventListener("mousemove", handlemove); document.removeEventListener("mouseup", handleup); }; document.addEventListener("mousemove", handlemove); document.addEventListener("mouseup", handleup); } } const delegateMove = e => { if (e.target.closest(".thumb")) { e.target.getAttribute("data-direction") === "left" ? range.move(e, "left") : range.move(e, "right"); } }; const delegateInput = e => { if (e.target.closest(".input")) { e.target.name === "inputStep" ? range.setInputStep(e) : range.init(); } }; const range = new Range(); range.init(); range.track.onmousedown = delegateMove; range.inputContainer.oninput = delegateInput;
После копирования вышеупомянутых кусков кода, Наш ползунок готов. Пользуйтесь на здоровье.