Часть 6: Коробки и начало реального приложения
Предыдущую статью вы можете прочитать здесь.
Введение
Контент, который я обещал для этой статьи, стал слишком большим. Мне пришлось разделить его. Кроме того, я потратил больше времени, чем ожидал, на написание кода. Я сделал МНОГО рефакторинга в поисках лучшего дизайна/наибольшей полезности.
Я закончил код для полностраничного движка, такого как BobSprite. Потом я понял, что для нас было бы полезнее, если бы я мог преобразовать двигатель в модульный формат: коробка; который вы можете использовать как единственный элемент специальной веб-эры или как специальную часть общей веб-страницы (например, тег видео); на самом деле, вы можете использовать много блоков на веб-странице.
Затем я воскресил идею сделать все в стиле чистого холста… и это сработало! Использовать автономные блоки проще, чем подчиняться правилам монолитного движка (от которого я отказался).
Прямо сейчас мне еще нужно создать дополнительные виджеты и утилиты. В любом случае, этого достаточно, чтобы послужить основой для сегодняшней статьи.
Настоящее приложение
Я выбрал генератор мозаики с простыми вариантами изображений для нашего реального приложения, потому что он полезен и необычен, а его код не будет отвлекать нас от изучения самой коробки. Настоящее приложение еще НЕ завершено.
Библиотека
Я изо всех сил старался сделать использование библиотеки чрезвычайно дружелюбным/интуитивно понятным/безопасным/немногословным. Эти цели приводят к
- мало функций, с понятными именами
- понятная и простая структура вашего приложения
- нет необязательных аргументов функции (несколько способов кодирования вызывают путаницу)
- коробка инкапсулирует все, что может, (почти) не давая вам возможности вызвать внутреннюю ошибку
- библиотека пытается отловить все возможные ошибки, которые вы могли бы сделать — она немедленно останавливает работу и пишет сообщение о очистке в консоли; сообщение всегда начинается с «Uncaught» и сопровождается двумя следами («-»)
- очень мало простых правил, которым вы должны следовать
Примечание: библиотека несколько отличается от ее описания в предыдущих статьях.
(Частичная) демонстрация
Мне нравятся одностраничные приложения. Но я люблю однофайловые приложения; особенно для простых демонстраций. Этот формат плохо подходит для изучения кода, я отвечу на этот вопрос в конце статьи.
Эта демонстрация представляет собой однофайловое приложение размером 170,1 КБ — несжатое; ничего страшного. Помимо библиотеки и кода приложения, он включает встроенные изображения и шрифты.
Демонстрация была построена с использованием традиционных элементов HTML (‹h1›, ‹div›, ‹br›), а также двух блоков GoodbyeHtml и одного метки GoodbyeHtml. Цель состоит в том, чтобы показать, что теперь мы можем выбирать между:
- чистое приложение GoodbyeHtml (всего одна большая коробка, как у BobSprite)
- традиционная HTML-страница, которая включает в себя столько полей GoodbyeHtml, сколько мы хотим
Каждая коробка создается с использованием только элемента холста. Браузер видит только один холст. Но внутри больше полотен для хранения изображений и управления ими. Все они нарисованы на основном полотне коробки.
Сегодня мы не будем рассматривать код библиотеки. Наша цель — научиться пользоваться библиотекой.
Некоторые читатели беспокоились, что будет сложно написать веб-страницу на основе холста. Надеюсь, вы обнаружите, что это очень просто, используя эту библиотеку.
Теперь давайте изучим упрощенную версию кода нашего реального (неполного) приложения.
Код приложения
// (embedded fonts here) // (embedded images here) "use strict" var goodbye function main() { // goodbye = createGoodbyeHtmlLibrary() loadResources() }
Мы начинаем создавать библиотеку и назначать ее глобальной переменной, чтобы она была доступна везде в нашем коде.
Затем нам нужно загрузить ресурсы.
Загрузка ресурсов
Наша веб-страница на основе холста похожа на программное обеспечение видеоигры: она должна загружать все ресурсы заранее. Компьютерная видеоигра просто загружает ресурсы с диска компьютера.
Поскольку наше приложение загружает ресурсы через Интернет, у нас должен быть способ проверить, загружены ли ресурсы, прежде чем продолжить.
Браузер загружает ресурсы асинхронно, даже встроенные.
Вот почему у нас есть библиотечная утилита под названием loader.
function loadResources() { // const loader = goodbye.createLoader() // loader.loadFont("black", fontSourceProSansBlack) loader.loadFont("white", fontSourceProSansWhite) // loader.loadImage("help-black", embededImages["help-black"]) } loader.loadImage("load-black", embededImages["load-black"]) } // loader.ready(resourcesLoaded) // must send a callback } function resourcesLoaded() { // the callback // // processing the images, if needed // (more code here)
Обратите внимание, что для использования загрузчика требуется 3 шага:
- создание загрузчика
- просит загрузить ВСЕ ресурсы
- сообщая загрузчику, что больше нет ресурсов для загрузки и какой обратный вызов (функция в нашем коде, которую загрузчик вызовет после загрузки всех ресурсов)
Загруженные ресурсы сохраняются в переменных библиотеки с именами allFonts и allImages.
ПРИМЕЧАНИЕ: во второй статье этой серии рассказывается, как создавать шрифты.
Другие библиотечные утилиты
В настоящее время, помимо createLoader, библиотека GoodbyeHtml имеет следующие служебные функции: cloneImage, createCanvas, fadeImage, createCheckerboard, negativeFromImage, createLabel, calcTextLength и createBox.
Например, с помощью fadeImage мы можем создать изображение отключенной кнопки из обычного изображения кнопки:
const disabled = goodbye.fadeImage(original, "black", 0.40)
Другой пример: иногда у нас есть значки белого цвета поверх черного; которые при нажатии становятся черными поверх белых значков — или наоборот. Нам не нужно загружать два набора иконок, если один набор является точной копией другого. Мы можем использовать negativeFromImage, который работаети для листов шрифтов.
const whiteIcon = goodbye.negativeFromImage(blackIcon)
Ярлык GoodbyeHtml
const label = goodbye.createLabel("white", "black", 10,10,5,5, "My label")
Метка GoodbyeHtml — это изображение (точнее, холст), с которым вы обращаетесь как с любым другим изображением. Его параметры:
- идентификатор (строка) используемого шрифта
- цвет фона (строка)
- левое заполнение (целое число)
- заполнение справа (целое число)
- верхнее заполнение (целое число)
- нижнее заполнение (целое число)
- текст (строка)
Вы не выбираете напрямую ширину и высоту. Ширина и высота зависят от выбранного шрифта и заполнения.
Параметры функций
Нигде нет необязательных параметров: нет альтернативных способов вызова функции. Такой дизайн упрощает освоение библиотеки.
Также я постарался сделать структуру параметров довольно стандартной внутри библиотеки и совместимой с распространенными паттернами.
Все координаты/размеры представлены в формате слева, сверху, ширины,и высоты.
Есть одно исключение; один случай, когда параметры работают так, как вы думаете: заполнение метки: они не соответствуют УЖАСНОМУ шаблону CSS для заполнения и полей.
Шаблон GoodbyeHtml для заполнения начинает походить на шаблон UNIVERSAL.
Он начинается с левого. Затем он ожидает правильно; таким образом легко позиционировать/центрировать текст горизонтально.
Далее следует верхнее заполнение, а затем нижнее заполнение; упрощая позиционирование/центрирование текста по вертикали.
Проблеск безумия и садизма CSS
Зачем мы ходим в школу? Мы ходим в школу, чтобы изучать закономерности, которые нам очень пригодятся на всю оставшуюся жизнь; потому что все остальные изучают ЖЕ шаблон.
Например, зеленый знак означает идти, красный знак означает стоп.
Другой пример — декартовы координаты: X и Y; которые часто переводятся влево и вверх. Это универсальный шаблон.
Сначала всегда идет координата x, а затем координата y.
Мы можем определить прямоугольник, указав его левый верхний угол и правый нижний угол: левый, верхний, справа, снизу (или слева, сверху, по ширине, по высоте).
Мы видим это повсюду, это касается не только программирования. Это математика!
Но он входит в CSS и говорит: «Забудьте все, чему вы научились. Настоящая система координат — сверху, справа, снизу, слева».
Выше мы видим изображение создателя CSS, когда ему было 2 года, и он играл со своей самой любимой марионеткой: счастливым и наивным веб-разработчиком.
Помимо того, что это противоречит универсальному образцу, это ерунда. Это не то же самое, что горизонталь-сначала-вертикаль-после, что имеет смысл и следует, насколько это возможно, универсальному шаблону.
В том возрасте, в котором я истязал себя (изучая CSS), я не мог запомнить этот ГЛУПЫЙ «паттерн». Затем, однажды, читая книгу по CSS, я нашел трюк, который помогает мне запомнить «шаблон».
Да! CSS — это не язык; это вонючий и липкий мешок скрытых трюков.
Хитрость заключается в слове TRouBLe. Должен признать, что это самый честный трюк, который я видел за всю свою жизнь.
Создание коробки
const parent = document.getElementById("first-box") const box = goodbye.createBox(1000, 600, parent)
Вам нужно 3 аргумента для создания блока: ширина, высота и HTML-контейнер, в который будет помещен блок.
Вы не добавляете поле. Поле дополняется. Более простой пример:
const box = goodbye.createBox(1000, 600, document.body) box.setBgColor("#084d6e")
В поле представлены 4 метода: setBgColor, initLayers, exchangeLayers и getLayer.
Создание слоев
// initLayers returns nothing box.initLayers(["base", "over", "help", "alert"])
Теперь все слои коробки определены. Слои автоматически добавляются в поле.
Вы не можете изменить или добавить слои. Вы можете изменить только две вещи:
- видимость каждого слоя
- порядок слоев
Разрешение удалять и добавлять слои сделало бы ваш код менее читабельным и менее удобным для сопровождения, потому что вы могли бы сделать это в любом месте вашего кода. Кроме того, эти функции не являются необходимыми.
box.exchangeLayers(["help", "over", "base", "alert"]) // OK box.exchangeLayers(["help", "over", "help", "alert"]) // error // message in console: Uncaught -- wrong argument ids for function box.exchangeLayers: duplicated item: help
Есть причина, по которой функция называется initLayer, а не createLayer: она не возвращает объект «слои». Вы должны использовать id (строку) каждого слоя.
Объект слоя имеет 5 методов: createPanel, show, hide, visible и log. эм>.
const a = box.getLayer("alert") // returns a layer object a.hide() a.show() a.log() let visible = a.visible()
Примечание. Слой не имеет левых/верхних координат, ширины, высоты или цвета фона. На самом деле это виртуальный слой.
Создание панели
const layer = box.getLayer("base") const panel = layer.createPanel(0, 0, 1000, 50, "black") panel.paintRect(0, 0, 250, 50, "white") panel.setFont("black") panel.write(30, 10, "Mosaic Generator")
Параметры createPanel: классический left, top, width, height — и bgColor.
При создании панель автоматически добавляется к своему слою. И проверяется дважды:
- если он полностью помещается внутри коробки
- для столкновения с другими панелями в том же слое
const layer = box.getLayer("base") const panelA = layer.createPanel(0, 0, 100, 50, "black") const panelB = layer.createPanel(99, 0, 100, 50, "red") // error // message in console: Uncaught -- panel 2 clashes with panel 1 in layer base
Объект панели имеет несколько методов (еще больше): hide, show, setFont, write, clearRect, paintRect, paintImage, setBgColor, createButton, createSurface, и лог.
Примечание. Надпись и рисование на панели не создают виджеты; таким образом, нет проверок на подгонку и конфликты.
Создание кнопки
const buttonOk = panel.createButton(460, 350, 68, 50, "dimgrey") const img = goodbye.createLabel("white", "black", 20, 20, 10, 10, "OK") buttonOk.setImageNormal(img) buttonOk.setOnClick(function () { box.getLayer("alert").hide() })
Создание кнопки — это почти копия создания панели.
Параметры createButotn: классический left, top, width, height — и bgColor. Положение кнопки относительно ее родителя: панели (не коробки).
При его создании на его панель автоматически добавляется кнопка. И проверяется дважды:
- если он полностью помещается внутри панели
- для столкновения с другими виджетами на той же панели
const b = panel.createButton(460, "black", 68, 50, "grey") // error // message in console: -- wrong argument top for function panel.createButton: expecting integer >= 0, got string: black
На данном этапе библиотека не вставляет текст в кнопку автоматически; мы должны действовать вручную.
Объект кнопки имеет следующие методы: hide, show, setImageNormal, setImageActive, setImagePressed, setImageDisabled, отключить, активировать, нормализовать, setBgColor, setOnClick, setButtonText и журнал.
Состояние кнопки
Кнопка GoodbyeHtml имеет 4 состояния, и вы можете установить изображение для каждого состояния:
- нормальный
- отключено (ничего не делает)
- активный (вы должны вызвать button.activate) — означает, что он был нажат и какой-то статус изменился; щелчок по ней снова делает кнопку нормальной, и этот статус меняется на предыдущее значение
- нажато — означает, что он получил событие нажатия мыши и ожидает события нажатия мыши, чтобы произвести событие щелчка.
Если мышь покидает кнопку, пока кнопка нажата, состояние нажатия отменяется, а будущее событие щелчка прерывается, чтобы избежать принятия псевдощелчка. Вы должны попробовать это в демо.
Для кнопки вы можете прослушивать только событие нажатия. Все остальные события мыши обрабатываются только внутри.
Создание виджета поверхности
Создание поверхности похоже на создание кнопки. Поверхность — это необработанный виджет, очень настраиваемый. Его цель — дать вам свободу делать то, что вы обычно не можете делать из-за жесткой/безопасной конструкции коробки.
Сейчас у нас нет времени обсуждать поверхность. Просто представьте себе специальный слой, содержащий одну панель с прозрачным фоном, покрывающую всю коробку. И эта панель имеет только один виджет, прозрачную фоновую поверхность, которая также полностью закрывает поле. В этом случае вы можете рисовать что угодно и где угодно… и у вас может быть больше таких специальных слоев.
Для поверхности вы должны напрямую прослушивать все события мыши, кроме события щелчка, которое недоступно. У вас уже есть события mouse down и mouse up для прослушивания. Не будем лишними.
Иерархия
библиотека › блоки › слои › панели › виджеты
Это так просто.
- Вы создаете библиотеку.
- Вы используете утилиты библиотеки, включая createBox.
- Из коробки вы создаете слои.
- Из каждого слоя вы создаете панели.
- На каждой панели вы создаете виджеты.
Это НЕ похоже на создание объекта HTML в JavaScript, где вы сначала создаете его, а затем добавляете в какой-то контейнер. Или забудьте это сделать. Или сделайте это дважды (один и тот же контейнер или нет).
В GoodbyeHtml нет потерянных виджетов.
Я не знаю, как ты это чувствуешь; но для меня это не может быть более простым/надежным/легким/интуитивно понятным/быстрым/читабельным/обслуживаемым/и т. д.
Что будет
Мне нужно включить в библиотеку дополнительные функции (включая работу с клавиатурой), опубликовать ее на GitHub, задокументировать и закончить генератор мозаики.
Код приложения следующей демонстрации будет отделен от библиотеки и встроенных ресурсов, подходящих для обучения.
Это ссылка на следующую статью серии.
Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.