LeCodesdocs

Устройство

Информация о платформе, текущий размер дисплея, связность и событие ресайза от хоста. device — обычный глобальный объект, ничего создавать не нужно.

Обзор

TypeScript
if (device.platform === 'ios') toast('running on iOS')

const layout = (w: number, h: number) => {
  console.log('display is', w, 'x', h, 'logical px')
}
layout(device.width, device.height)               // читается в любой момент
device.addEventListener('resize', layout)         // и снова при каждом ресайзе

Инфо о платформе

TypeScript
device.platform     // "web" | "android" | "ios" | string  (только чтение)
device.language     // код языка хоста, напр. "en"          (только чтение)
device.pixelRatio   // физических px на логический px; 1 на обычных дисплеях, 2–3 на retina/iOS

pixelRatio — коэффициент, с которым запекать Canvas для чёткого вывода без хардкода: new Canvas(w, h, { pixelRatio: device.pixelRatio }). Откатывается к 1, если хост его не сообщает.

Note

веб-поверхность 2D/GL сейчас рендерит в логическом разрешении, поэтому на вебе pixelRatio помогает только UI-канвасам; на iOS (физическая поверхность) он делает 2D-канвасы чёткими.

Размер дисплея

TypeScript
device.width        // текущая ширина дисплея, логические px  (только чтение)
device.height       // текущая высота дисплея, логические px (только чтение)

Те же значения, что доставляет событие resize, но читаемые в любой момент — не только внутри слушателя. Оба — логические px (как clientX/clientY), никогда не физические пиксели.

Note

оба равны 0, пока хост не сообщил размер. Не строй layout из device.width в начале файла, который выполняется до первого кадра — подпишись ещё и на resize.

Событие resize

TypeScript
device.addEventListener('resize', (width, height) => { })   // логические px
device.removeEventListener('resize', callback)              // та же ссылка на функцию

Срабатывает при любом изменении вьюпорта хоста (поворот, ресайз окна).

Связность: device.online и события online / offline

TypeScript
device.online                                    // boolean (только чтение)
device.addEventListener('offline', () => toast('No connection'))
device.addEventListener('online', () => sync())

Семантика navigator.onLine по мере возможностей: false — только когда платформа уверена, что сети нет; true не гарантирует, что интернет доступен — относись к нему как к подсказке для UI (баннеры «ты офлайн», откладывание синхронизации) и всё равно обрабатывай ошибки fetch.

Note

гейтится хостом — читается как true, и события никогда не срабатывают на хостах, которые не отслеживают связность (headless).

Точный тач

TypeScript
device.setPreciseTouch(enabled: boolean)    // ВЫКЛ по умолчанию

Подключи систему точного тача там, где платформа её поддерживает. Когда включено, быстрые штрихи сэмплируются на полной частоте дигитайзера (объединённые касания iOS, ≈120–240 Гц), а не раз в кадр дисплея (~60 Гц) — приложение с активным вводом (рисование, рукописный ввод, перетаскивание) получает больше точек и более гладкие линии. Для tap/кнопочных UI оставь выключенным.

Note

гейтится хостом — тихий no-op на хостах без понятия объединённого ввода (веб уже объединяет pointermove; headless).

Хаптика

TypeScript
device.vibrate()             // удар "medium" по умолчанию
device.vibrate("selection")  // лёгкий тик для смены значения

Выдай одиночный хаптик семантического style (по умолчанию "medium"):

Группа Стили Смысл
Impact "light" "medium" "heavy" "soft" "rigid" физический «тап» разного веса
Notification "success" "warning" "error" сигнал об исходе
Selection "selection" лёгкий тик для меняющегося значения

API семантический, а не длительность/паттерн, намеренно: это единственный словарь, который ощущается нативно и на iOS (Taptic Engine / UIFeedbackGenerator), и на Android (HapticFeedbackConstants / VibrationEffect). Длительность vibrate(ms) была бы честной только на вебе/Android — у iOS нет публичного API для вибрации произвольной длины.

Note

гейтится хостом — тихий no-op там, где нет хаптического железа (iPad, старые iPhone, веб, headless). Также уважает системную настройку хаптики пользователя.

Движение

Сфьюженный датчик ориентации устройства (гироскоп + акселерометр) — для управления наклоном/рулением и magic-window / 360°-панорам.

TypeScript
await device.motion.start()          // начать обновления (батарея); Promise<boolean> — false = нет сенсора

setLoop(() => {
  scene.camera.quaternion = device.motion.attitude   // камера 360°-панорамы, одна строка
  // или 2D-игра с наклоном:
  ball.x += device.motion.gravity.x
  ball.y += device.motion.gravity.y
})

device.motion.recenter()             // сделать «здесь» направлением вперёд (только yaw)
device.motion.stop()                 // освободить сенсор
Член Тип Примечания
start(options?) Promise<boolean> Начать обновления. false = нет сенсора / в разрешении отказано.
stop() void Остановить обновления, освободить сенсор.
recenter() void Обнулить текущее направление (pitch/roll остаются привязаны к гравитации).
available boolean Есть ли у устройства гироскоп вообще?
enabled boolean Идут ли обновления сейчас?
attitude Quat Текущая ориентация. По умолчанию мировой фрейм — присваивай прямо в quaternion узла/камеры.
gravity Vec3 Направление гравитации в экранном пространстве (x → вправо, y → вниз); читай x/y для 2D-наклона.

start(options) принимает { interval?: number, frame?: "world" | "device" }. interval (секунды, по умолчанию 1/60) — нижняя граница: сенсор фьюзит в фоне на частоте не ниже её, а ты опрашиваешь самый свежий сэмпл каждый кадр, так что частота сенсора не обязана совпадать с частотой кадров. frame по умолчанию "world": attitude/gravity приходят уже преобразованными в Y-up мировое пространство движка (а gravity — в экранное) и знают об ориентации — поверни телефон в ландшафт, и камера останется вертикальной автоматически. Передай "device" для сырого фрейма сенсора, если хочешь считать сам.

Опрашивай внутри setLoop; читаемые поля всегда возвращают последний сфьюженный сэмпл. attitude скорректирован от дрейфа, так что горизонт панорамы остаётся ровным бесконечно.

Note

гейтится хостом — тихий no-op там, где нет гироскопа (старые iPad, веб без разрешения, headless). available скажет заранее; attitude читается как Quat.identity, а gravity — как (0,0,0), когда обновления не идут.

Смотрите также