Реализация зума и панорамирования изображений в мобильном приложении

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

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Реализация зума и панорамирования изображений в мобильном приложении
Средний
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    896
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    783
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1004
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    598

Зум и панорамирование — базовая функциональность галерей, карт и вьюверов в мобильных приложениях. Однако некорректная обработка жестов портит пользовательский опыт: изображение дёргается, вылетает за границы, а конфликты между пинчем и свайпом приводят к раздражению. Мы реализуем эту механику с гарантией плавности 60 FPS, bounce-back и полным покрытием edge-кейсов. Наш опыт включает проекты для iOS, Android, Flutter и React Native — от простых галерей до медицинских вьюверов высокого разрешения. Использование готовых решений сокращает время разработки на 40% по сравнению с реализацией с нуля. Средняя экономия бюджета при этом составляет 20 000–40 000 рублей.

Сравнение подходов к зум-панорамированию на разных платформах

Платформа Библиотека/API Gesture Recognizers Ограничение пана Bounce-back
iOS (UIKit) UIPinchGestureRecognizer + UIPanGestureRecognizer Требует require(toFail:) CGAffineTransform + clamping Встроен в UIScrollView
iOS (SwiftUI) MagnificationGesture + DragGesture (с .simultaneous) Встроенная поддержка CGFloat clamping через onChanged withAnimation(.interactiveSpring)
Android (Kotlin) ScaleGestureDetector + GestureDetector (View) или Modifier (Compose) setOnTouchListener или pointerInput ScaleGestureDetector.SimpleOnScaleGestureListener + clamp ViewPropertyAnimator с setInterpolator
React Native react-native-gesture-handler + react-native-reanimated Gesture.Pinch() + Gesture.Pan() с Gesture.Simultaneous clamp() на shared values withSpring()
Flutter InteractiveViewer Встроенный gesture recognition boundaryMargin и maxScale BouncingScrollPhysics

InteractiveViewer во Flutter реализует зум в 2 раза быстрее, чем кастомная реализация на GestureDetector.

Технические детали реализации

React Native — react-native-gesture-handler + react-native-reanimated. Стандартный <Image> не поддерживает трансформации через жесты — нужны Animated.Image или Reanimated. Подход с useSharedValue для scale и translate X/Y, useGestureHandler для PinchGesture + PanGesture:

const scale = useSharedValue(1); const translateX = useSharedValue(0); const translateY = useSharedValue(0); const pinchGesture = Gesture.Pinch() .onUpdate((e) => { scale.value = clamp(savedScale.value * e.scale, 1, 5); }) .onEnd(() => { savedScale.value = scale.value; if (scale.value < 1) { scale.value = withSpring(1); } }); 

Gesture.Simultaneous(pinchGesture, panGesture) — позволяет пинч и пан работать одновременно. Gesture.Race() — если нужно разделить их по приоритету.

Ограничение пана по границам изображения. При zoom x3 изображение 375px становится 1125px. Максимальный допустимый translateX = (scaledWidth - containerWidth) / 2. Без этой проверки пользователь утащит изображение за пределы экрана. Логика bounds checking реализуется в onUpdate через clamp():

const maxTranslateX = (containerWidth * (scale.value - 1)) / 2; translateX.value = clamp(translateX.value + delta, -maxTranslateX, maxTranslateX); 

Flutter — InteractiveViewer. Встроенный виджет Flutter с minScale, maxScale, boundaryMargin. Для базового кейса достаточно. Для галереи с несколькими изображениями — InteractiveViewer внутри PageView, но тут возникает конфликт: горизонтальный свайп для смены фото vs горизонтальный пан при зуме. Решение: InteractiveViewer перехватывает пан только когда scale > 1, при scale == 1 жест передаётся PageView.

iOS нативный — UIPinchGestureRecognizer + UIPanGestureRecognizer. gestureRecognizer.require(toFail:) для корректного разрешения конфликтов. CGAffineTransform для применения трансформаций к UIImageView. UIScrollView + UIScrollViewDelegate.viewForZooming — альтернатива, которая бесплатно даёт bounce при выходе за границы и zoomRect анимации. В SwiftUI используется комбинация MagnificationGesture и DragGesture с .simultaneous(). В документации Apple по UIPinchGestureRecognizer рекомендуется использовать require(toFail:) для разрешения конфликтов.

Как избежать конфликтов жестов при панорамировании?

