Часть 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"])

Теперь все слои коробки определены. Слои автоматически добавляются в поле.

Вы не можете изменить или добавить слои. Вы можете изменить только две вещи:

  1. видимость каждого слоя
  2. порядок слоев

Разрешение удалять и добавлять слои сделало бы ваш код менее читабельным и менее удобным для сопровождения, потому что вы могли бы сделать это в любом месте вашего кода. Кроме того, эти функции не являются необходимыми.

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.

При создании панель автоматически добавляется к своему слою. И проверяется дважды:

  1. если он полностью помещается внутри коробки
  2. для столкновения с другими панелями в том же слое
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. Положение кнопки относительно ее родителя: панели (не коробки).

При его создании на его панель автоматически добавляется кнопка. И проверяется дважды:

  1. если он полностью помещается внутри панели
  2. для столкновения с другими виджетами на той же панели
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 для прослушивания. Не будем лишними.

Иерархия

библиотека › блоки › ​​слои › панели › виджеты

Это так просто.

  1. Вы создаете библиотеку.
  2. Вы используете утилиты библиотеки, включая createBox.
  3. Из коробки вы создаете слои.
  4. Из каждого слоя вы создаете панели.
  5. На каждой панели вы создаете виджеты.

Это НЕ похоже на создание объекта HTML в JavaScript, где вы сначала создаете его, а затем добавляете в какой-то контейнер. Или забудьте это сделать. Или сделайте это дважды (один и тот же контейнер или нет).

В GoodbyeHtml нет потерянных виджетов.

Я не знаю, как ты это чувствуешь; но для меня это не может быть более простым/надежным/легким/интуитивно понятным/быстрым/читабельным/обслуживаемым/и т. д.

Что будет

Мне нужно включить в библиотеку дополнительные функции (включая работу с клавиатурой), опубликовать ее на GitHub, задокументировать и закончить генератор мозаики.

Код приложения следующей демонстрации будет отделен от библиотеки и встроенных ресурсов, подходящих для обучения.

Это ссылка на следующую статью серии.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.