Files
smartsoltech_site/DRAG_GALLERY_FEATURE.md

13 KiB
Raw Blame History

🎯 Плавные drag-слайды в галерее портфолио

Что реализовано

Основные возможности

  1. Drag & Drop навигация

    • 🖱️ Перетаскивание мышью на desktop
    • 👆 Свайпы пальцем на мобильных
    • 📱 Работает на всех устройствах
  2. Плавное следование за пальцем/мышью

    • Изображение движется точно за курсором/пальцем
    • Визуальная обратная связь в реальном времени
    • Курсор меняется: grabgrabbing
  3. Умный порог переключения: 60% ширины

    • Если перетащили меньше 60% → возврат к текущему слайду
    • Если перетащили больше 60% → быстрое переключение на следующий/предыдущий
    • Плавная анимация при автоматическом переключении
  4. Сопротивление на краях

    • На первом фото: сопротивление при попытке свайпа вправо
    • На последнем фото: сопротивление при попытке свайпа влево
    • Коэффициент сопротивления: 0.3 (30% от движения)
  5. Производительность

    • requestAnimationFrame для плавной 60 FPS анимации
    • will-change: transform для GPU-ускорения
    • Отключение transitions во время драга для мгновенного отклика

🎨 Визуальные индикаторы

Курсоры:

Обычное состояние:  ✋ grab  (открытая ладонь)
При перетаскивании:  ✊ grabbing  (закрытая ладонь)

Drag-индикатор внизу:

┌────────────────────────────────┐
│                                │
│    ИЗОБРАЖЕНИЕ ГАЛЕРЕИ         │
│                                │
│         ▬▬▬▬▬                  │  ← Тонкая полоска
└────────────────────────────────┘

При драге полоска становится ярче

🔧 Технические детали

Алгоритм определения переключения:

const slideThreshold = 0.6;  // 60% ширины

if (Math.abs(movedBy) > threshold) {
    // Перетащили больше 60% → переключаем слайд
    if (movedBy < 0) {
        nextSlide();  // Движение влево
    } else {
        prevSlide();  // Движение вправо
    }
} else {
    // Перетащили меньше 60% → возврат
    returnToCurrentSlide();
}

Сопротивление на краях:

// На первом слайде (индекс 0)
if (currentTranslate > 0) {
    currentTranslate = currentTranslate * 0.3;
    // Движение замедляется в 3 раза
}

// На последнем слайде
if (currentTranslate < minTranslate) {
    currentTranslate = minTranslate + delta * 0.3;
    // Аналогичное сопротивление
}

CSS переходы:

