#javascript #css #vue.js #flexbox
#javascript #css #vue.js #flexbox
Вопрос:
Я пытаюсь разработать компонент, в котором вы могли бы изменять пропорции ширины двух блоков, перемещая ползунок влево и вправо:
codpen и демо:
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 200px;
width: -webkit-calc(50% - 5px);
width: -moz-calc(50% - 5px);
width: calc(50% - 5px);
}
.block-1 {
background-color: red;
}
.block-2 {
background-color: green;
}
.slider {
line-height: 100%;
width: 10px;
background-color: #dee2e6;
border: none;
cursor: e-resize;
}
<div id="app">
<div class="outer">
<div class="block block-1">
Block 1
</div>
<div class="slider">
S<br>l<br>i<br>d<br>e<br>r
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
Я попытался использовать draggable-vue-directive
и изменить ширину блоков в зависимости от положения ползунка.
Однако это сработало не очень хорошо, так как draggable-vue-directive
установите ползунок на position:fixed
, что, в свою очередь, испортило выравнивание блоков.
Как я могу сделать .slider
блок горизонтально перетаскиваемым без настройки position:fixed
?
Как правильно изменять размер Block1
и Block2
при перемещении ползунка?
Примечание: я не использую jQuery
Комментарии:
1. Существует множество способов создания ползунка. Чего вы пытаетесь достичь с его помощью? Ваша текущая реализация просто изменит содержимое в каждой области по мере их расширения / сужения. Это цель?
2. @BryceHowitson да, в принципе, если вы сдвинете ползунок
div
вправо, то онBlock 1
должен быть шире, аBlock 2
— уже
Ответ №1:
Вы можете настроить свой flexbox вместе с resize
— недостатком является то, что ползунок не очень настраивается:
- добавить
resize: horizontal
к одному из элементов flex - добавить
flex: 1
к другому гибкому элементу (чтобы этот гибкий элемент автоматически корректировался в ответ на изменение ширины другого гибкого элемента по мере его изменения)
Смотрите демонстрацию ниже:
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 100px;
width: 50%; /* 50% would suffice*/
}
.block-1 {
background-color: red;
resize: horizontal; /* resize horizontal */
overflow: hidden; /* resize works for overflow other than visible */
}
.block-2 {
background-color: green;
flex: 1; /* adjust automatically */
}
<div id="app">
<div class="outer">
<div class="block block-1">
Block 1
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
Поэтому мы будем использовать vanilla JS вместо решения resize, описанного выше:
- используйте
mousedown
прослушиватель, который регистрируетmousemove
прослушиватель, который обновляетblock-1
ширину (и сбрасываетmouseup
событие) - также подумайте о том,
min-width: 0
чтобы переопределитьmin-width: auto
block-2
элемент
Смотрите демонстрацию ниже:
let block = document.querySelector(".block-1"),
slider = document.querySelector(".slider");
slider.onmousedown = function dragMouseDown(e) {
let dragX = e.clientX;
document.onmousemove = function onMouseMove(e) {
block.style.width = block.offsetWidth e.clientX - dragX "px";
dragX = e.clientX;
}
// remove mouse-move listener on mouse-up
document.onmouseup = () => document.onmousemove = document.onmouseup = null;
}
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 100px;
width: 50%; /* 50% would suffice*/
}
.block-1 {
background-color: red;
}
.block-2 {
background-color: green;
flex: 1; /* adjust automatically */
min-width: 0; /* allow flexing beyond auto width */
overflow: hidden; /* hide overflow on small width */
}
.slider {
line-height: 100%;
width: 10px;
background-color: #dee2e6;
border: none;
cursor: col-resize;
user-select: none; /* disable selection */
text-align: center;
}
<div id="app">
<div class="outer">
<div class="block block-1">
Block 1
</div>
<div class="slider">
S<br>l<br>i<br>d<br>e<br>r
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
Решение
Вы можете легко адаптировать вышеуказанное в Vue, не используя для этого какие-либо пользовательские плагины Vue — изменения:
-
@mousedown
прослушиватель,slider
который запускает ползунок -
использование
refs
для обновления шириныblock-1
Смотрите демонстрацию ниже:
new Vue({
el: '#app',
data: {
block1W: '50%'
},
methods: {
drag: function(e) {
let dragX = e.clientX;
let block = this.$refs.block1;
document.onmousemove = function onMouseMove(e) {
block.style.width = block.offsetWidth e.clientX - dragX "px";
dragX = e.clientX;
}
// remove mouse-move listener on mouse-up
document.onmouseup = () => document.onmousemove = document.onmouseup = null;
}
}
});
.outer {
display: flex;
flex-direction: row;
}
.block {
height: 100px;
width: 50%; /* 50% would suffice*/
}
.block-1 {
background-color: red;
}
.block-2 {
background-color: green;
flex: 1; /* adjust automatically */
min-width: 0; /* allow flexing beyond auto width */
overflow: hidden; /* hide overflow on small width */
}
.slider {
line-height: 100%;
width: 10px;
background-color: #dee2e6;
border: none;
cursor: col-resize;
user-select: none; /* disable selection */
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="outer">
<div class="block block-1" ref="block1" :style="{'width': block1W}">
Block 1
</div>
<div class="slider" @mousedown="drag">
S<br>l<br>i<br>d<br>e<br>r
</div>
<div class="block block-2">
Block 2
</div>
</div>
</div>
Комментарии:
1. Действительно отличный ответ. Также довольно приятно, что решение сначала основано на «ванильном» JS, а затем «перенесено» в решение, ориентированное на Vue. Лично я бы просто использовал
addEventListener
вместо использования свойств «вкл. …» в JS, но здесь это не важно для общей функциональности. Хорошая работа. Большое вам спасибо.