Введение
Компонент слайдера в целом относительно прост, но он по-прежнему требует большого количества собственных знаний js.На следующем рисунке показан базовый компонент слайдера.
Видно, что в основном он в основном разделен на две части: часть слайдера и кнопка слайдера, а также синяя часть, которую слайдерная дорожка скользна, также является частью, которая включена в ползунок, а затем номер Выше представляет собой подсказку компонента элемента
Для вышеуказанных компонентов мышь может скользить, нажимая кнопку ползунка и перетаскивая, а затем щелкая по дорожке ползунка, также можно перемещать ползунок в указанное положение, поэтому основной логикой является реализация перетаскивания и логика щелчка по дорожке, код официального сайтакликните сюда
HTML-структура компонента
Упрощенная структура html выглядит следующим образом
<div class="el-slider" ...
//数字输入框
<el-input-number v-if="showInput">
</el-input-number>
//滑块轨道
<div class="el-slider__runway"
//已经滑过的轨道
<div class="el-slider__bar" :style="barStyle">
</div>
//第一个滑块按钮
<slider-button></slider-button>
//第二个滑块按钮
<slider-button></slider-button>
//滑块轨道的间断点
<div class="el-slider__stop"></div>
</div>
</div>
</div>
Вышеупомянутая структура выглядит много, но на самом деле большинство из них являются вспомогательными структурами.Вышеупомянутое поле ввода открывается опцией пользователя, а затем есть 2 кнопки, которые в основном используются для выбора диапазона.Как правило, только первая кнопка используется, и используется последняя точка прерывания.На самом деле она используется редко,выше<slider-button>
Это отдельный компонент, потому что этот компонент будет включать в себя много вещей, поэтому он выделен в отдельный компонент.
После простого анализа CSS из рисунка можно сделать вывод, что элемент div в синей части, скользивший по фону, должен быть абсолютно позиционирован, тогда кнопка ползунка также позиционируется абсолютно, а дорожка ползунка позиционируется относительно, на изменение ширины синей части Для изменения ее длины положение кнопки ползунка определяется слева, что в процентах
Анализ исходного кода кнопки-ползунка
Сначала посмотрите на использование этого компонента ползунка, самому базовому компоненту нужен только следующий код.
<el-slider v-model="value1"></el-slider>
value1 — это значение в данных, которое также изменяется при перемещении ползунка. Разберем сначала кнопочный компонент слайдера-кнопки, потому что он является ядром.Код этого компонента более 200 строк.Видно что он не простой.Субкомпонентов ровно столько.Структура html составляет
<template>
<div
class="el-slider__button-wrapper"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
@mousedown="onButtonDown"
@touchstart="onButtonDown"
:class="{ 'hover': hovering, 'dragging': dragging }"
:style="wrapperStyle"
ref="button"
tabindex="0"
@focus="handleMouseEnter"
@blur="handleMouseLeave"
@keydown.left="onLeftKeyDown"
@keydown.right="onRightKeyDown"
@keydown.down.prevent="onLeftKeyDown"
@keydown.up.prevent="onRightKeyDown"
>
<el-tooltip
placement="top"
ref="tooltip"
:popper-class="tooltipClass"
:disabled="!showTooltip">
<span slot="content">{{ formatValue }}</span>
<div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
</el-tooltip>
</div>
</template>
Это оболочка с div, вложенным в основную часть кнопки, самый внутренний div — это кнопка, которую мы видим, а внешний div — это более крупный div, который используется для реагирования на события щелчка и т. д. Сначала взгляните на @mouseenter и @mouseleave Функции обработки, соответствующие этим двум методам, используются для обработки того, движется ли мышь к кнопке для отображения всплывающей подсказки или нет.
handleMouseEnter() {
this.hovering = true;
this.displayTooltip();
},
handleMouseLeave() {
this.hovering = false;
this.hideTooltip();
},
Далее @ Mousedown = "Onbuttondondown" и @ touchstart = "onbuttondonk" - логика для обработки печати мыши и мобильного клемма, потому что кнопка перетаскивания - сначала нажмите кнопку, затем переместить мышь, чтобы перетащить, и, наконец, поднять это вверх. Запустите мышь, код onbuttondown выглядит следующим образом
onButtonDown(event) {
if (this.disabled) return;
event.preventDefault();
this.onDragStart(event);
window.addEventListener('mousemove', this.onDragging);
window.addEventListener('touchmove', this.onDragging);
window.addEventListener('mouseup', this.onDragEnd);
window.addEventListener('touchend', this.onDragEnd);
window.addEventListener('contextmenu', this.onDragEnd);
},
Во-первых, если компонент отключен, он будет возвращаться напрямую, затем preventDefault предотвратит срабатывание события по умолчанию, но почему вы хотите дать этой кнопке preventDefautl??Это просто обычный div.Странно, он обрабатывается на мобильная сторона? третье предложениеthis.onDragStart(event)
onDragStart(event) {
this.dragging = true;
this.isClick = true;
if (event.type === 'touchstart') {
event.clientY = event.touches[0].clientY;
event.clientX = event.touches[0].clientX;
}
if (this.vertical) {
this.startY = event.clientY;
} else {
this.startX = event.clientX;
}
this.startPosition = parseFloat(this.currentPosition);
this.newPosition = this.startPosition;
},
如上图,注意clientX和offsetX的区别,offsetX指的是点击点距离点击元素的左侧的距离。这里为啥要获得clientX并保存在startX中呢,是因为滑动滑块最后抬起鼠标时,需要计算抬起鼠标时的clientX的值和startX之间的差,这个差就是x轴移动的距离。 потомthis.startPosition = parseFloat(this.currentPosition)
CurrentPosition — вычисляемый атрибут от StartPosition до StartPosition.
currentPosition() {
return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`;
},
Приведенный выше код показывает, что currentPostion — это процент, а this.value внутри — это значение в v-модели компонента, которое является firstValue в родительском компоненте.Это firstValue передается пользователем в v-модель компонента компонент ползунка. Здесь немного запутанно. Короче говоря, данные, которые пользователь изначально передал в компонент ползунка, будут отражены здесь, а затем присвоить currentPostion начальное значение. Давайте посмотрим на логику в процессе перетаскивания мышью.
onDragging(event) {
if (this.dragging) {
this.isClick = false;
this.displayTooltip();
this.$parent.resetSize();
let diff = 0;
if (event.type === 'touchmove') {
event.clientY = event.touches[0].clientY;
event.clientX = event.touches[0].clientX;
}
if (this.vertical) {
this.currentY = event.clientY;
diff = (this.startY - this.currentY) / this.$parent.sliderSize * 100;
} else {
this.currentX = event.clientX;
diff = (this.currentX - this.startX) / this.$parent.sliderSize * 100;
}
this.newPosition = this.startPosition + diff;
this.setPosition(this.newPosition);
}
Сначала вы должны решить, находится ли он в состоянии перетаскивания, если нет, то ничего не делать, затемthis.isClick = false
Запишите флаг того, является ли это операцией щелчка, как false, указывая, что после начала перетаскивания это не операция щелчка. следующийthis.displayTooltip()
Используется для отображения всплывающей подсказки.this.$parent.resetSize()
Вызывается метод resetSize родительского компонента.Родительским компонентом является компонент слайдера.Этот метод сброса используется для вычисления ширины родительского компонента.
resetSize() {
if (this.$refs.slider) {
this.sliderSize = this.$refs.slider[`client${ this.vertical ? 'Height' : 'Width' }`];
}
},
this.$refs.slider
Получите элемент dom дорожки ползунка, а затем следуйте[`client${ this.vertical ? 'Height' : 'Width' }`]
Получите ширину или высоту своей клиентской области, clientWidth представляет внутреннюю ширину элемента, включая ширину, отступы, исключая границу и ширину полосы прокрутки ·
Затем объявляется переменная diff, diff обновляется ниже
this.currentX = event.clientX;
diff = (this.currentX - this.startX) / this.$parent.sliderSize * 100;
Diff рассчитывается по процентам движений мыши, учитывается ползунду, внимание может быть отрицательным, здесь использовали слайдовые. Позади а.this.newPosition = this.startPosition + diff
Он объявляет новую позицию (процент) кнопки ползунка, которая является начальной позицией плюс diff, что легко понять, и это значение может быть меньше или больше 100, и, наконец, вызывает setPostion для обновления позиции. Следовательно, процесс перетаскивания ползунка заключается в непрерывном получении самой последней позиции и выполнении операции обновления позиции.
Давайте посмотрим, что делает setPostion
setPosition(newPosition) {
if (newPosition === null || isNaN(newPosition)) return;
if (newPosition < 0) {
newPosition = 0;
} else if (newPosition > 100) {
newPosition = 100;
}
const lengthPerStep = 100 / ((this.max - this.min) / this.step);
const steps = Math.round(newPosition / lengthPerStep);
let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
value = parseFloat(value.toFixed(this.precision));
this.$emit('input', value);
this.$nextTick(() => {
this.$refs.tooltip && this.$refs.tooltip.updatePopper();
});
if (!this.dragging && this.value !== this.oldValue) {
this.oldValue = this.value;
}
}
Первый if указывает, что если newPosition не число, то оно не будет обработано, то при каких обстоятельствах newPosition не будет числом? Посмотрев на это, может оказаться, что пользователь может установить высоту дорожки ползунка в вертикальном режиме.Если значение, установленное в это время, неверно, может возникнуть нецифровая ситуация.
Второй параметр if else указывает, что значение newPosition может находиться только в диапазоне от 0 до 100. Когда мышь перетаскивается влево или вправо, появляется значение newPosition100. потомconst lengthPerStep = 100 / ((this.max - this.min) / this.step)
Вычисляется процент длины дорожки ползунка, соответствующий каждому шагу, где max и min — максимальное и минимальное значения компонента ползунка, а ползунок поровну делится на 100 длин.const steps = Math.round(newPosition / lengthPerStep)
Общее количество необходимых шагов рассчитывается и округляется в меньшую сторону. потомlet value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min
Значение конечного ползунка рассчитывается в одном предложении, и тогда значение передается на родительский компонент через EMIT, а затем родительский компонент продолжает выделять значение для родительского компонента составляющей ползунка, тем самым обновляя V-модель Пропущенные пользователем. Значение, следующее является NextTick, поскольку значение изменяется, всплывающая подсказка будет обновляться, то NextTick используется для обеспечения того, чтобы приобретенные данные были после обновления DOM
:style="wrapperStyle"
wrapperStyle() {
return this.vertical ? { bottom: this.currentPosition } : { left: this.currentPosition };
}
currentPosition() {
return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`;
},
<slider-button>
//slider组件的代码
<slider-button
:vertical="vertical"
v-model="firstValue"
:tooltip-class="tooltipClass"
ref="button1">
</slider-button>
//slider组件的代码
watch: {
value(val, oldVal) {
if (this.dragging ||
Array.isArray(val) &&
Array.isArray(oldVal) &&
val.every((item, index) => item === oldVal[index])) {
return;
}
this.setValues();
},
<div
class="el-slider__button-wrapper"
...
tabindex="0"
@focus="handleMouseEnter"
@blur="handleMouseLeave"
@keydown.left="onLeftKeyDown"
@keydown.right="onRightKeyDown"
@keydown.down.prevent="onLeftKeyDown"
@keydown.up.prevent="onRightKeyDown"
>
Здесь в основном устанавливается атрибут tabindex. Это означает, что к div можно получить доступ с помощью клавиши табуляции в конце. Таким образом, ползунком также можно управлять с помощью клавиш вверх, вниз, влево и вправо на клавиатуре. Обратите внимание, что методы фокуса и размытия существуют только в атрибуте tabindex и не являются - 1 раз для срабатывания (запускается вкладкой), посмотрите код onLeftKeyDown
onRightKeyDown() {
if (this.disabled) return;
this.newPosition = parseFloat(this.currentPosition) + this.step / (this.max - this.min) * 100;
this.setPosition(this.newPosition);
},
Значение делает ползунковый узел нажатым на уменьшенную клавиатуру влево с длинной длиной шага,this.step / (this.max - this.min) * 100
Рассчитывается процент длины ползунка (0-100 целых чисел), то я слышал обновление установки
Анализ кода дорожки слайдера
Когда пользователь щелкает дорожку ползунка, кнопку ползунка можно переместить в указанное положение, что требует привязки события щелчка к дорожке ползунка.
<div class="el-slider__runway"
:class="{ 'show-input': showInput, 'disabled': sliderDisabled }"
:style="runwayStyle"
@click="onSliderClick"
ref="slider">
Введите метод onSliderClick ниже
onSliderClick(event) {
if (this.sliderDisabled || this.dragging) return;
this.resetSize();
if (this.vertical) {
const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom;
this.setPosition((sliderOffsetBottom - event.clientY) / this.sliderSize * 100);
} else {
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
this.setPosition((event.clientX - sliderOffsetLeft) / this.sliderSize * 100);
}
this.emitChange();
},
Во-первых, определите ли отключенные или в сопротивлении, если он возвращается напрямую. Это значение. Значение поставляется путем перетаскивания кнопки слайзера внутри подкомпонентов в родительский компонент, когда вы нажимаете кнопку SLIDER, CLICK Event будет пузым к слайдере, поэтому необходимо судить. Затем вычисляйте длину дорожки ползунка (клиент, пиксели), следующее направление, определяющее, если на другой компонент делится на вертикальный и горизонтальный, если он горизонтальный, черезgetBoundingClientRect().left
Получите с левой стороны ползунковую дорожку из клиентской области, затемevent.clientX - sliderOffsetLeft
setPosition(percent) {
const targetValue = this.min + percent * (this.max - this.min) / 100;
if (!this.range) {
this.$refs.button1.setPosition(percent);
return;
}
let button;
if (Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)) {
button = this.firstValue < this.secondValue ? 'button1' : 'button2';
} else {
button = this.firstValue > this.secondValue ? 'button1' : 'button2';
}
this.$refs[button].setPosition(percent);
},
Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)
Чтобы убедиться. Тогда в нем есть тернарный оператор,button = this.firstValue < this.secondValue ? 'button1' : 'button2'
Это очень странно, firstValue и secondValue относятся к соответствующим значениям двух кнопок, которые привязаны к кнопке 1 и кнопке 2 соответственно.В начальном состоянии firstValue соответствует меньшему значению диапазона, переданного пользователем, и secondValue — большее значение.
Обратите внимание, что вы можете перетаскивать кнопку 1 первого значения слева до упора вправо, пока она не станет больше, чем кнопка 2 второго значения справа. В это время, если вы снова щелкнете область с зеленой стрелкой, движущаяся кнопка должна быть кнопкой 2 слева, иначе возникнет ошибка. Наоборот, переместите кнопку 1. Так что этот тернарный оператор не может быть меньше! Наконец, обновите позицию, вызвав setPosition дочернего компонента.