Справочник по виджету¶
Виджет Ziex Try-On — один скрипт, который добавляет на страницу товара окно примерки. Он рисует кнопку-launcher, окно с шагами загрузки, прогресса и результата и необязательную галерею «Мои образы». С API Ziex Try-On виджет общается сам; ваша страница только настраивает его и слушает его события.
Первую работающую страницу покажет быстрый старт на HTML. Если CMS-модуль загружает виджет по требованию, прочитайте Отложенную загрузку и подхват. Магазину с Content-Security-Policy нужны директивы со страницы CSP.
Подключение¶
<script src="https://api.ziex-tryon.com/embed/tryon-embed.js"></script>
<script>
window.TryOn.init({ apiKey: 'pk_YOUR_PUBLISHABLE_KEY' });
</script>
- Скрипт — один адрес для всех магазинов. Версия определяется свойством
TryOn.version, а не адресом. - На публичной странице используйте публикуемый ключ (
pk_…). Секретный ключ (tryon_…) в исходном коде страницы держать нельзя: он ещё и открывает вход в клиентский портал. Публикуемый ключ работает только с домена, к которому он привязан. - Ключ, который начинается с
pk_test_(илиtryon_test_), — тестовый: в шапке окна появляется значок «ТЕСТ», а на примерку отвечает картинка-заглушка, без списаний. - Виджет обращается к тому адресу, с которого загружен. Опцию
apiUrlзадавайте, только если отдаёте скрипт с другого адреса.
TryOn.init(options)¶
init() запускает виджет. Обязателен только apiKey: без него виджет пишет ошибку в консоль и ничего не делает.
Повторный init() целиком заменяет конфигурацию, от прошлого вызова ничего не переносится (для частичного изменения
есть update()). Исключение — уже отправленная примерка: она сохраняет ключ, адрес API и товар,
с которыми была отправлена.
Подключение и товар¶
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
apiKey |
строка | – (обязательна) | Публикуемый ключ вашего магазина. |
apiUrl |
строка | origin из src скрипта |
Базовый URL API. Завершающий слэш убирается. |
productImage |
строка | определяется по странице | Картинка вещи: URL https:// или data: URI. Если не задана, берётся из разметки товара на странице (JSON-LD Product, og:image, затем первое крупное изображение). |
productTitle |
строка | определяется по странице | Название товара. Если не задано, берётся из JSON-LD, og:title, микроразметки товара или <h1>. |
productUrl |
строка | адрес самой страницы | Ссылка на товар. Сохраняется вместе с примеркой и показывается вам рядом с заданием. В значении по умолчанию отбрасываются трекинговые параметры. Сохраняются только ссылки http(s). |
productSku |
строка | определяется по странице | Артикул товара. Принимается для совместимости; окно сейчас его не показывает. |
productPrice |
строка | определяется по странице | Цена товара в том виде, как задана. Принимается для совместимости; окно сейчас её не показывает. |
modelImage |
строка | нет | URL или data: URI готового фото модели. Покупатель может заменить его своим. С публикуемым ключом фото всегда отправляется как загрузка, поэтому URL должен читаться кросс-доменно (CORS). |
category |
строка | tops |
Категория вещи: tops, outerwear, bottoms, one-pieces или auto. Старое значение dresses читается как one-pieces. Покупатель видит категории, которые поддерживает ваш аккаунт. |
lockCategory |
boolean | false |
Вместе с заданной category прячет выбор категории и использует её как есть. |
mode |
строка | quality |
quality или balanced. Любое другое значение превращается в quality. Покупателя об этом не спрашивают. |
integration |
строка | widget/<TryOn.version> |
Кто управляет виджетом, в виде имя/версия (например shop-theme/2.3). Имя в нижнем регистре, всё значение не длиннее 64 символов. Всё остальное заменяется значением по умолчанию. Метка сохраняется в задании, чтобы нам было проще помочь, когда что-то идёт не так. |
Внешний вид и поведение¶
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
lang |
строка | из <html lang> |
ru или en. ru, если язык страницы начинается с ru, иначе en. |
launcher |
boolean | true |
false — без плавающей кнопки и без запросов при init(); окно открывает ваша страница через TryOn.open(). |
launcherPosition |
строка | right |
right или left. |
theme |
строка | light |
light, dark или auto. auto ориентируется на яркость body страницы (затем html) при каждом открытии окна; нечитаемый фон считается светлым. |
accent |
строка | «чернильный» (#18181B) |
Любой CSS-цвет. Красит главную кнопку, полосу прогресса и фокус. Цвет текста на нём подбирается для читаемого контраста. Недопустимое значение игнорируется. |
container |
строка | body |
CSS-селектор элемента, в который монтируется виджет. Если ничего не найдено, используется body. |
cabinet |
boolean | false |
true включает «Мои образы» и «Запомнить это фото»: результаты и, только если покупатель отметил галочку, его фото хранятся в браузере покупателя (IndexedDB) до 30 дней. При false виджет не хранит в браузере ничего, кроме записи об идущей примерке (см. Примерка переживает своё окно). |
debug |
boolean | false |
Пишет в консоль диагностические строки с префиксом [TryOn]. |
Ограничения по времени¶
Все значения — положительное число миллисекунд (числовая строка преобразуется). Всё остальное в init() заменяется
значением по умолчанию, а в update() игнорируется.
| Опция | По умолчанию | Что ограничивает |
|---|---|---|
submitTimeoutMs |
30000 |
Ожидание ответа на отправку примерки и заголовков сохраняемой картинки результата. |
pollTimeoutMs |
15000 |
Каждый запрос статуса. |
bodyTimeoutMs |
15000 |
Чтение небольшого JSON-ответа после получения заголовков. |
resultImageTimeoutMs |
120000 |
Загрузка картинки результата для копии в «Моих образах». |
attemptMaxMs |
2700000 (45 мин) |
Как долго отправленная примерка отслеживается, считая с момента отправки. Потом попытка заканчивается событием error с code равным TIMEOUT. |
lookupGraceMs |
120000 (2 мин) |
Сколько времени ответ «не найдено» ещё не считается окончательным, когда виджет ищет примерку с потерянным ответом, и сколько он ждёт хоть какого-то ответа сервера, прежде чем сообщить о потере соединения. |
overloadedWindowMs |
60000 |
Окно, в котором отклонённая примерка отправляется повторно (см. Сервер занят). |
Менять их приходится редко. attemptMaxMs соответствует самому долгому времени жизни задания на нашей стороне.
Пример¶
<script src="https://api.ziex-tryon.com/embed/tryon-embed.js"></script>
<script>
window.TryOn.init({
apiKey: 'pk_YOUR_PUBLISHABLE_KEY',
productImage: 'https://shop.example/img/blue-jacket.jpg',
productTitle: 'Синяя куртка',
productUrl: 'https://shop.example/blue-jacket',
category: 'outerwear',
lockCategory: true,
lang: 'ru',
theme: 'auto',
accent: '#0F766E',
cabinet: true
});
</script>
Методы и свойства¶
| Член | Описание |
|---|---|
TryOn.init(options) |
Запускает виджет. См. выше. |
TryOn.open() |
Открывает окно. До init() ничего не делает. Так окно открывают при launcher: false. |
TryOn.close() |
Закрывает окно. Уже отправленная примерка продолжает работать (см. ниже). |
TryOn.update(options) |
Меняет работающую конфигурацию без перерисовки. Принимает опции init(); меняются только переданные ключи. Если вызвать до init(), работает как init(). |
TryOn.destroy() |
Убирает виджет со страницы и завершает отправленную примерку. Также снимает все слушатели событий. |
TryOn.on(event, fn) |
Подписка на событие. Одна и та же функция подписывается один раз. |
TryOn.off(event, fn) |
Отписка. Без fn снимает все слушатели этого события. |
TryOn.version |
Версия виджета строкой в формате semver (например 1.2.0). Читается до init(). |
update() или destroy()¶
update() и destroy() не взаимозаменяемы.
update()только меняет конфигурацию. Ничего не перерисовывается, а уже отправленная примерка сохраняет ключ, адрес API и товар и доходит до конца. Новые значения применяются при следующем открытии или отправке. В открытом окне новая картинка вещи показывается сразу.destroy()завершает отправленную примерку. Её задание уже создано и оплачено, поэтому результат теряется, а запись, которую виджет ведёт для неё, удаляется. Следующийinit()ничего не подхватит.
Одностраничные сайты
Когда покупатель переключает товар или вариант или меняется маршрут, вызывайте TryOn.update({ … }). Не вызывайте
destroy() с последующим init() и не вызывайте destroy() из слушателя closed. И то и другое выбрасывает
примерку, за которую покупатель уже заплатил.
// Переключён вариант на одностраничной странице товара:
window.TryOn.update({
productImage: variant.imageUrl,
productTitle: variant.title,
productUrl: variant.url
});
События¶
window.TryOn.on('result', function (data) {
console.log(data.result_url, data.job_id, data.processing_time_ms, data.background);
});
window.TryOn.on('error', function (err) {
console.warn(err.code, err.message, err.detail, err.background);
});
| Событие | Данные | Когда |
|---|---|---|
opened |
нет | Окно открыто. |
closed |
нет | Окно закрыто. |
upload_started |
нет | Покупатель нажал «Примерить», виджет начал отправку. Один раз на примерку. |
upload_completed |
статус завершённого задания | Задание успешно завершено. Приходит вместе с result. |
result |
{ result_url, job_id, processing_time_ms } |
Результат готов. |
error |
объект Error |
Примерка не удалась или время ожидания вышло. |
ready |
{ job_id, kind, background } |
Исход ждёт следующего открытия окна. См. ниже. |
Подробности:
result:result_url— адрес картинки. Считайте его временным и не стройте на нём собственное хранилище.processing_time_ms— время обработки задания.error— объектError.err.message— текст, который сам виджет показывает покупателю на языке страницы.err.codeравенTIMEOUT, когда время ожидания вышло,OVERLOADED, когда сервер оставался занятым во всех повторах, а в остальных случаях — коду неудавшегося задания.err.detailсодержит сырой текст сервера только для ваших логов: покупателям его показывать нельзя.background: true. Отправленная примерка отслеживается и при закрытом окне. События, пришедшие в этот момент, несут в данныхbackground: true(err.backgroundдляerror). При открытом окне флага нет, поэтому слушатели, написанные без учёта флага, продолжают работать.ready. Исход (результат или ошибка) пришёл при закрытом окне или на более ранней странице той же вкладки.kind—resultилиerror;backgroundвсегдаtrue. Событие приходит после собственных событий исхода (upload_completed,resultилиerror), поэтому страница, которая открывает окно в слушателеready, не получит этих событий с флагом фона для исхода, который уже на экране. Следующая страница вкладки повторно его не отправляет.- Тайминг. Если задание завершилось при открытом окне,
resultприходит через доли секунды после финального статуса, а при медленной загрузке картинки — до 5 секунд спустя.
Исключения, брошенные слушателем, перехватываются и на виджет не влияют.
Примерка переживает своё окно¶
С момента отправки примерки задание существует на нашей стороне и оплачено. Поэтому виджет не отменяет ничего, когда покупатель закрывает окно:
- Виджет продолжает следить за заданием в фоне. События
upload_completed,resultиerrorпо-прежнему приходят, сbackground: true. - Исход показывается при следующем открытии окна. Если включена галерея «Мои образы», результат сохраняется туда как обычно.
- Фото, которые ещё готовятся, — другое дело: ничего не отправлено, поэтому закрытие окна отменяет такую попытку и ничего не отправляет.
- Одновременно идёт только одна примерка. Пока она выполняется, повторное нажатие «Примерить» отклоняется.
Она переживает смену страницы в той же вкладке. Перед отправкой виджет записывает небольшую запись в
sessionStorage вкладки: ключ идемпотентности, номер задания, когда оно известно, время отправки, оценку времени,
категорию и товар. Фото, ключа API и адреса API в записи нет. Следующая страница сайта, которая вызывает TryOn.init()
с тем же ключом, подхватывает запись и продолжает следить за заданием. Подходят перезагрузка страницы, переход на другую
страницу товара и навигация внутри одностраничного сайта. Закрытая вкладка теряет запись; тогда результат виден только в
клиентском портале и в истории заданий.
Пока исход ждёт, виджет:
- ставит значок на launcher и меняет его доступное название;
- выставляет на элементе
<html>атрибутdata-tryon-ready:resultилиerror; - отправляет
ready(один раз, на странице, где пришёл исход).
/* Отметьте свою кнопку при launcher: false или с собственным триггером */
html[data-tryon-ready] .my-tryon-button::after {
content: "";
display: inline-block;
width: 8px;
height: 8px;
margin-left: 6px;
border-radius: 50%;
background: #16a34a;
}
html[data-tryon-ready="error"] .my-tryon-button::after { background: #dc2626; }
Флаг остаётся выставленным на каждой следующей странице вкладки, пока покупатель не откроет окно и не увидит исход. События исхода уходят один раз, на странице, где он пришёл; следующие страницы лишь показывают значок и флаг.
Если ответ на отправку потерялся (ошибка прокси, оборванное соединение, таймаут), виджет не сообщает о сбое, а ищет
примерку по её ключу идемпотентности: одно нажатие не превращается в два списания, и результат не пропадает. Если сервер
вообще недоступен в течение lookupGraceMs, виджет сообщает о потере соединения.
Сервер занят¶
Когда все слоты обработки изображений на сервере заняты, API отклоняет примерку ответом 503 с кодом OVERLOADED.
Ничего не создаётся и не списывается, поэтому виджет отправляет тот же запрос с тем же ключом идемпотентности после
паузы Retry-After (не меньше 1 секунды, не больше 30, а при отсутствии заголовка — 5 секунд). Повторов не больше 3,
и только пока повтор укладывается в 60 секунд от первой отправки (overloadedWindowMs). Во время ожидания окно сообщает,
что сейчас много примерок, не показывает проценты и просит остаться на странице. Если повторы исчерпаны, попытка
завершается событием error с code равным OVERLOADED и кнопкой повтора. Закрытие окна повторы не останавливает;
destroy() останавливает.
Тестовые ключи¶
Ключ, начинающийся с pk_test_ или tryon_test_, добавляет в шапку окна значок «ТЕСТ». Примерка с тестовым ключом не
уходит в модель и не списывается; результат — фиксированная картинка-заглушка. Значок следует за ключом, с которым
отправлена текущая примерка, поэтому update({ apiKey }) показывает или убирает его в открытом окне сразу. tryon_test
без завершающего подчёркивания — обычный ключ.
Оформление¶
По умолчанию окно светлое и рисуется только из CSS-переменных. Виджет ставит на свой хост-элемент класс
ziex-tryon-host. Задавайте переменные на этом классе в собственной таблице стилей:
.ziex-tryon-host {
--tryon-accent: #0F766E;
--tryon-radius: 8px;
--tryon-font: "Inter", system-ui, sans-serif;
--tryon-launcher-bottom: 96px;
}
| Переменная | Что задаёт |
|---|---|
--tryon-accent, --tryon-accent-fg |
Главная кнопка, полоса прогресса, фокус и цвет текста на них. Опция accent задаёт обе; если вы задаёте в CSS только --tryon-accent, задайте и --tryon-accent-fg (на светлой теме по умолчанию белый). |
--tryon-bg, --tryon-surface, --tryon-fg, --tryon-muted, --tryon-border |
Фон окна, панели, текст, второстепенный текст, линии. |
--tryon-danger, --tryon-success |
Цвета ошибки и успеха. |
--tryon-font |
Семейство шрифта. |
--tryon-radius |
Радиус скругления окна и его элементов. |
--tryon-scrim |
Цвет затемнения страницы за окном. |
--tryon-shadow-window, --tryon-shadow-launcher |
Тени. |
--tryon-launcher-bottom, --tryon-launcher-side |
Отступ launcher от края страницы. |
Заданные вами цветовые переменные действуют в обеих темах, поэтому проверяйте контраст в той теме, которую видят ваши
покупатели. Надёжнее опция accent: она ещё и подбирает читаемый цвет текста.
На телефоне (480 px и уже) окно становится листом на всю высоту, а страница за ним не прокручивается, пока окно открыто.
Совместимость¶
TryOn.version следует семантическому версионированию. В пределах мажорной версии опции и события только
добавляются: могут появиться новые необязательные опции и события, существующие сохраняют смысл. Запись подхвата,
флаг html[data-tryon-ready], событие ready и интерфейс лоадера заморожены для версии 1; см.
Отложенную загрузку и подхват.