Страница записи с котом

Пример кастомизации где за посетителем следит кот: он смотрит в поле, которое заполняют, отворачивается, когда свободного времени нет, и радуется удачной записи.

15 минутНичего больше не нужно

Это та самая страница, которую вы скачаете. Поводите курсором — кот следит; заполните имя и почту — посмотрит в поле, где печатают.

Ключ виден в открытую — и это нормально

В этом рецепте ключ не прячется: он вписан прямо в страницу. При создании дайте ему скоуп «публичный для сайта» — по нему можно только читать типы встреч, читать свободное время и создавать брони, а сработает он лишь с адресов, которые вы сами впишете в разрешённые. Это другой путь, чем в рецепте «Где хранить ключ»: там прятали ключ, который может всё. Здесь ему с самого начала можно немногое.

Это не блок для вставки внутрь страницы, а страница целиком: один файл, в котором лежит и разметка, и стили, и скрипт, и сам кот. Ничего дополнительно загружать на сайт не нужно.

1

Взять файл

Скачайте страницу или скопируйте её содержимое. Внутри 673 строки, но править нужно ровно одну.

Скачать файлом

Ключ на странице будет виден в открытую — так и задумано. У скоупа «публичный для сайта» нет доступа ни к чужим записям, ни к настройкам аккаунта: только расписание и создание брони.

2

Получить ключ

Откройте Ключи API и создайте ключ со скоупом «публичный для сайта». Там же, в настройках ключа, впишите адрес страницы в разрешённые origin'ы — без этого API ответит отказом.

Origin — это адрес без пути

Для https://vashsite.ru/zapis.html в разрешённые вписывается https://vashsite.ru, без /zapis.html на конце.

3

Подставить ключ

Откройте файл любым текстовым редактором, найдите data-api-key и замените плейсхолдер на свой ключ.

<div class="app" id="app"
     data-api-key="ВАШ_КЛЮЧ">
Единственное место, которое нужно менять.
4

Положить на сайт

Файл самодостаточный, поэтому годится любой способ выложить обычную страницу. Открывать стоит сразу с настоящего адреса — того же, что вписан в разрешённые origin'ы у ключа. Двойной щелчок по файлу на диске не подойдёт: адреса не будет, и API ответит отказом.

Свой хостингположите файл рядом с остальными страницами — он откроется по своему адресу
Конструктор сайтавставьте страницу окном на любой странице конструктора — блоком «HTML-код»
Ссылкойотправляйте адрес страницы в переписке — она самостоятельная
<iframe src="/zapis.html"
        style="width:100%;height:760px;border:0"
        title="Запись на встречу"></iframe>
Так страница вставляется окном внутрь другой страницы.

Внутрь блока «HTML-код» целиком не вставляется

Это готовая страница со своими html и head, а блок «HTML-код» ждёт кусок разметки. Если файл есть куда выложить — оставьте окно, как выше, это проще всего. Если выкладывать некуда, страницу можно превратить в блок.

Скопируйте в поле всё, кроме обёртки: строку link со шрифтами, весь style, <div class="app" id="app"> со всем содержимым и script в конце. Сами html, head, meta и body не нужны.

Осталось поправить три правила в начале style. Они написаны в расчёте на отдельную страницу и, попав в конструктор, разъедутся на весь сайт: звёздочка обнулит отступы у всех блоков, а body перекрасит фон страницы и развернёт её содержимое по центру.

/* было — правила для отдельной страницы */
*{box-sizing:border-box;margin:0;padding:0}
html{height:100%}
body{
  font:400 16px/1.5 Nunito,system-ui,sans-serif; color:var(--ink);
  min-height:100%;
  background:radial-gradient(120% 60% at 50% 0%, #fff3e0 0%, transparent 60%), var(--paper);
  display:flex; justify-content:center; padding:20px 16px 40px;
  -webkit-font-smoothing:antialiased;
}

/* стало — то же самое, но только внутри формы */
#app,#app *{box-sizing:border-box;margin:0;padding:0}
#app{
  font:400 16px/1.5 Nunito,system-ui,sans-serif; color:var(--ink);
  margin:0 auto; padding:20px 16px 40px;
  -webkit-font-smoothing:antialiased;
}
Правила замыкаются на #app, поэтому оформление дальше формы не уходит. Кремовый фон можно вернуть, дописав строку background к #app.

