Рад приветствовать тебя,%username%. В этой статье я поделюсь тем, как создать вращающуюся ручку (в зарубежной литературе именуемой Knob). Далее по тексту я её буду называть «кноб». Перейдём к сути вопроса.
Что меня сподвигло на написание данной статьи, или как я докатился до жизни такой
Когда-то давно, будучи ещё совсем зелёным и несмышлёным кодером, мне взбрело в голову сделать своё приложение. Моих знаний хватало на какой‑нибудь «Hello, World!», но кое‑что выдать я мог. И на тот момент мне позарез нужно было реализовать кноб — тот самый круглый регулятор, который можно крутить и он будет менять значения. И как любой уважающий себя программист, я сразу же полез гуглить стековерфлоу и прочее непотребство, дабы найти тот самый рецепт хлеба сладкого ответ на вопрос, который так долго меня волновал. Однако перешерстив весь интернет, ничего путёвого я не нашёл. Расстроившись, я забил на эту идею. Но какие‑то угольки веры в то, что у меня получится это сделать, тлели в моей охолодевшей к этому миру душонке. И вот на днях, когда я начал работать над своим проектом (небольшая программка, если она найдёт отклик в народных массах, то и по ней напишу парочку руководств, но пока что спустимся на землю), мне опять ударила моча в голову с этими кнобами. Тем более, что все предпосылки ведут именно к этому. Они нужны в интерфейсе. Забравшись в православный яндекс и одновременно в бездуховный гугл, я опять наткнулся на мель отсутствия нужной мне информации. Были только решения с какими‑то библиотеками и интересными историями на jQuery. Плюнув на всё и засучив рукава, я вдруг осознал, что этот мир нуждается в моём компетентном и всем так очень нужном мнении (спойлер: нет). Тогда я принялся за gehirnsturm и молниеносно придумал пару решений. Первое заключалось в SVG на основе path, но оно показалось мне чересчур геморройным. Там много JS надо написать, но не надо никаких стилей зато. И вот второе решение отчасти продолжает идею первого, но с некоторыми отличиями: оно немножко проще в том плане, что начальное, конечное и текущее значение, а также шаг уже вмонтированы в него, что существенно сокращало труды мои праведные. Но, как гласит закон сохранения энергии, энергия не приходит и не уходит, она лишь изменяется. Поправьте, если не так. А это значит, что если где‑то стало проще, то где‑то стало сложнее.
И вот тут я плавно перехожу к сути дела и раскрываю тему полностью
Наша реализация будет самой бюджетной, но самой выполнимой. Для начала создаём файлы index.html, styles.css и main.js соответственно. Желательно в одной папочке. Вот код файла index.html
:
<!DOCTYPE html>
<html>
<head>
<!-- Подключаем файл с CSS -->
<link href="styles.css" rel="stylesheet"/>
</head>
<body>
<!-- Берём рандомные значения и задаём:
идентификатор элемента (id),
тип (type)
максимальное (max) и
минимальное (min) значения,
шаг (step) и
непосредственно значение (value) -->
<input type="range" id="Knob" max="100" min="0" step="0.01" value="14.8"/>
<!-- Подключаем файл с JavaScript -->
<script src="main.js"></script>
</body>
</html>
Код файла styles.css
:
/* Всё, что нам необходимо, делаем здесь */
input[type="range"]
{
appearance: none; /* Убираем дефолтный стиль */
border-radius: 100%; /* Округляем элемент */
/* Задаём ширину (width) и высоту (height) */
height: 64px;
width: 64px;
}
/* А здесь мы отключаем дорожку */
input[type="range"]::-webkit-slider-runnable-track
{
appearance: none; /* Убираем дефолтный стиль */
height: 64px; /* Задаём высоту, равную элементу */
opacity: 0; /* Скрываем элемент, убрав непрозрачность */
width: 64px; /* Задаём ширину, равную элементу */
}
/* И убираем сам ползунок */
input[type="range"]::-webkit-slder-thumb
{
appearance: none; /* Убираем дефолтный стиль */
height: 64px; /* Задаём высоту, равную элементу */
opacity: 0; /* Скрываем элемент, убрав непрозрачность */
width: 64px; /* Задаём ширину, равную элементу */
}
/* Об элементе input с типом range можете ознакомиться в документациях Google, Mozilla и прочих */
Код файла main.js
:
// Создаём функцию, которая вычислит угол поворота
function countAngle(e) {
var fieldOfView = 240; // Т. н. "поле зрения"
var half_of_fov = fieldOfView / 2; // Половина от поля зрения
/* Нормализуем значение:
* Делим разность значения с минимумом на разность максимума с минимумом
*/
var normalized = (e.target.value - e.target.min) / (e.target.max - e.target.min);
var result = (normalized * fieldOfView) - half_of_fov; // Масштабируем значение и смещаем на половину
return result;
}
var elem = document.querySelector("#Knob"); // Получаем элемент по идентификатору
elem.style.background = "conic-gradient(from " + ((((elem.value - elem.min) / (elem.max - elem.min)) * 240) - 120) + "deg, #008081, #FFFFFF)";
// Устанавливаем в качестве фона конический градиент со значением вычисленным вручную, потому что функция принимает в качестве аргумента событие элемента. Здесь же мы получаем все значения напрямую и вычисляем их
// Создаём событие, реагирующее на ввод наших данных
elem.oninput = function (e) {
// e.target - это наш элемент, к которому мы прикрутили стиль
e.target.style.background = "conic-gradient(from " + countAngle(e) + "deg, #008081, #FFFFFF)"; // Обновляем значение
// Важно! Если мы вместо знака "=" укажем "+=", то у нас получится не то, потому что мы будем к текущему значению прибавлять новое значение и получится к примеру вот так:
// conic-gradient(from 0deg, #008081, #FFFFFF)conic-gradient(from 1deg, #008081, #FFFFFF)
// А нам нужно, чтобы значение было таким:
// Сначала: conic-gradient(from 0deg, #008081, #FFFFFF)
// А потом только conic-gradient(from 1deg, #008081, #FFFFFF)
};
Достоинства данного подхода:
Быстро
Дёшево
Не нужно библиотек или фреймворков
Недостатки
Нужно скрывать все элементы интерфейса
Трудность создания фона
Дополнительная нагрузка (тут уже у меня сработал ген осторожности)
В комментариях вы сами найдёте другие положительные и отрицательные стороны данного решения, ну а ниже вы можете уже увидеть результат работы нашего кода: