Калькулятор Самозамеса
Пишем калькулятор самазамеса жидкости
Онлайн версия калькулятора самозамеса
Калькулятор самозамеса и намотки на Android
Здравствуйте, дорогие читатели!
ВНИМАНИЕ: Статья рассчитана на новичка (или не новичка), который что-то читал о самозамесе, хотел перейти на него, но не знает в каких пропорциях ему всё смешивать.
Бóльшая часть новичков в парении задумывается о переходе с готовой жидкости на самозамес.
Рассмотрим тот случай, когда новичок покупает глицерин (VG), пропиленгликоль (PG), дистиллированную воду и никотиновую основу. Купив всё это появляются вопросы: «А как всё это смешивать, если в основе 40% глицерина, а я хочу на выходе 80% глицерина, сохранив при этом объём 100мл?», «В основе 12мг/мл никотина, а я хочу 3мг/мл. Как мне этого добиться?». Некоторые, благодаря знаниям математики, начинают рассчитывать пропорции смешивания составляющих итоговой жидкости. Кто-то забивает на это дело и начинает снова покупать дорогую готовую жидкость. А кто-то находит в интернете калькулятор смешивания жидкости, благодаря которому и рассчитывает пропорции.
Калькулятор это хорошо, но человек же так и не узнает, почему нужно смешивать именно в таких пропорциях.
Я познакомлю Вас с алгоритмом расчёта пропорций смешивания жидкости. Давайте сделаем всё интереснее? Согласны? Тогда напишем калькулятор самозамеса, который возможно вам пригодится в дальнейшем.
Для максимальной простоты и наглядности будем использовать HTML (язык разметки), CSS (язык описания внешнего вида документа) и JavaScript (сценарный язык программирования). Не пугайтесь, тут ничего сложного нету.
Начать нам нужно с создания основного документа index.html. Вы можете создать такой файл в обычном блокноте, но при сохранении выбрать кодировку UTF-8 и формат файла html.