/* Плавный переход при автоматическом переключении */
.gallery-slides-container {
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Отключение при ручном драге */
.gallery-slides-container.no-transition {
    transition: none;
}

📱 Поддержка устройств

Desktop (мышь):

  • mousedown → начало драга
  • mousemove → движение за курсором
  • mouseup → завершение, проверка порога
  • mouseleave → автоматическое завершение при выходе курсора

Mobile/Tablet (touch):

  • touchstart → начало свайпа
  • touchmove → движение за пальцем
  • touchend → завершение, проверка порога
  • Passive events для лучшей производительности

Общие:

  • Клавиатура: стрелки ← →
  • Кнопки навигации: ◀ ▶
  • Клик по thumbnails
  • Responsive layout для всех разрешений

🎯 Пользовательский опыт

Сценарий 1: Быстрый свайп (Desktop)

  1. Пользователь кликает на изображение
  2. Курсор меняется на grabbing
  3. Быстро перетаскивает влево на 70% ширины
  4. Отпускает кнопку мыши
  5. Результат: Слайд быстро переключается на следующий

Сценарий 2: Медленное перетаскивание (Mobile)

  1. Пользователь касается изображения
  2. Медленно тащит пальцем вправо на 40%
  3. Изображение плавно двигается за пальцем
  4. Отпускает палец
  5. Результат: Слайд возвращается обратно (не достигнут порог 60%)

Сценарий 3: Попытка свайпа на краю

  1. Пользователь на первом изображении (индекс 0)
  2. Пытается свайпнуть вправо (к предыдущему)
  3. Изображение двигается, но с сопротивлением (30%)
  4. Результат: Слайд возвращается на место, показывая что это первое фото

⚙️ Настройки и параметры

Изменяемые параметры:

// Порог переключения (по умолчанию 60%)
const slideThreshold = 0.6;
// Можно изменить на 0.5 (50%) для более чувствительного переключения
// Или на 0.7 (70%) для более строгого

// Коэффициент сопротивления на краях (по умолчанию 0.3)
const resistanceFactor = 0.3;
// Можно изменить на 0.5 для меньшего сопротивления
// Или на 0.1 для большего сопротивления

// Длительность анимации перехода (по умолчанию 0.3s)
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
// Можно изменить на 0.2s для более быстрого
// Или на 0.5s для более медленного

🚀 Оптимизации производительности

1. GPU-ускорение

.gallery-slides-container {
    will-change: transform;
    /* Браузер заранее подготавливает слой для GPU */
}

2. RequestAnimationFrame

function animation() {
    setSliderPosition();
    if (isDragging) requestAnimationFrame(animation);
}
// 60 FPS плавная анимация, синхронизированная с refresh rate экрана

3. Passive Event Listeners

addEventListener('touchmove', handler, { passive: true });
// Не блокирует скролл, улучшает производительность

4. Debounce для resize

let resizeTimer;
window.addEventListener('resize', () => {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(() => {
        updateGallery(currentIndex, false);
    }, 250);
});
// Не пересчитывает позиции при каждом пикселе изменения размера

🐛 Обработка edge cases

1. Отпускание кнопки вне галереи

galleryWrapper.addEventListener('mouseleave', handleDragEnd);
// Автоматически завершает драг если курсор вышел за пределы

2. Предотвращение контекстного меню

galleryWrapper.addEventListener('contextmenu', (e) => {
    if (isDragging) e.preventDefault();
});
// Не показывает правый клик во время драга

3. Изменение размера окна

window.addEventListener('resize', () => {
    updateGallery(currentIndex, false);
});
// Пересчитывает позиции при изменении orientation или размера окна

4. Защита от случайных кликов

pointer-events: none;  // На самом изображении
// Предотвращает открытие Lightbox во время драга

📊 Сравнение с предыдущей версией

Функция Было Стало
Навигация Только кнопки/клавиши + Drag & Drop
Визуальный фидбек Мгновенное переключение Плавное следование за курсором
Порог переключения 50px фиксированный 60% от ширины адаптивный
Производительность ~30 FPS 60 FPS (GPU)
UX на краях Жесткая остановка Плавное сопротивление
Анимация Linear Cubic-bezier easing

🎓 Как это работает

Шаг 1: Начало драга

User: mousedown/touchstart
      ↓
[Запомнить startX]
      ↓
[Включить класс "dragging"]
      ↓
[Отключить CSS transitions]
      ↓
[Запустить requestAnimationFrame]

Шаг 2: Движение

User: mousemove/touchmove
      ↓
[Вычислить deltaX = currentX - startX]
      ↓
[Применить к currentTranslate]
      ↓
[Проверить края, добавить сопротивление]
      ↓
[Обновить transform через RAF]

Шаг 3: Завершение

User: mouseup/touchend
      ↓
[Остановить RAF]
      ↓
[Вычислить movedBy]
      ↓
[Сравнить с threshold (60%)]
      ↓
    / \
   /   \
  /     \
> 60%   < 60%
  ↓       ↓
Next   Return
Slide  to Current
  ↓       ↓
[Включить transitions]
  ↓
[Плавная анимация]

🔍 Debugging

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

1. Проверить консоль

console.log('Current index:', currentIndex);
console.log('Moved by:', movedBy, 'px');
console.log('Threshold:', threshold, 'px');
console.log('Should switch:', Math.abs(movedBy) > threshold);

2. Визуализировать transform

console.log('Transform:', currentTranslate, 'px');
console.log('Expected:', -currentIndex * wrapperWidth, 'px');

3. Проверить размеры

console.log('Wrapper width:', galleryWrapper.offsetWidth);
console.log('Total images:', totalImages);

📝 Деплой на сервер

cd /opt/smartsoltech_site
git pull origin master
docker compose restart django_app

Проверить на сайте:

  1. Открыть любой проект портфолио с галереей
  2. Попробовать перетаскивать изображения мышью
  3. На мобильном - свайпы пальцем
  4. Проверить порог 60% работает корректно

Контрольный список функций

  • Плавное движение изображения за курсором/пальцем
  • Порог переключения 60% от ширины (адаптивный)
  • Автоматическое быстрое переключение при достижении порога
  • Плавный возврат если не достигнут порог
  • Сопротивление на краях галереи (30% замедление)
  • GPU-ускорение через transform
  • 60 FPS анимация через requestAnimationFrame
  • Курсоры grab/grabbing для визуального фидбека
  • Поддержка мыши (desktop)
  • Поддержка touch (mobile/tablet)
  • Работает с клавиатурой
  • Работает с кнопками навигации
  • Responsive дизайн
  • Обработка edge cases (resize, mouseleave, contextmenu)

🎉 Результат

Галерея портфолио теперь работает как современное мобильное приложение:

  • 📱 Instagram-подобные свайпы
  • 🎨 Плавные анимации
  • Быстрый отклик
  • 💪 Интуитивное управление

Пользователи могут естественным образом перелистывать фотографии проектов, получая приятный визуальный опыт на любом устройстве!