Рад приветствовать тебя,%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)
};

Достоинства данного подхода:

  • Быстро

  • Дёшево

  • Не нужно библиотек или фреймворков

Недостатки

  • Нужно скрывать все элементы интерфейса

  • Трудность создания фона

  • Дополнительная нагрузка (тут уже у меня сработал ген осторожности)

В комментариях вы сами найдёте другие положительные и отрицательные стороны данного решения, ну а ниже вы можете уже увидеть результат работы нашего кода:

Комментарии (0)