Как сделан кот

Это не картинка и не видео, а файл формата Rive: скелет с костями и машиной состояний, весом 7,6 килобайта. Такой файл умеет реагировать на происходящее, а не просто проигрывать ролик.

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

Проигрыватель Rive подгружается фоном, уже после того как форма готова к работе. Это важно: у него есть однофайловая сборка на 3,1 мегабайта, и если подключить её обычным способом, страница не отрисуется, пока она не скачается целиком. Здесь берётся сборка на 444 килобайта и грузится следом, поэтому запись работает сразу, а если проигрыватель не доедет — просто не будет кота.

Почему кот следит за курсором по всей странице

Rive видит указатель только над своим холстом, а это квадрат 232 на 232 пикселя. Настоящие движения мыши за его пределами страница пересылает коту сама, прижимая точку к ближайшему краю холста. Поэтому он провожает курсор и когда тот далеко внизу, у кнопки.

На что кот реагирует

В файле нет заранее заготовленных настроений — прижатых ушей или опущенного хвоста там просто нет. Единственное, чем кот управляем, — это куда он смотрит. Поэтому взгляд и стал языком: странице достаточно «показать» коту нужную точку, и он туда посмотрит.

Человек заполняет полесмотрит в это поле, пока в нём печатают
Не введено имяпереводит взгляд на поле имени, рамка краснеет и вздрагивает
Опечатка в почтето же самое, но взгляд на поле почты
В этот день нет времениотворачивается вниз и в сторону
Время успели занятьсмотрит на список времени, который тут же обновляется
Ключ не сработалотворачивается
Запись прошласмотрит на вас, рамка зеленеет, портрет подпрыгивает

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

Пока держится заданный взгляд — полторы-две с половиной секунды — настоящая мышь его не перебивает, иначе реакцию было бы не заметить. Если в системе включено «уменьшить движение», все тряски и подскоки отключаются.

Следы лап вместо серых полос

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

Чтобы следы не мелькали, их появление отложено: первые 160 миллисекунд ничего не показывается, и если ответ успел прийти раньше, посетитель не увидит вообще ничего. А появившись, следы держатся не меньше 450 миллисекунд — иначе они мигнули бы и пропали. Тот же приём возвращается один раз в конце: если выбранное время увели прямо во время записи, страница на мгновение обновляет список.

Что и когда спрашивается

Форматзапрашивает у Планёрки открытые типы встреч
Датазапрашивает свободное время на все 20 дней разом
Времяничего не спрашивает — данные уже на странице
Контактыничего не спрашивает
Готовоотправляет запись

Свободное время запрашивается сразу на весь горизонт в двадцать дней — одним обращением, как только выбран тип встречи. Раньше страница ходила за каждым днём отдельно, и именно на этом терялось время; один запрос быстрее и проще, чем двадцать. Дни, где всё занято, гаснут.

Если что-то не так

Кота нет, а форма работаетНе загрузился проигрыватель Rive — его тянет unpkg.com. Так и задумано: страница не ждёт его и работает без кота. Проверьте, открывается ли unpkg.com у вас в браузере.
Кот есть, но не следит за курсоромНа сенсорных экранах наведения нет вообще — там кот просто сидит и моргает. Это не поломка.
На первом экране бесконечно идут следы лапКлюч не отвечает. Почти всегда это CORS: адрес страницы не в списке разрешённых у ключа. В консоли браузера (F12) это выглядит как Failed to fetch — сам ответ браузер прячет, потому что сам же его заблокировал.
«Не могу получить данные»Проверьте два места: ключ в data-api-key скопирован целиком, и адрес страницы добавлен в разрешённые origin'ы у этого ключа в «Разработчикам» → «API-ключи».
Работает на своём адресе, но не в этом демоТак и должно быть — у демо на planerka.app свой ключ со своим списком разрешённых адресов. Ваш ключ настроен на ваш адрес, у чужого он не сработает, и наоборот.