Внучки подрастают. Старшей осенью в школу. Хороший повод реинкарнировать в виде WEB-приложения дочкину тренировалку по арифметике. Собирался десятилетие, но все руки не доходили и напрямую с html-версткой и фронтендом не сталкивался. Под катом — процесс от ТЗ до готового изделия, который может быть интересен таким-же как я начинающим.

Тренировалку сделал в 2002-м с помощью gif-рисовальщика и VB5. История сохранила только .exe, скриншоты которого и станут основой графики нового приложения:


… а вот «педагогическую концепцию» стоит слегка подправить:
  • суть процесса, как и ранее, в помощи третьему лицу (ученик Миша);
  • в случае ошибки необходимо предоставить правильный ответ;
  • оценки отменяются (хорошо бы им придать характер игрового счета, конвертируя в очки время и соотношение правильных и ошибочных ответов);
  • родительский контроль (как без него?) посредством 'дневника', в котором сохраняются последние три-четыре решенных задания (30-40 примеров) и минимальная статистика;
  • желательна виртуальная специализированная клавиатура;
  • для разрядки стоит добавить примитивной анимации.

Итого требуется реализовать три основных сцены: настройка сложности, решение заданий и 'дневник' для устройств не менее фаблета (для смартфонов с естественной вертикальной ориентацией экрана компоновка сцен должна быть иной).
Вооружившись изложенным ТЗ и краткими напутствием мэтров [1] и [4], приступил к реализации проекта с рабочим названием «Арифмиша».

Выбор средства разработки


По ключевой фразе 'html editor' YaST предложил Bluefish. Среда разработки интерфейса далека в визуализации от подзабытого VB5. Для просмотра верстки необходимо вызвать дежурный браузер.
Cинтаксис html и js подсвечиваются. Автозаполнение имеется, но порой досаждают парные языковые конструкции (теги, скобки). Есть структуризатор Java-script кода.
За время разработки вряд ли успею оценить все возможности редактора.

Хостинг


Стоит сразу озаботиться хостингом для проверки верстки на разных платформах. Воспользовался советом sim3x на Тостерe. Немного поэкспериментировав c GitHub Project Pages получил то, что хотел. Шаблон index.html взял со StackOverflow, но это необязательно — можно просто ссылаться на корневую страницу в ветви gh-pages репозитория.

Структура приложения


Приложение состоит из единственного файла html, содержащего отображаемые элементы, и подгружаемых файла стилей, java-скриптов, графики. Компонентов немного. Подумав, таки добавил две структурные папки: ./img и ./js.

Верстка


Структура html, <head>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="max-age=0">
    <meta http-equiv="cache-control" content="no-cache" >
    <meta http-equiv="expires" content="0" >
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" >
    <meta http-equiv="pragma" content="no-cache">
    <title>Arithmisha</title>
    <meta name="viewport" content="width=device-width">
    <meta name="keywords" lang="ru" content="Арифмиша арифметика пример упражнение">
    <meta name="keywords" lang="en" content="Arithmisha arithmetic example exercise">
    <meta name="robots" content="index, follow">
    <script src="./js/cookie.js" type="text/javascript"></script>
    <script src="./js/arithmisha.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="arithmisha.css">
  </head>
  <body onload="postLoadInit();" onkeydown="catchControlKey(event);" onkeypress="keyboardHandle(event);" ondblclick="return false;">
<!-- ...элементы страницы... -->
  </body>
</html>
В теге head определил кодировку страницы, заголовок, инструкции поисковым роботам и, на всякий случай, запрет кэширования на период разработки.

Разработку начал с верстки и переключения сцен. По личной неопытности процесс имел характер эксперимента: сформировал сцену — проверь в браузерах.

Поскольку сцен всего три, не разбивал их на отдельные html-файлы, а регулировал переключением свойства visibility корневого div элемента слоя сцены. Все элементы сцен завязаны на базовое изображение, снабженное кликабельной картой и размещенное в нижнем слое вместе с виртуальной клавиатурой.
С целью горизонтального центрирования сцен, для базового слоя выбрал ширину 740px (при абсолютном позиционировании), с некоторым избытком охватывающую картинку и виртуальную клавиатуру. Позднее добавил анимированных Марьванну и Мишу, разогнав их по углам с помощью margin.

Arithmisha. Settings.

Для организации групп элементов в колонки пользовался стилевым атрибутом display:inline-block;
html сцены 'Настройка сложности'
<div id="divSettings" class="layer3" style="visibility:hidden; top:80px; font-size:15px;">
<!--  из кода удалены <var>, используемые при локализации -->
  <div style="display:inline-block; text-align: left;">  <var id="setOp">Action</var>:<br>
    <input class="setOp" type="checkbox" value="1"> addition<br>
    <input class="setOp" type="checkbox" value="2"> subtraction<br>
    <input class="setOp" type="checkbox" value="4"> multiplication<br>
    <input class="setOp" type="checkbox" value="8"> division<br>
  </div>
  <div style="display:inline-block; text-align:left; vertical-align:top;">  Magnitude:<br>
    <input class="setOrder" type="checkbox" value="16"> ones<br>
    <input class="setOrder" type="checkbox" value="32"> tens<br>
    <input class="setOrder" type="checkbox" value="64"> tenths<br>
  </div>
  <div style="display:inline-block; text-align:left;">  To find:<br>
    <input class="setFind" type="checkbox" value="256"> result<br>
    <input class="setFind" type="checkbox" value="640"> operand<br>
    <input class="setFind" type="checkbox" value="1024"> action<br>
    <input class="setFind" type="checkbox" value="2048"> relationship<br>
  </div>
  <div>
    <var id="setOpd2nd">2nd operand</var>:
    <var id="minOpd2nd" class="edited">1</var> - <var id="maxOpd2nd" class="editable">9</var>
  </div>
</div>

Соглашения


Использование соглашений — не самоцель: хороший тон и один из признаков профессионализма. Дело ведь не в количестве пробелов в отступах, а в едином читабельном оформлении компонентов. Желательно сразу продумать систему именования. В этом смысле мое изделие далеко от идеала.

Валидация html


Валидатор validator.w3.org/nu/#file помог выловить ляпы верстки, устранить избыточность и подсказал решение вопроса с устаревшими атрибутами типа align=”center”. Любое отклонение валидатор квалифицировал как ошибку. Для required alt в map area наличие ошибки понятно, интересно другое — почему required? Что в атрибуте alt такого важного для W3C?

Java Script


При обращении из скрипта адресуемые элементы страницы должны быть уже загружены. На этот случай есть событие onload элемента body. Последовательность получилась следующая:
  • вызов скрипта из header, объявление и инициализация глобальных переменных;
  • загрузка элементов тела страницы и отработка встроенных скриптов;
  • вызов процедуры события onload.
Доступ к конкретному элементу страницы производится по значению атрибута id. Для группировки элементов пользовался атрибутом class. Возвращаемые методами getElementsByClassName() и getElementsByTag() списки элементов (NodeList) индексируются подобно Array, но таковыми не являются.
Элемент может принадлежать нескольким классам, разделяемых пробелом. В отличие от имен тегов, значения атрибутов id и class регистрозависимы.
Атрибуты элементов html обладают некоторой двойственностью: если для элемента input типа checkbox страницы атрибут checked='checked', то в js — это свойство элемента el.checked == true, а метод el.getAttribute('checked') вернет null.

Генерация примеров арифметики основана на случайном выборе из интервала целых чисел. Проверка ответа в VB5-реализации базировалась на логическом значении, возвращаемом функцией eval('2 * 2 = 5'). В этой реализации — на строковом сравнении правильного ответа и ответа пользователя. В связи с этим пришлось исключать неоднозначности вида: 2? 2 = 4 и округлять периодическую дробную часть чисел.

События


Назначение обработчиков события onclick для элементов area карты изображения возможно только из скрипта. Для подавления действия по-умолчанию (переход по html-ссылке, без которой событие не работает) обработчик должен вернуть false:
document.getElementById("areaBlackboard").onclick =
  function() {
    showTask();
    return false; //disable default action
  };