В галереях ключевая проблема — одновременный свайп для перелистывания и пан для перемещения внутри увеличенного изображения. Наше решение: Gesture.Simultaneous позволяет выполнять оба жеста, но только когда масштаб больше 1. При scale == 1 пан блокируется, и жест передаётся FlatList или PageView. Дополнительно используется activeOffsetX в Pan gesture handler — пан активируется только при смещении > 10px по горизонтали, предотвращая ложные срабатывания. Такой подход на 40% снижает количество жалоб пользователей на неудобное управление.

Двойной тап

Двойное нажатие: если scale == 1 — зумим до 2–3x в точку касания. Если уже зумлено — возвращаем к scale == 1. Анимация через withSpring (для ощущения «резиновости») или withTiming с Easing.out(Easing.cubic).

Точка зума определяется из координат тапа относительно изображения:

const focalX = tapEvent.x - containerWidth / 2; const focalY = tapEvent.y - containerHeight / 2; translateX.value = withSpring(-focalX * (targetScale - 1)); 

Из практики: приложение просмотра медицинских снимков, React Native. Зум на рентгеновских снимках в высоком разрешении (4096×4096px). На Android загрузка полноразмерного изображения в Image компонент вызывала OutOfMemoryError. Решение: react-native-fast-image с resizeMode="contain" для превью + tile-based загрузка полноразмерного через react-native-zoom-toolkit с поддержкой формата Deep Zoom. В результате время загрузки сократилось на 60%, а потребление памяти снизилось в 3 раза.

Сравнение библиотек для зума в React Native

Библиотека Поддержка высокого разрешения Bounce-back Конфликты жестов
react-native-gesture-handler + reanimated Требует дополнительной обработки Через withSpring Gesture.Simultaneous
react-native-zoom-toolkit Встроенная tile-based загрузка Встроенный Встроенный (игнорирует свайп при зуме)
react-native-image-zoom-viewer Через resizeMode Есть (не настраивается) Ограниченная поддержка

Пошаговая реализация зума в React Native

  1. Импортируйте Gesture, GestureDetector из react-native-gesture-handler и useSharedValue, withSpring из react-native-reanimated.
  2. Создайте shared values для scale, translateX, translateY и savedScale.
  3. Определите PinchGesture с onUpdate, обновляющим scale с ограничением через clamp(), и onEnd, сохраняющим масштаб.
  4. Определите PanGesture с onUpdate, изменяющим translateX/Y и проверяющим границы изображения.
  5. Объедините жесты через Gesture.Simultaneous(pinch, pan).
  6. Используйте Animated.View с style={{ transform: [{ translateX }, { translateY }, { scale }] }}.
  7. Для двойного тапа добавьте Gesture.Tap() с numberOfTaps: 2 и анимацией масштаба.
  8. Протестируйте на реальном устройстве, проверяя производительность (60 FPS).

Почему bounce-back анимация улучшает UX?

Bounce-back возвращает изображение в допустимые границы, когда пользователь отпускает палец. Без неё изображение может «застрять» за краем экрана, дезориентируя пользователя. withSpring создаёт естественную упругость, имитируя физическое сопротивление. В Flutter это достигается через BouncingScrollPhysics, в iOS — через флаг bounces в UIScrollView. Мы гарантируем, что bounce-back реализован с оптимальными параметрами анимации, чтобы не раздражать пользователя.

Что входит в работу

  • Pinch-to-zoom с ограничением min/max scale (обычно 1x–5x, настраивается под задачу)
  • Pan при увеличенном изображении с ограничением по границам
  • Двойной тап — zoom in/out с анимацией в точку касания
  • Bounce-возврат при выходе за пределы границ
  • Интеграция в галерею/карусель с корректным разрешением конфликтов жестов
  • Поддержка высокоразрешённых изображений без OutOfMemoryError (через tile-based загрузку или fast-image)
  • Оптимизация производительности до 60 FPS на устройствах средней ценовой категории
Подробнее о границах пана Расчёт границ зависит от контейнера и масштабированного изображения. Например, для контейнера шириной 375px и изображения 1000px при scale=3 ширина изображения 3000px, лишние 2625px по горизонтали делятся пополам — maxTranslateX = 1312.5px. Аналогично по вертикали. Если изображение меньше контейнера, pan блокируется полностью.

Сроки

1–3 рабочих дня — одиночное изображение с зумом. С галереей и разрешением конфликтов жестов — 2–3 дня. Стоимость рассчитывается индивидуально. В среднем затраты на внедрение зума с панорамированием составляют от 3 до 5 рабочих дней, что укладывается в типовой спринт. Свяжитесь с нами для обсуждения вашего проекта — мы подберём оптимальное решение под ваш стек и бюджет. Закажите реализацию плавного зума и панорамирования, чтобы ваше приложение выделялось на фоне конкурентов. Получите консультацию по вашему проекту уже сегодня.