Интерфейсы, преобразования, масштабирование и метод…

Привет! Последние несколько дней я работал над проектом, в котором мне нужно было реализовать бесконечную доску и размещать стикеры. Идея состоит в том, чтобы создать компонент, который можно было бы прокручивать бесконечно. Пользователь должен иметь возможность перетаскивать виджеты внутри него в любое место в этом бесконечном пространстве. Более того, довольно интересно становится добавить инструмент масштабирования, удобный инструмент для таких приложений. Я довольно много узнал о механизмах трансформации и масштабирования при создании этого проекта. Читайте дальше, чтобы узнать, как это сделать с нуля!

Структура компонентов

Начнем с того, что определимся, какие модели и компоненты потребуются. Ясно, что очевидны две вещи: необходимы Notes для представления перетаскиваемых виджетов и Board, чтобы обернуть все, что внутри него. Глядя на нашу цель - бесконечное пространство для перетаскивания, наш контейнер Notes должен расширяться сам по себе, а Board должен позволять нам прокручивать до любой области большой доски. . Этого можно добиться, добавив еще один промежуточный компонент-оболочку: Background. Этот компонент будет расширяться при необходимости, тем самым давая ощущение бесконечного пространства.

Наша окончательная структура компонентов в восходящем порядке будет выглядеть следующим образом:

  1. Примечание. Это виджет, который можно перетаскивать.
  2. Фон: содержит все узлы. По умолчанию он занимает 100% ширины и высоты своего родительского элемента: Board. Он будет расширяться, когда узел перетаскивается за его пределы.
  3. Плата: супер-самая обертка для всех остальных компонентов. Его ширина и высота фиксированы. Когда Фон раскрывается, полосы прокрутки автоматически запускаются в Доске.

Как реализовать эффект бесконечного пространства?

Присмотревшись, мы обнаруживаем, что есть всего 4 случая, когда нам нужно увеличить размеры Background: переполнение в любых 4 направлениях, вверх, вниз, влево и вправо. Это можно сделать, просто отслеживая текущую позицию перетаскиваемой заметки и проверяя, находится ли она рядом с положением границы Background или нет. Если да, увеличьте ширину и высоту по мере необходимости.

При увеличении ширины или высоты автоматически появляются полосы прокрутки.

Как изменить масштаб при наведении курсора мыши?

Это немного сложно реализовать. Я дам обзор, который поможет вам начать работу. Когда мы говорим, что хотим увеличить в какой-то момент, мы хотим, чтобы все было увеличено. Точнее, каждый объект увеличивается относительно нашего «ZoomCenter». Или мы можем сказать, что каждая точка удаляется на определенное расстояние от нашего ZoomCenter.

Этого можно достичь, преобразовав наши значения (x, y), используя:

newX = zoomFactor * (x-zoomCenter.x) + zoomCenter.x

newY = zoomFactor * (y-zoomCenter.y) + zoomCenter.y

newWidth = zoomFactor * ширина

newHeight = zoomFactor * высота

Но здесь есть одна загвоздка. Наше значение по умолчанию zoomFactor равно 1.0.

Когда мы хотим увеличить масштаб, мы увеличиваем наш zoomFactor на 0,1.

Когда мы хотим уменьшить масштаб, мы уменьшаем наш zoomFactor на 0,1.

Теперь предположим, что мы увеличили масштаб до 1.1. Теперь, когда мы уменьшаем масштаб, наш zoomFactor станет 1.0. Согласно нашим операциям преобразования выше, никаких изменений не происходит, потому что множитель 1.0 ничего не меняет. С этим можно справиться, сбросив наши значения до значений по умолчанию перед применением преобразований.

Преобразование можно сбросить, просто разделив его на последний zoomFactor. Прежде чем применить преобразование, используя приведенные выше правила, мы сбросим значения, используя следующие преобразования:

x = ((x-lastZoomCenter.x) / oldZoomFactor) + lastZoomCenter.x

y = ((y-lastZoomCenter.y) / oldZoomFactor) + lastZoomCenter.y

width = width / oldZoomFactor

height = height / oldZoomFactor

Вот и все. Это должно вас подтолкнуть.

Модели, контроллеры и утилиты

Позиция
Простая модель Position будет использоваться для представления координат на доске.
Нам нужно будет отслеживать движения мыши и поддерживать начальные и конечные состояния курсора при перетаскивании. Это делается путем создания MouseController.

MouseController
Он помогает нам моделировать поведение мыши. Это логически достигается за счет сохранения последних координат «mousedown» и сохранения «текущей» позиции курсора. Кроме того, мы будем использовать этот контроллер только во время события «перетаскивание». Вот почему у нас есть флаг «isPressed».

Примечание
Эта заметка завершает атрибуты нашего текстового материала. У нас будет контроллер для этой модели, с которым пользователь будет напрямую взаимодействовать большую часть времени.
Здесь предоставляется небольшое взаимодействие с DOM, чтобы упростить перемещение заметок с помощью BoardController.

Прежде чем я перейду к нашим последним компонентам: NoteController и BoardController, давайте разберем поведение нашего приложения:

  1. Заметки с перетаскиванием: нам нужно знать точку начала и точку перетаскивания заметки. Положение заметки будет использоваться для отображения заметок на экране. Эту операцию можно выполнить с помощью описанных выше Position, MouseController и Note. Однако нам все еще нужно сообщить Примечание, что «событие перетаскивания» было инициировано. Это будет сделано с помощью NoteController, который будет определен ниже.
  2. Обнаружение перетаскивания за пределы границы: должен быть способ определить, находится ли узел в «пограничной области», чтобы наш элемент Background мог увеличивать свою ширину или высоту.

Нет необходимости иметь отдельный компонент Angular для представления Background. Его можно напрямую интегрировать в HTML-компонент Board.

Взаимодействия BoardController-NoteController

Идея состоит в том, чтобы представить NoteController как «сенсорный» элемент, который будет отправлять сигнал события Drag-And-Drop в BoardController. Вся логика Notes, включающая размещение заметок и обработку перетаскивания за пределы границы, определяется внутри BoardController.

Вот обзор того, как NoteController отправляет сигналы.

Здесь ,

  • startDrag () вызывается в «mousedown».
  • drag () вызывается в «mousemove» . Он просто отправляет сигнал BoardController о том, что этот узел был перетащен.
  • stopDrag () вызывается для «mouseup».

BoardController обрабатывает все логические преобразования. Более того, написание всего модульного с использованием архитектуры MVC упростит кодирование.

Надеюсь, эти примеры фрагментов кода дадут вам представление о реальной реализации.

Вот и все.
Бойбой.