Ввод данных организован и c виртуальной и с физической клавиатур непосредственно в элементы (innerHTML). Вместо курсора использовал blinking border CSS. Для физической клавиатуры определены обработчики событий onkeypress (отображаемые символы), onkeydown (управляющие символы) тела страницы. Дело в следующем:
  • не все броузеры инициируют onkeypress для Backspace;
  • необходимо подавить действие клавиш в браузере (Backspace — возврат на домашнюю страницу, Enter — клик по сфокусированному элементу).

Отладка


Отладчики в браузерах упрятаны довольно глубоко в меню (для FF: “Меню” — “Разработка” — “Отладчик”), но есть горячие клавиши.
Если что-то пошло не так, стоит заглянуть на вкладку “Консоль”, где выводятся синтаксические ошибки исполняемых скриптов. Ловятся по одной, что естественно для интерпретации. А если и в консоли пусто, самое время обратиться к js-валидатору www.jslint.com.

Для времени исполнения есть полный набор отладочных инструментов: просто песня в сравнении с бэкенд отладкой с клиента, когда порой располагаешь только контрольной печатью.

Графика и анимация.


Программно-управляемую анимацию не планировал, поэтому не заострялся на CSS и js решениях. Тем не менее, понравилось предложение mefisto в комментах к [2]. Если склеить и двигать кадры вертикально получится вполне по-киношному.
APNG не прижился в Chrome. Выбрал старый, добрый и уже не проприетарный gif.
Для обработки скриншотов и формирования gif использовал GIMP. Редактор содержит все необходимое, включая gif-оптимизатор. Эффективность оптимизации можете оценить сами.
Коротко и наглядно об анимации в GIMP можно посмотреть здесь: http://www.progimp.ru/articles/sposobyi_sozdaniya_animatsii_v_gimp/
Пробовал animizer.net/ru/gif-apng-assembler. Интересно, но нет возможности дублировать загруженные кадры и при разбиении анимации теряются значения задержек.

Xранение данных


Для хранения настроек и 'дневника' использовал cookie со сроком 2 недели. Есть ограничение — куки привязаны к конкретному браузеру.
Мои потребности исчерпывающе удовлетворил пакет из сети. Код использован с согласия автора.

Локализация


Не есть предмет первой необходимости, но подумать об этом стоит. Целям локализации служат скрипты с постфиксом, соответствующим локальному языку (lang_ru.js), распихивающие строки текста по свойствам элементов и глобальным переменным приложения. В самом html — мой жуткий английский по-умолчанию. Для выбора и загрузки скрипта локализации в конце тела страницы добавлен код:
<html>
  <body>
...
    <script>
      var script = document.createElement("script");
      script.type = "text/javascript";
      script.src = "./js/lang_"+
        (window.navigator.userLanguage || window.navigator.language).split("-")[0] + ".js";
      document.body.appendChild(script);   // добавить загрузку скрипта в конец документа
    </script>
  </body>
</html>

К локальным настройкам можно отнести и альтернативные знаки арифметических действий: деления (слэш /, двоеточие:, обелюс ?) и умножения (звездочка *, крестик ?, точка • ). Ну и, конечно, представление десятичной “точки”.

Итог


Процесс разработки доставил удовольствие. В чем прелесть программирования — всегда есть пусть не материальный, но результат: https://miktim.github.io/arithmisha/

ХаброБиблиография:


1. Checkit ,“Создание веб-сайта. Курс молодого бойца”
2. Lord_D, “Анимированный PNG в Firefox, Opera и WebKit? Легко!”
3. Disturbed,“Нужна ли HTML-валидация?!”
4. перевод splincodewd, «Руководство по HTML/CSS/JavaScript»

Ax да,

Монетизация


У Мишки на майке есть место для логотипа спонсора :). Правда придется менять хостера…
Поделиться с друзьями
-->

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


  1. miktim
    28.05.2016 15:10

    Приношу извинения НЛО за неверную трактовку фразы о PNG из правил оформления публикаций.
    С 10-летием.
    Михаил.