Приветствие
Доброго времени суток, уважаемый Хабр. Меня зовут Илья и это моя первая статья. Откровенно говоря, я давно уже искал повод опубликовать какой-нибудь материал и вот, похоже этот день настал. Сразу предупрежу — я не считаю решение описанное ниже какой-то метой или истинно верным, но когда вам нужно срочно посчитать строку содержащую простые математические выражения, с использованием скобок, оно может сильно упростить жизнь.
Предисловие
Примерно неделю назад я столкнулся с необходимостью преобразовать строку подобного типа (81+47*(33-42))/15 в математическое выражение и посчитать его. Казалось бы используй любое решение из интернетов и дело в шляпе, но к сожалению использовать библиотеки нельзя, а решения которые я находил как правило считали не правильно, то порядок действий путают, то каким-то образом отрицательное число получается… В общем я просто написал свой код и был доволен, но на следующий день меня вдруг осенило.

Перейдем к практике
Я предлагаю рассмотреть все на практике написания калькулятора, который будет составлять строку а позже считать её, оговорюсь очень важно, чтоб число скобок в выражении было сколько нашей душе угодно и не так важно число символов после запятой, для моей задачи хватит и 3-х.
И так, напишем простой интерфейс с полем ввода и выводом результата
<div class="calc"> <div class="story"></div> <input class="input" type="text" name=""> </div>
И добавим ему симпатичных стилей
body, input {padding: 0; margin: 0; border: 0; font-family: 'Roboto';} body { background: #222; color:#fff; display: flex; height: 100vh; justify-content: center; align-items: center; } .calc { height: 420px; width: 240px; position: relative; border-radius: 8px; box-shadow: -5px -5px 20px 5px rgba(255,255,255,0.08), 5px 5px 20px 5px rgba(0,0,0,0.5), -1px -1px 0px 0px rgba(255,255,255,0.2), 1px 1px 0px 0px rgba(0,0,0,0.4); background: linear-gradient(120deg, #464e55 0%, #292929 100%); } .calc:after { content: ''; position: absolute; z-index: -1; background: #fff; display: block; width: calc(100% + 2px); height: calc(100% + 2px); margin: -1px; top: 0; border-radius: 8px; background: linear-gradient(120deg, #4f6579 0%, #312e40 100%); } .story { height: calc(420px - 42px); text-align: right; box-sizing: border-box; padding: 10px 20px; display: flex; justify-content: end; flex-direction: column; opacity: 0.6; overflow: hidden; position: relative; } .story:after { content: ''; background: linear-gradient(180deg, #3e464d 0%, #312e4000 50%); display: block; width: 100%; height: 100%; top: 0; left: 0; position: absolute; border-radius: 10px; } .story i { font-size: 12px; font-style: normal; } .input { outline: none; border: none; margin: 0; width: 100%; background: none; color: #fff; } .input { height: 42px; border-top: 1px dotted #8096a4; box-sizing: border-box; text-align: right; padding: 0 20px; line-height: 40px; font-size: 18px; font-weight: 300; }
А далее создадим маленькую магии и напишем простой код для создания строки, которую мы будем считать
let string = '', $input = document.querySelector('.input'); $story = document.querySelector('.story'); // событие для отлавливания нажатия в поле input $input.onkeyup = function(e){ // если нажат Enter то считаем строку if(e.key == 'Enter'){ // это наша будущая функция let result = strToMath(string); // передаём результат в поле ввода чтоб иметь возможность дальше работать с ним this.value = result; // просто сохраняем историю $story.innerHTML += `<i>${string}=${result}</i>`; // а это вынужденный костыль, чтоб считать правильно дробные числа string = `(${string})`; } else string += e.key; // не совсем верное решение но ведь это просто пример }
Хорошо, тут должно быть в целом все понятно, но что у нас с strToMath()?
А собственно вот и она
function strToMath(string){ // преходящие значение необходимо разбить на массив по символам, иначе ничего работать не будет string = string.replaceAll(' ', '').replaceAll('+', ' + ').replaceAll('*', ' * ').replaceAll('-', ' - ').replaceAll('/', ' / ').split(' '); // Переносим повторный символ == "-" к следующему числу for(let i = 0; i < string.length; i++){ if(string[i] == ''){ string.splice(i, 2); string[i] = '-'+string[i]; } } // а теперь та самая магичиская и потрясающая функция calc() прямиком из css3 // создаем любой элемент let calc = document.createElement('calc'); // передаём туда в свойство opacity наш объедененный через пробел массив calc.style['opacity'] = `calc(${string.join(' ')})`; // получаем значение обратно удаляя все лишнее let result = parseFloat(calc.style['opacity'].replace('calc(', '').replace(')', '')) // и удаляем сам элемент calc.remove(); // результат возвращаем. return result; }
Грубо говоря, данное решение в 4 строки если поступающие данные всегда валидны.
По большому счету мы просто эксплуатируем функцию из css3 для получения решения. Однако есть и минусы, если у нас калькулятор то прошлое решение должно поступать с новым в данную функцию т.к. calc() обрезает результат до 5-ти символов после запятой и при делении 89/81 = 1.09877 после обратного умножения мы не получим 89 , результатом будет: 89.0004
Еще говоря об opacity, если использовать z-index то значение будет округляться, а размерные значения требуют обязательно в конце умножения на 1px т.е. примерно так будет выглядеть код:
calc.style['top'] = `calc((${string.join(' ')}) * 1px)`; let result = parseFloat(calc.style['top'].replace('calc(', '').replace('px)', ''))
Так же собрал маленькое демо с полноценным калькулятором доступным по ссылке: https://preview-1326290.playcode.io/
Код демки: https://playcode.io/1326290
Спасибо за внимание, понимаю, что данный инструмент просто велосипед, но имеющий право на существование.
ссылка на оригинал статьи https://habr.com/ru/post/724750/
Добавить комментарий