renovations
This commit is contained in:
parent
a62682e188
commit
35081a779a
115 changed files with 439 additions and 325 deletions
BIN
2-ui/5-widgets/5-custom-events/4-slider-events/slider.png
Normal file
BIN
2-ui/5-widgets/5-custom-events/4-slider-events/slider.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
34
2-ui/5-widgets/5-custom-events/4-slider-events/solution.md
Normal file
34
2-ui/5-widgets/5-custom-events/4-slider-events/solution.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
|
||||
Для решения этой задачи достаточно создать две функции: `valueToPosition` будет получать по значению положение бегунка, а `positionToValue` -- наоборот, транслировать текущую координату бегунка в значение.
|
||||
|
||||
Как сопоставить позицию слайдера и значение?
|
||||
|
||||
Для этого посмотрим крайние значения слайдера. Допустим, размер бегунка `10px`.
|
||||
|
||||
Раз центр соответствует значению, то крайнее левое значение будет соответствовать центру на `5px`, а крайнее правой -- центру на `5px` от правой границы:
|
||||
|
||||
<img src="slider.png">
|
||||
|
||||
Соответственно, ширина области изменения будет `sliderElem.clientWidth - thumbElem.clientWidth`. Далее её можно уже поделить на части, количество пикселей на значение будет:
|
||||
|
||||
```js
|
||||
pixelsPerValue = (sliderElem.clientWidth-thumbElem.clientWidth) / max;
|
||||
```
|
||||
|
||||
Может получиться так, что это значение будет дробным, меньше единицы. Например, если `max = 1000`, а ширина слайдера `110` (пробег 100), то будет `0.1` пикселя на значение.
|
||||
|
||||
Используя `pixelsPerValue` мы сможем переводить позицию бегунка в значение и обратно.
|
||||
|
||||
Крайнее левое значение `thumbElem.style.left` равно нулю, крайнее правой -- как раз ширине доступной области `sliderElem.clientWidth - thumbElem.clientWidth`. Поэтому можно получить значение слайдера, поделив его на `pixelsPerValue`:
|
||||
|
||||
```js
|
||||
function positionToValue(left) {
|
||||
return Math.round( left / pixelsPerValue);
|
||||
}
|
||||
|
||||
function valueToPosition(value) {
|
||||
return pixelsPerValue * value;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://cdn.polyfill.io/v1/polyfill.js?features=CustomEvents,Element.prototype.closest"></script>
|
||||
<link rel="stylesheet" href="slider.css">
|
||||
<script src="slider.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="slider" class="slider">
|
||||
<div class="thumb"></div>
|
||||
</div>
|
||||
|
||||
|
||||
Slide:<span id="slide"> </span>
|
||||
Change:<span id="change"> </span>
|
||||
|
||||
<button onclick="slider.setValue(50)">slider.setValue(50)</button>
|
||||
|
||||
<script>
|
||||
|
||||
var sliderElem = document.getElementById('slider');
|
||||
|
||||
var slider = new Slider({
|
||||
elem: sliderElem,
|
||||
max: 100
|
||||
});
|
||||
|
||||
sliderElem.addEventListener('slide', function(event) {
|
||||
document.getElementById('slide').innerHTML = event.detail;
|
||||
});
|
||||
|
||||
sliderElem.addEventListener('change', function(event) {
|
||||
document.getElementById('change').innerHTML = event.detail;
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
.slider {
|
||||
margin: 5px;
|
||||
width: 310px;
|
||||
height: 15px;
|
||||
border-radius: 5px;
|
||||
background: #E0E0E0;
|
||||
background: -moz-linear-gradient(left top, #E0E0E0, #EEEEEE) repeat scroll 0 0 transparent;
|
||||
background: -webkit-gradient(linear, left top, right bottom, from(#E0E0E0), to(#EEEEEE));
|
||||
background: linear-gradient(left top, #E0E0E0, #EEEEEE);
|
||||
}
|
||||
.thumb {
|
||||
position: relative;
|
||||
top: -5px;
|
||||
width: 10px;
|
||||
height: 25px;
|
||||
border-radius: 3px;
|
||||
background: blue;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
function Slider(options) {
|
||||
var elem = options.elem;
|
||||
|
||||
var thumbElem = elem.querySelector('.thumb');
|
||||
|
||||
var max = options.max || 100;
|
||||
var sliderCoords, thumbCoords, shiftX, shiftY;
|
||||
|
||||
// [<*>----------------]
|
||||
// |...............|
|
||||
// first last
|
||||
var pixelsPerValue = (elem.clientWidth - thumbElem.clientWidth) / max;
|
||||
|
||||
elem.ondragstart = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
elem.onmousedown = function(event) {
|
||||
if (event.target.closest('.thumb')) {
|
||||
startDrag(event.clientX, event.clientY);
|
||||
return false; // disable selection start (cursor change)
|
||||
}
|
||||
}
|
||||
|
||||
function startDrag(startClientX, startClientY) {
|
||||
thumbCoords = thumbElem.getBoundingClientRect();
|
||||
shiftX = startClientX - thumbCoords.left;
|
||||
shiftY = startClientY - thumbCoords.top;
|
||||
|
||||
sliderCoords = elem.getBoundingClientRect();
|
||||
|
||||
document.addEventListener('mousemove', onDocumentMouseMove);
|
||||
document.addEventListener('mouseup', onDocumentMouseUp);
|
||||
}
|
||||
|
||||
function moveTo(clientX) {
|
||||
// вычесть координату родителя, т.к. position: relative
|
||||
var newLeft = clientX - shiftX - sliderCoords.left;
|
||||
|
||||
// курсор ушёл вне слайдера
|
||||
if(newLeft < 0) {
|
||||
newLeft = 0;
|
||||
}
|
||||
var rightEdge = elem.offsetWidth - thumbElem.offsetWidth;
|
||||
if(newLeft > rightEdge) {
|
||||
newLeft = rightEdge;
|
||||
}
|
||||
|
||||
thumbElem.style.left = newLeft + 'px';
|
||||
|
||||
elem.dispatchEvent(new CustomEvent('slide', {
|
||||
bubbles: true,
|
||||
detail: positionToValue(newLeft)
|
||||
}));
|
||||
}
|
||||
|
||||
function valueToPosition(value) {
|
||||
return pixelsPerValue * value;
|
||||
}
|
||||
|
||||
function positionToValue(left) {
|
||||
return Math.round( left / pixelsPerValue);
|
||||
}
|
||||
|
||||
function onDocumentMouseMove(e) {
|
||||
moveTo(e.clientX);
|
||||
}
|
||||
|
||||
function onDocumentMouseUp() {
|
||||
endDrag();
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
document.removeEventListener('mousemove', onDocumentMouseMove);
|
||||
document.removeEventListener('mouseup', onDocumentMouseUp);
|
||||
|
||||
elem.dispatchEvent(new CustomEvent('change', {
|
||||
bubbles: true,
|
||||
detail: positionToValue(parseInt(thumbElem.style.left))
|
||||
}));
|
||||
}
|
||||
|
||||
function setValue(value) {
|
||||
thumbElem.style.left = valueToPosition(value) + 'px';
|
||||
}
|
||||
|
||||
this.setValue = setValue;
|
||||
}
|
46
2-ui/5-widgets/5-custom-events/4-slider-events/task.md
Normal file
46
2-ui/5-widgets/5-custom-events/4-slider-events/task.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Слайдер с событиями
|
||||
|
||||
[importance 5]
|
||||
|
||||
На основе слайдера из задачи [](/task/slider-widget) создайте графический компонент, который умеет возвращать/получать значение.
|
||||
|
||||
Синтаксис:
|
||||
|
||||
```js
|
||||
var slider = new Slider({
|
||||
elem: document.getElementById('slider'),
|
||||
max: 100 // слайдер на самой правой позиции соответствует 100
|
||||
});
|
||||
```
|
||||
|
||||
Метод `setValue` устанавливает значение:
|
||||
|
||||
```js
|
||||
slider.setValue(50);
|
||||
```
|
||||
|
||||
У слайдера должно быть два события: `slide` при каждом передвижении и `change` при отпускании мыши (установке значения).
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
var sliderElem = document.getElementById('slider');
|
||||
|
||||
sliderElem.addEventListener('slide', function(event) {
|
||||
document.getElementById('slide').innerHTML = event.detail;
|
||||
});
|
||||
|
||||
sliderElem.addEventListener('change', function(event) {
|
||||
document.getElementById('change').innerHTML = event.detail;
|
||||
});
|
||||
```
|
||||
|
||||
В действии:
|
||||
[iframe src="solution" height="80"]
|
||||
|
||||
<ul>
|
||||
<li>Ширина/высота слайдера может быть любой, JS-код это должен учитывать.</li>
|
||||
<li>Центр бегунка должен располагаться в точности над выбранным значением. Например, он должен быть в центре для 50 при `max=100`.</li>
|
||||
</ul>
|
||||
|
||||
Исходный документ -- возьмите решение задачи [](/task/slider-widget).
|
Loading…
Add table
Add a link
Reference in a new issue