Создали? Хорошо, поехали дальше.
Создадим структуру документа:
<!DOCTYPE html>
<html>
<head>
<title>Калькулятор</title>
<meta charset="utf-8">
</head>
<body>
<div class="content">
</div>
</body>
</html>
Здесь мы указали какой тип будет у документа, какая кодировка, а также открыли блок с классом «content». Не будем углубляться в программирование, но скажу, что класс мы задали блоку для дальнейшего его визуального оформления.
Далее нам необходимо добавить в документ форму, в которой мы будем выбирать нужные нам значения для смешивания жидкости.
<form name="form">
<table>
<tr>
<td>Требуемое количество жидкости:
<td><input type="range" name="t_count" min="0" max="100" value="100" step="5" onchange="outputUpdate(name, value)" id="t_count" /></td>
<td><output for="t_count" id="t_count_id">100</output>мл</td>
</tr>
<tr>
<td>Требуемое содержание никотина: </td>
<td><input type="range" name="t_nic" min="0" max="100" value="3" step="3" onchange="outputUpdate(name, value)" id="t_nic" /></td>
<td><output for="t_nic" id="t_nic_id">3</output>мг/мл</td>
</tr>
<tr>
<td>Требуемое содержание PG: </td>
<td><input type="range" name="t_pg" min="0" max="100" value="30" step="5" onchange="outputUpdate(name, value)" id="t_pg" /></td>
<td><output for="t_pg" id="t_pg_id">30</output>%</td>
</tr>
<tr>
<td>Требуемое содержание VG: </td>
<td><input type="range" name="t_vg" min="0" max="100" value="70" step="5" onchange="outputUpdate(name, value)" id="t_vg" /></td>
<td><output for="t_vg" id="t_vg_id">70</output>%</td>
</tr>
<tr>
<td>Требуемое содержание H<sub>2</sub>O:</td>
<td><input type="range" name="t_h2o" min="0" max="100" value="0" step="5" onchange="outputUpdate(name, value)" id="t_h2o" /></td>
<td><output for="t_h2o" id="t_h2o_id">0</output>%</td>
</tr>
<tr>
<td>Содержание никотина в основе:</td>
<td><input type="range" name="o_nic" min="0" max="100" value="36" step="3" onchange="outputUpdate(name, value)" id="o_nic" /></td>
<td><output for="o_nic" id="o_nic_id">36</output>мг/мл</td>
</tr>
<tr>
<td>Содержание PG в основе:</td>
<td><input type="range" name="o_pg" min="0" max="100" value="55" step="5" onchange="outputUpdate(name, value)" id="o_pg" /></td>
<td><output for="o_pg" id="o_pg_id">55</output>%</td>
</tr>
<tr>
<td>Содержание VG в основе:</td>
<td><input type="range" name="o_vg" min="0" max="100" value="35" step="5" onchange="outputUpdate(name, value)" id="o_vg" /></td>
<td><output for="o_vg" id="o_vg_id">35</output>%</td>
</tr>
<tr>
<td>Содержание H<sub>2</sub>O в основе:</td>
<td><input type="range" name="o_h2o" min="0" max="100" value="10" step="5" onchange="outputUpdate(name, value)" id="o_h2o" /></td>
<td><output for="o_h2o" id="o_h2o_id">10</output>%</td>
</tr>
</table>
<div class="btn" onClick="res(this.form)">Рассчитать</div>
</form>
<div id="result"></div>
Здесь мы добавили форму, которая будет находиться в таблице. Тег "<table>" означает, что мы начинаем таблицу. Тег "<tr>" означает, что мы начинаем строку в таблице. Тег "<td>" означает, что мы создаём ячейку в строке таблицы. В ячейках у нас есть текст и поля формы (<input type=«range» name=«t_count» min=«0» max=«100» value=«100» step=«5» onchange=«outputUpdate(name, value)» id=«t_count» />). Давайте разберёмся с полем. в атрибуте «type» мы указали тип «range», что означает ползунок. Задали имя этому полю, в данном случае «t_count», т.е. нужный объём жидкости на выходе. Указали минимальное и максимальное значение ползунка, а также указали шаг, с которым он будет передвигаться. В атрибуте «value» указываем исходное значение, например, 100 (100мл жидкости). В событии «onchange» (с английского — изменение) мы передаём в функцию JavaScript имя и значение поля для того, чтобы выводить сбоку от поля текущее значение бегунка. Также у нас есть атрибут «id», в котором мы указываем уникальный идентификатор поля. Далее можно увидеть такой код "<output for=«t_count» id=«t_count_id»>100</output>". Сюда будет выводиться текущее значение поля с помощью языка JavaScript. Причём изменяться значение будет только при передвижении ползунка.
Можно заметить, что в коде, написанном выше, есть ещё пара строчек кода. Первая: "<div class=«btn» onClick=«res(this.form)»>Рассчитать</div>". Тут мы создали кнопку, с помощью которой начнётся расчёт пропорций. В событии «onClick» задали вызов функции res, если на кнопку было нажатие мыши. В функцию передали все элементы формы. Вторая строка: "<div id=«result»></div>". Тут мы создали блок, в который будем выводить результат расчёта пропорций.
Посмотрим на результат формы, открыв файл «index.html» в браузере.

Теперь перейдём к самому интересному, а именно к написанию алгоритма расчёта пропорций. Алгоритм у нас будет простой: считаем количество добавляемой никотиновой основы; считаем количество глицерина, пропиленгликоля и воды в основе; исходя из рассчитанных параметров в основе, считает количество добавляемого глицерина, пропиленгликоля и воды; далее выводим результат. Добавим JavaScript код в теге «head» в нашем документе. Код:
........
<head>
.......
<script type="text/javascript">
var vars = new Array();
function res(form1){
var inputs = form.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) vars[inputs[i].name] = parseFloat(inputs[i].value);
sum = vars["t_pg"] + vars["t_vg"] + vars["t_h2o"];
if(sum != 100) alert("Вы выбрали неверное сооношение PV/VG/H2O");
osnova = parseFloat(vars["t_nic"] / (vars["o_nic"] / vars["t_count"])).toFixed(2);
o_pg = osnova * (vars["o_pg"] / 100);
o_vg = osnova * (vars["o_vg"] / 100);
o_h2o = osnova * (vars["o_h2o"] / 100);
d_pg = parseFloat((vars["t_count"] * (vars["t_pg"] / 100)) - o_pg).toFixed(2);
d_vg = parseFloat((vars["t_count"] * (vars["t_vg"] / 100)) - o_vg).toFixed(2);
d_h2o = parseFloat((vars["t_count"] * (vars["t_h2o"] / 100)) - o_h2o).toFixed(2);
if(d_h2o < 0) d_h2o = 0;
count = parseFloat(osnova) + parseFloat(d_pg) + parseFloat(d_vg) + parseFloat(d_h2o);
result = "Добаить никотиновой основы: " + osnova + "мл<br />";
result += "Добаить PG: " + d_pg + "мл<br />";
result += "Добаить VG: " + d_vg + "мл<br />";
result += "Добаить H<sub>2</sub>O: " + d_h2o + "мл<br />";
result += "Результат: " + count + "мл.<br />";
if(count > vars["t_count"]) result += "Рузультат превышает требуемое количество жидкости. <br />Если вас это не устраивает, то измените параметры в форме, которые вы заполнили неверно!<br />Возможно это произошло из-за наличия воды в основе при требуемом отсутствии её в итоговой жидкости.<br />Если это так, то не переживайте и начинайте смешивать жидкость!<br />";
document.getElementById("result").innerHTML = result;
}
function outputUpdate(name, val) {
document.querySelector("#" + name + "_id").value = val;
}
</script>
</head>
.........
Теперь всё по порядку! Для начала создадим массив vars, который будет содержать в себе названия полей из формы и их значения. var vars = new Array()
Далее необходимо создать функцию res, которая будет принимать значения из формы. function res(form1){
В следующем шаге необходимо создать переменную «inputs», в которую поместим все поля из формы. Далее через цикл записываем в наш массив «vars» поля формы в виде: «поле[»имя поля"] = значение поля". for (var i = 0; i < inputs.length; i++) vars[inputs[i].name] = parseFloat(inputs[i].value);
Теперь начинается самое вкусное. Далее мы будет рассчитывать пропорции для смешивания жидкости. Сначала нужно проверить — а не ввели ли нам неверные значения в поле, например, могли ввести, что требуется получить на выходе 90% глицерина, 100% пропиленгликоля и 80% дистиллированной воды. Это в сумме составляет более 100% от общей жидкости, что не есть правильно. Поэтому мы сознаём переменную «sum», в которой храним сумму процентов. В следующей строке делаем проверку, описанную выше. if(sum != 100) alert(«Вы выбрали неверное соотношение PV/VG/H2O»). Тут написано: «если сумма не равна 100, то выводим сообщение с текстом „Вы выбрали неверное соотношение PV/VG/H2O“».
Далее рассчитываем количество добавляемой никотиновой жидкости. osnova = parseFloat(vars[«t_nic»] / (vars[«o_nic»] / vars[«t_count»])).toFixed(2). Тут простая формула: количество добавляемой никотиновой основы = требуемое количество никотина / (никотина в основе / требуемый объём жидкости). После расчёта никотиновой жидкости, нужно рассчитать количество глицерина, пропиленгликоля и воды в добавляемой основе. Для всех трёх параметров будет похожая формула: количество параметра в основе = количество добавляемой никотиновой основы / (содержание нужного параметра в основе / 100). Например, для воды в коде написано так: o_h2o = osnova * (vars[«o_h2o»] / 100).
Теперь посчитаем количество глицерина, пропиленгликоля и воды, которое необходимо добавить к полученному объёму никотиновой основы. Формула для каждого параметра: количество добавляемого параметра в никотиновую основу = (требуемый объём жидкости * (требуемое количество параметра в итоговой жидкости / 100)) — количество параметра в никотиновой основе. Пример кода для расчёта добавляемого глицерина d_vg = parseFloat((vars[«t_count»] * (vars[«t_vg»] / 100)) — o_vg).toFixed(2).
При выборе значений в форме может быть такое, что в требуемой жидкости мы не захотим видеть воду, а в основе она присутствует, следовательно, вода будет присутствовать и в конечной жидкости. В таком случае, при расчёте воды, переменная для воды в результате у нас будет иметь отрицательное значение. Чтобы от этого избавиться, нужно обнулить значение переменной с водой. Такой случай может случиться и с глицерином, и с пропиленгликолем, но я не стал добавлять проверки на это, т.к. на практике таких случаев может просто не быть. Главное, что я Вам сообщил об этом нюансе.
Считаем количество полученной жидкости и записываем его в переменную «count». Далее необходимо записать весь результат в переменную «result» и вывести его. Можно заметить, что, при формировании текста вывода, присутствует условие (if(count > vars[«t_count»]) result += "....."), которое означает, что если количество полученной жидкости больше требуемого количества жидкости, то выводим подсказку, в которой расписано с чем это может быть связано (например, с отсутствием воды в требуемой жидкости, но присутствием её в никотиновой основе). Далее просто выводим весь результат в блок с идентификатором «result».
Вы заметили, что в JS коде есть ещё одна функция «outputUpdate», которая принимает в качестве параметров имя и значение поля. Эта функция создана для того, чтобы при движении ползунка отображать текущее его значение.
Давайте протестируем получившийся код. Укажем в форме желаемое количество жидкости 100мл, желаемое содержание никотина 3мг/мл, желаемое содержание PG — 25%, желаемое содержание VG — 65%, желаемое содержание воды 10%. Также укажем параметры основы: содержание никотина в основе 36мг/мл, содержание PG в основе — 55%, содержание VG в основе — 35% и содержание воды в основе — 10%. Жмём на кнопку «Рассчитать» и получаем результат:

Работает, но выглядит не очень красиво. Теперь добавим код, представленный ниже, в тег «head» в нашем документе, который чуточку украсит это всё.
<style>
body{
background-color: #2b3944;
color: #fff;
font-size: 20px;
}
.content{
text-align: center;
}
#result{
text-shadow: 0 0 2px #00f;
}
.btn{
background: #fff;
display: inline-block;
padding: 5px 10px;
border-radius: 10px;
cursor: pointer;
color: #000;
font-weight: bolder;
}
.btn:hover{
background: #000;
color: #fff;
box-shadow: 0px 0px 10px #fff;
}
.btn:active{
opacity: 0.8;
}
table{
margin: 0 auto;
}
tr td:first-child{
text-align: right;
}
tr td:nth-child(2){
text-align: center;
}
tr td:nth-child(3){
text-align: left;
}
</style>
Получаем теперь такую картину:

Так намного лучше. Не правда ли? Правда.
Вот собственно мы и написали калькулятор самозамеса, а также разобрались в алгоритме его работы. Калькулятор очень простой и в нём есть что доделать, например, добавить некоторые проверки, добавить поля, в которых можно выбрать количество ароматизатора в жидкостях и т.д.
Калькулятор Вы можете использовать у себя на компьютере, на мобильном телефоне и на планшете. Он является кроссплатформенным и подстраивается под различные разрешения экрана. Всё, что Вам нужно, так это файл index.html
Представляю полный код калькулятора:
<!DOCTYPE html>
<html>
<head>
<title>Калькулятор</title>
<meta charset="utf-8">
<style>
body{
background-color: #2b3944;
color: #fff;
font-size: 20px;
}
.content{
text-align: center;
}
#result{
text-shadow: 0 0 2px #00f;
}
.btn{
background: #fff;
display: inline-block;
padding: 5px 10px;
border-radius: 10px;
cursor: pointer;
color: #000;
font-weight: bolder;
}
.btn:hover{
background: #000;
color: #fff;
box-shadow: 0px 0px 10px #fff;
}
.btn:active{
opacity: 0.8;
}
table{
margin: 0 auto;
}
tr td:first-child{
text-align: right;
}
tr td:nth-child(2){
text-align: center;
}
tr td:nth-child(3){
text-align: left;
}
</style>
<script type="text/javascript">
var vars = new Array();
function res(form1){
var inputs = form.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) vars[inputs[i].name] = parseFloat(inputs[i].value);
sum = vars["t_pg"] + vars["t_vg"] + vars["t_h2o"];
if(sum != 100) alert("Вы выбрали неверное соотношение PV/VG/H2O");
osnova = parseFloat(vars["t_nic"] / (vars["o_nic"] / vars["t_count"])).toFixed(2);
o_pg = osnova * (vars["o_pg"] / 100);
o_vg = osnova * (vars["o_vg"] / 100);
o_h2o = osnova * (vars["o_h2o"] / 100);
d_pg = parseFloat((vars["t_count"] * (vars["t_pg"] / 100)) - o_pg).toFixed(2);
d_vg = parseFloat((vars["t_count"] * (vars["t_vg"] / 100)) - o_vg).toFixed(2);
d_h2o = parseFloat((vars["t_count"] * (vars["t_h2o"] / 100)) - o_h2o).toFixed(2);
if(d_h2o < 0) d_h2o = 0;
count = parseFloat(osnova) + parseFloat(d_pg) + parseFloat(d_vg) + parseFloat(d_h2o);
result = "Добаить никотиновой основы: " + osnova + "мл<br />";
result += "Добаить PG: " + d_pg + "мл<br />";
result += "Добаить VG: " + d_vg + "мл<br />";
result += "Добаить H<sub>2</sub>O: " + d_h2o + "мл<br />";
result += "Результат: " + count + "мл.<br />";
if(count > vars["t_count"]) result += "Рузультат превышает требуемое количество жидкости. <br />Если вас это не устраивает, то измените параметры в форме, которые вы заполнили неверно!<br />Возможно это произошло из-за наличия воды в основе при требуемом отсутствии её в итоговой жидкости.<br />Если это так, то не переживайте и начинайте смешивать жидкость!<br />";
document.getElementById("result").innerHTML = result;
}
function outputUpdate(name, val) {
document.querySelector("#" + name + "_id").value = val;
}
</script>
</head>
<body>
<div class="content">
<form name="form">
<table>
<tr>
<td>Требуемое количество жидкости:
<td><input type="range" name="t_count" min="0" max="100" value="100" step="5" onchange="outputUpdate(name, value)" id="t_count" /></td>
<td><output for="t_count" id="t_count_id">100</output>мл</td>
</tr>
<tr>
<td>Требуемое содержание никотина: </td>
<td><input type="range" name="t_nic" min="0" max="100" value="3" step="1" onchange="outputUpdate(name, value)" id="t_nic" /></td>
<td><output for="t_nic" id="t_nic_id">3</output>мг/мл</td>
</tr>
<tr>
<td>Требуемое содержание PG: </td>
<td><input type="range" name="t_pg" min="0" max="100" value="30" step="5" onchange="outputUpdate(name, value)" id="t_pg" /></td>
<td><output for="t_pg" id="t_pg_id">30</output>%</td>
</tr>
<tr>
<td>Требуемое содержание VG: </td>
<td><input type="range" name="t_vg" min="0" max="100" value="70" step="5" onchange="outputUpdate(name, value)" id="t_vg" /></td>
<td><output for="t_vg" id="t_vg_id">70</output>%</td>
</tr>
<tr>
<td>Требуемое содержание H<sub>2</sub>O:</td>
<td><input type="range" name="t_h2o" min="0" max="100" value="0" step="5" onchange="outputUpdate(name, value)" id="t_h2o" /></td>
<td><output for="t_h2o" id="t_h2o_id">0</output>%</td>
</tr>
<tr>
<td>Содержание никотина в основе:</td>
<td><input type="range" name="o_nic" min="0" max="100" value="36" step="3" onchange="outputUpdate(name, value)" id="o_nic" /></td>
<td><output for="o_nic" id="o_nic_id">36</output>мг/мл</td>
</tr>
<tr>
<td>Содержание PG в основе:</td>
<td><input type="range" name="o_pg" min="0" max="100" value="55" step="5" onchange="outputUpdate(name, value)" id="o_pg" /></td>
<td><output for="o_pg" id="o_pg_id">55</output>%</td>
</tr>
<tr>
<td>Содержание VG в основе:</td>
<td><input type="range" name="o_vg" min="0" max="100" value="35" step="5" onchange="outputUpdate(name, value)" id="o_vg" /></td>
<td><output for="o_vg" id="o_vg_id">35</output>%</td>
</tr>
<tr>
<td>Содержание H<sub>2</sub>O в основе:</td>
<td><input type="range" name="o_h2o" min="0" max="100" value="10" step="5" onchange="outputUpdate(name, value)" id="o_h2o" /></td>
<td><output for="o_h2o" id="o_h2o_id">10</output>%</td>
</tr>
</table>
<div class="btn" onClick="res(this.form)">Рассчитать</div>
</form>
<div id="result"></div>
</div>
</body>
</html>
Доработанной онлайн версией Вы можете пользоваться вот тут.

Надеюсь, что хоть кому-то статья была полезна :)
Copyright ©ZveR
43 комментария
Также могу написать программу под IOS, Android.
Помимо калькулятора жидкости, сделаю ещё калькулятор намотки.
В конце июля выложу приложение в Play Market и уведомлю пользователей сайта новой статьёй.
С ползунками всяко удобней чем те что с циферками
Респект
В каких случаях неправильно считает? Напишите пример, если это так, то в ближайшие дни исправлю.
Изменю шаг ползунка, а также скоро на сайте появится мой калькулятор намотки спирали. По возможности протестируйте и его. Для нас важно, чтобы всё работало корректно и было удобно в использовании!:)
Только на IOS придётся немного подождать, ведь разработка и тестирование — это времязатратные процессы.
Опишу недостатки…
1. Ползунки «живут» своей жизнью. Очень неудобно выставлять концентрации PG-VG-H2O.
2. Содержание никотина в основе не выставляется на 100 мг/мл
3. Поле выбора вида замеса — абсолютно бестолковое и ненужное
4. Калькулятор рассчитан либо на монозамес, либо на суммарную концентрацию ароматизаторов.
5. Калькулятор не учитывает PG ароматизатора. Поэтому все рассчеты НЕВЕРНЫЕ!!!
6. Нет возможности расчета для случая, когда основа не используется.
7. Нет возможности расчета для безникотиновых основ.
Если кому интересно, воспользуйтесь моим калькулятором, сделанным в Excel —
Калькулятор замеса
1. Рассчитан на 5 аромок
2. Учитывает PG ароматизаторов
3. Можно провести расчет самозамеса на безникотиновой основе, либо вообще без использования основы.
4. Полученный «рецепт» можно распечатать
5. Excel-ский документ как «книга рецептов». Каждый лист соответствует одному виду замеса. И эта «книга» будет хранится на Вашем компьютере. Если хотите добавить новый рецепт — делаете копию листа и меняете значения.
Заполнять необходимо только жёлтые ячейки.
В случае ошибок (попытка получения безникотинового замеса из никотиновой основы, суммарная концентрация PG-VG-H2O в основе больше 100% и т.п.) — соответствующие ячейки будут выделены красным цветом.