Рад приветствовать тебя,%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) };
Достоинства данного подхода:
Быстро
Дёшево
Не нужно библиотек или фреймворков
Недостатки
Нужно скрывать все элементы интерфейса
Трудность создания фона
Дополнительная нагрузка (тут уже у меня сработал ген осторожности)
В комментариях вы сами найдёте другие положительные и отрицательные стороны данного решения, ну а ниже вы можете уже увидеть результат работы нашего кода:
