Перетаскивание, масштабирование, поворот на холсте (вверху) — подготовка к математическим знаниям

Canvas

написать впереди

Эта статья была впервые опубликована в публичном аккаунте: CoyPan, как и ожидалось.

Недавно я сделал запрос на мобильную страницу действий, которая, вероятно, является страницей DIY. Пользователи могут перетаскивать, масштабировать и вращать материал для достижения цели «сделай сам». Нереально использовать DOM для достижения, я использую холст для достижения взаимодействия с пользователями. В процессе разработки он включает материальные элементы в холсте.Перетащите, масштабируйте, вращайтеЖдать. В этой статье будет подробноНе использует сторонние библиотекикак реализовать эти функции. Для демонстрации окончательного эффекта вы можете обратиться к изображению в формате gif выше. Адрес демо-версии здесь:Используйте мобильный телефон или браузер для имитации доступа к мобильному телефону.

В этой статье впервые представлены математические знания, которые требуют внимания во всем требовании.

анализ спроса

Общий процесс всего требования:

  1. Когда пользователь щелкает, чтобы выбрать элемент, этот элемент рисуется на холсте.
  2. Пользователь перетаскивает, масштабирует и вращает элементы на холсте.
  3. Пользователь может удалить элемент на холсте.

Чтобы реализовать взаимодействие перетаскивания, масштабирования и вращения на холсте, необходимо использовать два основных момента:

  1. Когда пользователь касается, определите, коснулся ли пользователь элемента, был ли элемент масштабирован и повернул ли кнопку.
  2. Когда пользователь перемещает палец, движение элемента контролируется в соответствии с силой пальца.

Мы знаем, что самое основное в холсте — это система координат. В этом требовании два наиболее важных момента:

  1. Как определить, коснулся ли пользователь элемента, то есть коснулсяпроблема с точкой падения.
  2. Как управлять перемещением элементов на холсте через координаты: перемещать, масштабировать, вращать.

Проблема точки падения: как определить, коснулся ли элемент

Во-первых, предоставляется простой метод: в холсте есть метод isPointInPath для определения того, находится ли точка перетаскивания в определенном месте.дорожкасередина. Однако, если элемент холста является изображением, то мы должны добавить путь для переноса каждого изображения при его рисовании. Это решение проблемы размещения. Не вдаваясь здесь в подробности, я использую следующую схему.

Для того, чтобы упростить задачу, мы можем смело предположить:

Все элементы на холсте (картинки, различная графика и т.п.) имеют прямоугольную форму. Это охватило большинство случаев. Прямоугольниккоординаты четырех вершинПоложение элемента может быть определено.

Когда пользователь касается холста, координаты касания пользователя могут быть получены через событие касания. Если координаты касания находятся «внутри» координат вершины прямоугольника, то элемент был затронут. Итак, нашу проблему можно абстрагировать следующим образом:

Зная координаты четырех вершин прямоугольника и координаты точки, как судить, находится ли точка внутри прямоугольника.

Если прямоугольник не вращается, то проблема очень проста, просто используйте горизонтальные и вертикальные координаты точки суждения, чтобы они находились в пределах горизонтальных и вертикальных координат прямоугольника. Этот метод бесполезен, если прямоугольник повернут. Чтобы решить эту проблему, начните с повторения школьной математики.

вектор

Мы можем преобразовать координаты на двумерной плоскости в векторы для вычислений, что значительно упростит задачу.

векторное произведение векторов

Результатом перекрестного произведения двух векторов является вектор, а не скаляр. Направление векторного произведения перпендикулярно плоскости, в которой лежат два вектора.

Для двух векторов на плоскости значение в третьем измерении равно 0, а значение их перекрестного произведения равно:

Другими словами, мы можем легко судить:

В плоскости, когда угол поворота не превышает 180 градусов, от одного вектора к другому вектору, по часовой стрелке или против часовой стрелки.

В качестве примера возьмем двумерную систему координат холста:

)

Можно сделать вывод, что:

Пусть в двухмерной плоскости холста значение бинома, соответствующего векторному произведению вектора A и вектора B, равно m. Если m>0, то вектор A поворачивается по часовой стрелке на угол (меньше 180 градусов) и направление вектора B может быть достигнуто. Если m

Судя по тому, что точка приземления находится внутри прямоугольника

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

Итак, есть функция, позволяющая судить [было ли прикосновение к элементу холста]:

/**
 * 判断落点是否在长方形内
 * 
 * @param {Array} point 落点坐标。 数组:[x, y]
 * @param {Array} rect 长方形坐标, 按顺序分别是:左上、右上、左下、右下。 
 *                     数组:[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
 * 
 * @return {boolean} 
 */

function isPointInRect(point, rect) {
    const [touchX, touchY] = point;
    // 长方形四个点的坐标
    const [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] = rect;
    // 四个向量
    const v1 = [x1 - touchX, y1 - touchY];
    const v2 = [x2 - touchX, y2 - touchY];
    const v3 = [x3 - touchX, y3 - touchY];
    const v4 = [x4 - touchX, y4 - touchY];
    if(
        (v1[0] * v2[1] - v2[0] * v1[1]) > 0 
        && (v2[0] * v4[1] -  v4[0] * v2[1]) > 0
        && (v4[0] * v3[1] - v3[0] * v4[1]) > 0
        && (v3[0] * v1[1] -  v1[0] * v3[1]) > 0
    ){
        return true;
    }
    return false;
}

Угол поворота

Пользователь может вращать элементы на холсте, так как же вычислить угол поворота через координаты точки касания пользователя дважды до и после?

Как видно из приведенного выше уравнения, как скалярное произведение, так и перекрестное произведение могут найти прилежащий угол. Какой выбрать?

В вращающейся сцене направление вращения (против или по часовой стрелке) очень важно, и скалярное произведение в конечном итоге получает только скаляр, у которого нет направления. Перекрестное произведение является вектором и имеет направление. Мы выбираем векторное произведение для вычисления угла поворота.

1. Как правило, центр элемента на холсте является источником вращения.Когда пользователь касается и перемещается по холсту, смещение двух точек касания до и после функции мониторинга событий очень мало, и формируется угол вектора по центру вращения должен быть меньше 90 градусов.

2. Положительные и отрицательные значения векторного произведения векторов могут определять направление вращения.

3. Функция арксинуса монотонно возрастает в диапазоне от минус 90 до 90 градусов.

Через три вышеуказанных пункта можно получить:

Поэтому в холсте можно использовать следующую функцию для вычисления угла поворота, образованного двумя последовательными точками касания и центром вращения:

/**
 * 计算旋转角度
 * 
 * @param {Array} centerPoint 旋转中心坐标
 * @param {Array} startPoint 旋转起点
 * @param {Array} endPoint 旋转终点
 * 
 * @return {number} 旋转角度
 */

function getRotateAngle(centerPoint, startPoint, endPoint) {
    const [centerX, centerY] = centerPoint;
    const [rotateStartX, rotateStartY] = startPoint;
    const [touchX, touchY] = endPoint;
    // 两个向量
    const v1 = [rotateStartX - centerX, rotateStartY - centerY];
    const v2 = [touchX - centerX, touchY - centerY];
    // 公式的分子
    const numerator =  v1[0] * v2[1] - v1[1] * v2[0];
    // 公式的分母
    const denominator = Math.sqrt(Math.pow(v1[0], 2) + Math.pow(v1[1], 2)) 
        * Math.sqrt(Math.pow(v2[0], 2) + Math.pow(v2[1], 2));
    const sin = numerator / denominator;
    return Math.asin(sin);
}

Зная угол поворота и начальные координаты, найти координаты после поворота

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

/**
 * 
 * 根据旋转起点、旋转中心和旋转角度计算旋转终点的坐标
 * 
 * @param {Array} startPoint  起点坐标
 * @param {Array} centerPoint  旋转点坐标
 * @param {number} angle 旋转角度
 * 
 * @return {Array} 旋转终点的坐标
 */

function getEndPointByRotate(startPoint, centerPoint, angle) {
    const [centerX, centerY] = centerPoint;
    const [x1, y1] = [startPoint[0] - centerX, startPoint[1] - centerY];
    const x2 = x1 * Math.cos(angle) - y1 * Math.sin(angle);
    const y2 = x1 * Math.sin(angle) + y1 * Math.cos(angle);
    return [x2 + centerX, y2 + centerY];
}

перетащите, масштабируйте

В этом требовании перетаскивание и масштабирование не очень требовательны к математике. Для перетаскивания нужно вычислить разницу между горизонтальными и вертикальными координатами точки касания и добавить ее к элементу на холсте.

написать на обороте

В этой статье в основном представлены некоторые математические знания, необходимые для реализации перетаскивания, масштабирования, вращения и других взаимодействий на холсте. Если что-то не так, пожалуйста, поправьте меня. В то же время, если у вас есть другие идеи для удовлетворения потребностей, добро пожаловать на обмен.

В следующей статье будет представлен процесс [использования математических знаний, представленных в этой статье, для реализации демонстрации в начале статьи].