{"id":51440073,"url":"https://github.com/alexstep/sensor","last_synced_at":"2026-07-05T10:30:46.497Z","repository":{"id":357938863,"uuid":"1165962648","full_name":"alexstep/sensor","owner":"alexstep","description":"Gyroscope css effects","archived":false,"fork":false,"pushed_at":"2026-05-14T22:10:22.000Z","size":43,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-15T00:26:26.908Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexstep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-24T18:24:33.000Z","updated_at":"2026-05-14T22:10:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alexstep/sensor","commit_stats":null,"previous_names":["alexstep/sensor"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/alexstep/sensor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexstep%2Fsensor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexstep%2Fsensor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexstep%2Fsensor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexstep%2Fsensor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexstep","download_url":"https://codeload.github.com/alexstep/sensor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexstep%2Fsensor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35151638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-05T10:30:46.413Z","updated_at":"2026-07-05T10:30:46.487Z","avatar_url":"https://github.com/alexstep.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Гироскоп и CSS\n\nДополнение к статье [Оживляем UI на мобилках с Sensor API](https://habr.com/ru/articles/1010328/)\n\nПример создания реалистичных бликов и световых эффектов на CSS, управляемых наклоном устройства. Данные с гироскопа транслируются в CSS-переменные, а визуальные эффекты строятся на чистом CSS через `calc()`, `radial-gradient`, `linear-gradient` и `box-shadow`.\n\n## Демо\n\n[Демку](https://alexstep.github.io/sensor/) лучше смотреть с мобилки, а ещё лучше открыть [миниапп в телеграм](https://t.me/calendar0bot/ui_example)\n\n\n## Быстрый старт\n\nПромпт для AI агента:\n```\nДобавь эффекты блеска UI, реагирующие на наклон устройства.\nИнструкция: https://raw.githubusercontent.com/alexstep/sensor/main/AI.md\n```\n\nили ручками:\n```html\n\u003cscript src=\"sensors.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const gyro = new GyroShine()\n  gyro.start()\n  gyro.on('change', e =\u003e {\n    document.documentElement.style.setProperty('--gyro-gamma-percent', e.detail.gammaPercent)\n    document.documentElement.style.setProperty('--gyro-beta-percent', e.detail.betaPercent)\n  })\n\u003c/script\u003e\n```\n\n## API сенсоров\n\n`GyroShine` автоматически выбирает лучший доступный источник (в порядке приоритета):\n\n| Приоритет | Источник | Где работает |\n|-----------|----------|--------------|\n| 1 | Telegram Mini Apps API | Telegram iOS/Android |\n| 2 | GravitySensor / Accelerometer | Chrome, Android |\n| 3 | deviceorientation | Safari, Firefox и др. |\n\nНа iOS Safari автоматически запрашивается разрешение пользователя.\n\n## API\n\n### Конструктор\n\n```js\nconst gyro = new GyroShine({\n  refreshRate: 42,        // частота опроса датчиков (мс)\n  animate: false,         // JS-интерполяция значений\n  useSpring: true,        // пружинная физика (false = линейная интерполяция)\n  stiffness: 0.12,        // жёсткость пружины (0.01–0.3)\n  damping: 0.82,          // затухание пружины (0.5–0.95)\n  lerpSpeed: 0.09,        // скорость lerp (если useSpring = false)\n  minBatteryLevel: 0.4,   // мин. заряд батареи (0 = не проверять)\n  debug: false,           // вывод логов в консоль\n})\n```\n\nВсе параметры доступны через `gyro.config`:\n\n```js\ngyro.config.refreshRate   // 42\ngyro.config.useSpring     // true\ngyro.config.debug         // false\n```\n\n### Методы\n\n| Метод | Описание |\n|-------|----------|\n| `gyro.start()` | Запуск отслеживания (async, выбирает источник автоматически) |\n| `gyro.stop()` | Остановка всех датчиков и анимации |\n| `gyro.on(event, cb)` | Подписка на событие |\n| `gyro.off(event, cb)` | Отписка от события |\n\n### События\n\n**`change`** - вызывается при изменении наклона:\n\n```js\ngyro.on('change', e =\u003e {\n  e.detail.gammaPercent // \"0.00\"–\"100.00\", горизонтальный наклон (лево-право)\n  e.detail.betaPercent  // \"0.00\"–\"100.00\", вертикальный наклон (вперёд-назад)\n})\n```\n\nНейтральное положение устройства - `50.00` / `50.00`.\n\n**`lowbattery`** - вызывается при низком заряде (датчики автоматически останавливаются):\n\n```js\ngyro.on('lowbattery', e =\u003e {\n  console.log('Заряд:', e.detail.level)\n})\n```\n\n### Свойства\n\n```js\ngyro.animate = true              // включить/выключить JS-интерполяцию (getter/setter)\ngyro.config.useSpring = false    // переключить режим интерполяции\ngyro.config.stiffness = 0.05     // изменить жёсткость пружины\ngyro.config.damping = 0.9        // изменить затухание\ngyro.config.debug = true         // включить логи\n```\n\n## Создание CSS-эффектов бликов\n\n### Принцип работы\n\n1. JS обновляет две CSS-переменные на `:root`:\n   - `--gyro-gamma-percent` - горизонтальный наклон (0–100)\n   - `--gyro-beta-percent` - вертикальный наклон (0–100)\n\n2. CSS использует эти переменные через `calc()` для смещения градиентов, теней и позиций.\n\n3. Все эффекты оборачиваются в `@media (prefers-reduced-motion: no-preference)` для accessibility.\n\n### Базовый шаблон\n\n```css\n:root {\n  --gyro-gamma-percent: 50;\n  --gyro-beta-percent: 50;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  .card {\n    --g-offset: calc(var(--gyro-gamma-percent) - 50);\n    --b-offset: calc(var(--gyro-beta-percent) - 50);\n\n    /* эффекты используют --g-offset и --b-offset */\n  }\n}\n```\n\n`--g-offset` и `--b-offset` - это смещение от центра (от -50 до +50), удобное для расчётов.\n\n### Рецепты эффектов\n\n#### 1. Динамическая тень (параллакс глубины)\n\nТень смещается в противоположную от наклона сторону, создавая иллюзию объёма:\n\n```css\n.card {\n  box-shadow:\n    calc(var(--g-offset) * -0.15px) calc(1px + var(--b-offset) * -0.1px) 0px #24246855,\n    calc(var(--g-offset) * -0.5px)  calc(15px + var(--b-offset) * -0.5px) 25px rgba(0,0,0,0.5);\n}\n```\n\nМножитель (`-0.15px`, `-0.5px`) задаёт интенсивность реакции.\n\n#### 2. Световое пятно (radial-gradient на ::before)\n\nМягкий блик, следующий за наклоном - имитация отражения источника света:\n\n```css\n.card::before {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  pointer-events: none;\n  background: radial-gradient(\n    ellipse 120% 100% at\n      calc(50% + var(--g-offset) * -1%)\n      calc(15% + var(--b-offset) * -0.6%),\n    rgba(255,255,255,0.15) 0%,\n    rgba(255,255,255,0.05) 40%,\n    transparent 70%\n  );\n}\n```\n\nКлючевое - позиция центра `at X Y` привязана к переменным наклона.\n\n#### 3. Скользящая линия-блик (edge highlight)\n\nТонкая белая полоса, скользящая по краю элемента:\n\n```css\n.card::after {\n  content: \"\";\n  position: absolute;\n  right: 0;\n  top: calc(95% + var(--gyro-beta-percent) * -1.5%);\n  width: 1px;\n  height: 60%;\n  background: linear-gradient(to top, #0000 10%, #fff1 40%, #fff3 55%, #fff1 70%, #0000 90%);\n  opacity: 0.8;\n  pointer-events: none;\n}\n```\n\n#### 4. Бегущий блик по разделителю\n\nГоризонтальная полоса света, которая «скользит» по border между секциями:\n\n```css\n.separator::after {\n  content: \"\";\n  position: absolute;\n  top: -1px;\n  left: calc(140% + var(--gyro-gamma-percent) * -2%);\n  width: 50%;\n  height: 1px;\n  background: linear-gradient(90deg, #0000 5%, #fff1 25%, #fff5 45%, #fff5 55%, #fff1 75%, #0000 95%);\n  pointer-events: none;\n}\n```\n\nБольшой `left` со смещением через `--gyro-gamma-percent` заставляет полосу «выезжать» в видимую область при наклоне.\n\n#### 5. Блик на кнопке (многослойный gradient)\n\nКомбинация из трёх слоёв: диагональный блик + верхний край + боковой край:\n\n```css\n.button::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  pointer-events: none;\n  background:\n    /* диагональный блик */\n    linear-gradient(\n      102deg,\n      #fff1 calc(-5% + var(--g-offset) * -4%),\n      #fff4 calc(40% + var(--g-offset) * -4%),\n      #fff7 calc(56% + var(--g-offset) * -4%),\n      #fff1 calc(105% + var(--g-offset) * -4%)\n    ),\n    /* верхний блик (1px) */\n    linear-gradient(to right,\n      #0000 calc(6% + var(--g-offset) * -2.85%),\n      #fff7 calc(48% + var(--g-offset) * -2.2%),\n      #0000 calc(96% + var(--g-offset) * -2.85%)\n    ) 0 0 / 100% 1px no-repeat,\n    /* правый блик (1px) */\n    linear-gradient(to bottom,\n      #0000 calc(6% + var(--b-offset) * 2.1%),\n      #fff7 calc(50% + var(--b-offset) * 2.55%),\n      #0000 calc(96% + var(--b-offset) * 2.1%)\n    ) 100% 0 / 1px 100% no-repeat;\n}\n```\n\n### Советы\n\n- **`will-change`** - указывайте на элементах с частым обновлением (`will-change: background, box-shadow`), но не злоупотребляйте.\n- **Множитель смещения** - чем больше коэффициент при `--g-offset`, тем сильнее реагирует эффект. Начинайте с `0.5–1%` и подбирайте визуально.\n- **Укороченные цвета** - `#fff3`, `#0000` - это CSS Color Level 4 (4-значный hex с альфа-каналом), поддерживается всеми современными браузерами.\n- **`pointer-events: none`** - обязательно на всех псевдоэлементах с эффектами, чтобы не блокировать клики.\n- **Статический fallback** - вне медиа-запроса `prefers-reduced-motion` задайте статичные тени и бордеры, чтобы интерфейс выглядел нормально без анимации.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexstep%2Fsensor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexstep%2Fsensor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexstep%2Fsensor/lists"}