Анализ принципа жестов и практики математических знаний в HTML5
введение
В эпоху сенсорных экранов гуманизированные жесты проникли во все сферы нашей жизни. Современные приложения уделяют все больше внимания взаимодействию и опыту с пользователями.Жесты являются наиболее прямым и эффективным способом взаимодействия.Хорошее взаимодействие с помощью жестов может снизить затраты пользователя и процесс, а также значительно улучшить взаимодействие с пользователем.
В последнее время многие проекты компании имеют высокий спрос на жесты, а существующие библиотеки жестов не могут быть полностью охвачены, поэтому создается легкая и простая в использовании мобильная библиотека жестов. В этом сообщении в блоге в основном анализируются принципы общих жестов на мобильных терминалах и математические знания, используемые в процессе обучения, с точки зрения внешнего интерфейса. Я надеюсь, что это может немного вдохновить всех, и я также с нетерпением жду великих богов, указывающих на недостатки или даже ошибки, спасибо.
В основном объясните пять жестов, которые часто используются в проектах:
- тянуть:
drag
- Сожмите, чтобы увеличить:
pinch
- Вращение двумя пальцами:
rotate
- Увеличение одним пальцем:
singlePinch
- Вращение одним пальцем:
singleRotate
Tips :
потому чтоtap
а такжеswipe
Включено много базовых библиотек, поэтому они не включены для переносимости, но при необходимости могут быть расширены;
Принцип реализации
Как мы все знаем, все жесты основаны на нативных событиях браузера.touchstart
, touchmove
, touchend
, touchcancel
Осуществляется инкапсуляция верхнего уровня, поэтому идея инкапсуляции состоит в том, чтобы вызывать склад через независимые события.handleBus
, то в родномtouch
Уточненное время в событии инициирует и передает вычисленное значение параметра для завершения операции жеста. Принцип реализации относительно прост и понятен. Давайте не будем спешить. Давайте сначала проясним некоторые используемые математические понятия и объединим код, чтобы применить математику к практическим задачам. Математическая часть может быть скучной, но я надеюсь, что вы продолжите читать, и я верю в это. принесет много пользы.
Функция базовых математических знаний
Наша общая система координат принадлежит линейному пространству, или векторному пространству. Это пространство представляет собой набор точек и векторов;
Точка
Может пониматься как наша точка координат, например, начало координатO(0,0),A(-1,2)
, через собственный объект событияtouches
Вы можете получить координаты точки касания, параметрыindex
Представляет первую точку контакта;
Вектор
это система координатСегменты линии с размером и направлением, например, по происхождениюO(0,0)
точка-точкаA(1,1)
сегмент стрелки , называемый векторомa
,ноa=(1-0,1-0)=(1,1)
;
Как показано на рисунке ниже, гдеi
а такжеj
Вектор называется единичным вектором системы координат, также называемым базовым вектором.Наша общая единица системы координат равна1
,которыйi=(1,0);j=(0,1)
;
Функция для получения вектора:
вектор по модулю
представлятьдлина вектора, обозначаемый как|a|
, является скаляром только с величиной и без направления;
Геометрический смыслx,y
— гипотенуза прямоугольного треугольника с прямыми сторонами, вычисленная по теореме Пифагора;
getLength
функция:
Произведение векторов
Векторы также имеют свойства, над которыми можно работать. Они могут выполнять такие операции, как сложение, вычитание, умножение, количественное произведение и векторное произведение. Далее мы введем понятие количественного произведения, которое мы используем, также известное как скалярный продукт, который определяется как формула:
Когда a=(x1,y1),b=(x2,y2), то a·b=|a|·|b|·cosθ=x1·x2+y1·y2;
теорема о коллинеарности
коллинеарны, т. е. два вектора находятся впараллельносостояние, когдаa=(x1,y1),b=(x2,y2)
, то существует единственное действительное число λ такое, чтоa=λb
, после подстановки точки координат можно получитьx1·y2= y1·x2
;
Поэтому, когдаx1·y2-x2·y1>0
, как наклонka > kb, так что в это времяb
вектор относительноa
Вектор принадлежит вращению по часовой стрелке, иначе против часовой стрелки;
Угол поворота
Используя формулу количественного произведения, мы можем экстраполировать, чтобы найти угол между двумя векторами:
cosθ=(x1·x2+y1·y2)/(|a|·|b|);
Тогда мы можем определить направление вращения по теореме о коллинеарности, и функция определяется как:
Матрицы и преобразования
Поскольку наиболее важной особенностью пространства является то, что оно может приспосабливаться к движению, в линейном пространстве
Мы используем векторы для описания объектов, а матрицы — для описания движения объектов;
И как матрица описывает движение?
Мы знаем, что вектор может быть определен базовым вектором системы координат, напримерa=(-1,2)
, мы обычно соглашаемся с базисными векторами i = (1,0) и j = (0,1), поэтому:
a = -1i + 2j = -1(1,0) + 2(0,1) = (-1+0,0+2) = (-1,2);
Трансформация матрицы, на самом деле, состоит в том, чтобы преобразовать базовый вектор через матрицу, что завершает преобразование вектора;
Например, каштан выше, поставьтеa
Вектор преобразуется матрицей (1,2,3,0), в этот момент базисный векторi
Зависит от(1,0)
превратиться в(1,-2)
а такжеj
Зависит от(0,1)
превратиться в(3,0)
, следуя приведенному выше выводу, то
a = -1i + 2j = -1(-1,2) + 2(3,0) = (5,-2);
Как показано ниже:
Рисунок A представляет систему координат до преобразования, в настоящее времяa=(-1,2)
, после преобразования матрицы базисный векторi,j
Преобразование вызвало преобразование системы координат, которая стала следующей фигурой B, поэтомуa
вектор по(-1,2)
превратился в(5,-2)
;
На самом деле связь между вектором и системой координат не меняется (
a = -1i+2j
), это базовый вектор, который вызывает изменение системы координат, а затем система координат продолжает использовать ассоциацию, чтобы вызвать изменение вектора;
Объединить код
На самом деле CSStransform
Равные преобразования выполняются через матрицы, которые мы обычно пишемtranslate/rotate
Равный синтаксис похож на упакованный синтаксический сахар, который прост в использовании и быстро преобразуется в форму матрицы на нижнем уровне. Напримерtransform:translate(-30px,-30px)
После компиляции он будет преобразован вtransform : matrix(1,0,0,1,30,30)
;
Обычно в двумерной системе координат для описания всех преобразований достаточно только матрицы 2X2, но поскольку CSS находится в 3D-среде, в CSS используется матрица 3X3, которая выражается как:
Третий ряд0,0,1
представляет собойz
Параметры по умолчанию для осей. В этой матрице(a,b)
является координатной осьюi
база, и(c,d)
как дляj
основание,e
дляx
смещение оси,f
дляy
Смещение оси, поэтому хорошо понятен верхний каштан,translate
не привело кi,j
База изменена, просто смещение,следовательноtranslate(-30px,-30px) ==> matrix(1,0,0,1,30,30)
~
всеtransform
заявление, произойдет соответствующее преобразование, как показано ниже:
// 发生偏移,但基向量不变;
transform:translate(x,y) ==> transform:matrix(1,0,0,1,x,y)
// 基向量旋转;
transform:rotate(θdeg)==> transform:matrix(cos(θ·π/180),sin(θ·π/180),-sin(θ·π/180),cos(θ·π/180),0,0)
// 基向量放大且方向不变;
transform:scale(s) ==> transform:matrix(s,0,0,s,0,0)
translate/rotate/scale
Синтаксис очень мощный и делает наш код более читабельным и простым в написании, ноmatrix
обладает более мощными конверсионными характеристиками, благодаряmatrix
, можно трансформировать как угодно, например, наш общийзеркальная симметрия,transform:matrix(-1,0,0,1,0,0)
;
MatrixTo
Однакоmatrix
Несмотря на то, что он мощный, он не очень удобочитаем, и наши записи выполняются черезtranslate/rotate/scale
свойства, однако поgetComputedStyle
читатьtransform
но этоmatrix
:
transform:matrix(1.41421, 1.41421, -1.41421, 1.41421, -50, -50);
Как изменился этот элемент? . Это было сбито с толку. -_-|||
Поэтому у нас должен быть способmatrix
переводится на то, что нам более знакомоtranslate/rotate/scale
Поняв принцип, мы можем приступить к выполнению ~
Мы знаем, что первые 4 параметраrotate
а такжеscale
Влияние , имеет две переменные, поэтому необходимо перечислить два неравенства согласно приведенному выше преобразованию через первые два параметра:
cos(θ·π/180)*s=1,41421;
sin(θ·π/180)*s=1,41421;
Разделив два неравенства, легко найтиθ
а такжеs
, идеально! ! Функция выглядит следующим образом:
Принцип жестов
Затем мы применяем вышеуказанные функции к реальной среде, моделируем работу жестов с помощью диаграмм и кратко объясняем принципы расчета жестов. Я надеюсь, что после понимания этих основных принципов вы сможете создавать более крутые жесты, как это сделали мы вmac
То же самое используется на тачпаде.
Пример ниже:
Точка: представляет собой точку касания пальца;
Сегмент пунктирной линии между двумя точками: представляет собой вектор, образованный операцией двумя пальцами;
вектор/точка: представляет начальный вектор/начальную точку, полученную при запуске касания;
b вектор/точка B: представляет собой вектор/точку в реальном времени, полученную во время touchmove;
Формула внизу оси представляет вычисляемое значение;
Перетаскивание (событие перетаскивания)
Изображение выше представляет собой симуляцию жеста перетаскивания.A
переместить точку наB
Дело в том, что мы хотим вычислить смещение этого процесса;
Поэтому мыtouchstart
Запишите координаты начальной точки A в:
// 获取初始点A;
let startPoint = getPoint(ev,0);
затем вtouchmove
Получить текущую точку в событии и рассчитать ее в режиме реального времени△x
а также△y
:
// 实时获取初始点B;
let curPoint = getPoint(ev,0);
// 通过A、B两点,实时的计算出位移增量,触发 drag 事件并传出参数;
_eventFire('drag', {
delta: {
deltaX: curPoint.x - startPoint.x,
deltaY: curPoint.y - startPoint.y,
},
origin: ev,
});
Tips:
fire
Функция пройдена и выполненаdrag
Склад обратного вызова, соответствующий событию, может быть;
Щепотка (щипок, чтобы увеличить)
Вышеприведенное изображение является симуляцией щипкового зума, щипковогоa
векторное увеличение доb
вектор, через начальное состояниеa
Сумма по модулю вектораtouchmove
Приобретенныйb
Вычислите модуль вектора, чтобы получить значение масштабирования:
// touchstart中计算初始双指的向量模;
let vector1 = getVector(secondPoint, startPoint);
let pinchStartLength = getLength(vector1);
// touchmove中计算实时的双指向量模;
let vector2 = getVector(curSecPoint, curPoint);
let pinchLength = getLength(vector2);
this._eventFire('pinch', {
delta: {
scale: pinchLength / pinchStartLength,
},
origin: ev,
});
Поворот (вращение двумя пальцами)
Первоначально двунаправленный векторa
, повернут наb
вектор,θ
это значение, которое нам нужно, поэтому просто передайтеgetAngle
Функция для нахождения угла поворота:
// a向量;
let vector1 = getVector(secondPoint, startPoint);
// b向量;
let vector2 = getVector(curSecPoint, curPoint);
// 触发事件;
this._eventFire('rotate', {
delta: {
rotate: getAngle(vector1, vector2),
},
origin: ev,
});
singlePinch (зум одним пальцем)
В отличие от жестов, описанных выше, как масштабирование одним пальцем, так и вращение одним пальцем требуют нескольких уникальных концепций:
Элемент действия (
operator
): элемент, которым нужно управлять. Вышеупомянутые три жеста на самом деле не заботятся об элементе операции, потому что правильное значение параметра может быть вычислено просто самим жестом, а масштабирование и вращение одним пальцем должны полагаться на опорную точку элемента операции (центр). точка рабочего элемента) для расчета ;Кнопка: поскольку жест одним пальцем и жест перетаскивания конфликтуют друг с другом, для различения требуется специальный метод взаимодействия, здесь выделяется определенная область, похожая на кнопку, при работе с кнопкой, это один -пальцевое масштабирование или вращение, а за пределами области кнопки это обычное перетаскивание.Практика показала, что это метод работы, который легко принять пользователям и имеет лучший опыт;
изображеноa
Векторное масштабирование одним пальцемb
Вектор, центр операнда (квадрата) увеличен, а значение масштаба равноb
по модулю вектора /a
модуль вектора;
// 计算单指操作时的基准点,获取operator的中心点;
let singleBasePoint = getBasePoint(operator);
// touchstart 中计算初始向量模;
let pinchV1 = getVector(startPoint,singleBasePoint);
singlePinchStartLength = getLength(pinchV1);
// touchmove 中计算实时向量模;
pinchV2 = getVector(curPoint, singleBasePoint);
singlePinchLength = getLength(pinchV2);
// 触发事件;
this._eventFire('singlePinch', {
delta: {
scale: singlePinchLength / singlePinchStartLength,
},
origin: ev,
});
singleRotate (вращение одним пальцем)
В сочетании с масштабированием одним пальцем и вращением двумя пальцами вы можете легко узнатьθ
- нужный нам угол поворота;
// 获取初始向量与实时向量
let rotateV1 = getVector(startPoint, singleBasePoint);
let rotateV2 = getVector(curPoint, singleBasePoint);
// 通过 getAngle 获取旋转角度并触发事件;
this._eventFire('singleRotate', {
delta: {
rotate: getAngle(rotateV1, rotateV2),
},
origin: ev,
});
Приращение движения
из-заtouchmove
Событие представляет собой высокочастотное триггерное событие в реальном времени. Фактически операция перетаскивания срабатывает N раз.touchmove
событие, поэтому вычисленное значение является только приращением, т. е. представляет собойtouchmove
Значение, добавленное событием, представляет собой лишь небольшое значение, а не конечное значение результата, поэтому его необходимо определятьmtouch.js
Данные о местоположении хранятся извне, аналогично:
// 真实位置数据;
let dragTrans = {x = 0,y = 0};
// 累加上 mtouch 所传递出的增量 deltaX 与 deltaY;
dragTrans.x += ev.delta.deltaX;
dragTrans.y += ev.delta.deltaY;
// 通过 transform 直接操作元素;
set($drag,dragTrans);
исходное положение
Внешнее обслуживание данных о местоположении, если начальное значение установлено на 0 непосредственно, как описано выше, обеспечивается с помощью css.transform
Элемент атрибута не может быть правильно распознан, что приведет к мгновенному возврату элемента операции при его запуске.(0,0)
Следовательно, нам нужно сначала получить реальное значение позиции элемента, а затем поддерживать и оперировать им. На этом этапе нам нужно использовать вышеупомянутыйgetComputedStyle
метод сmatrixTo
функция:
// 获取css transform属性,此时得到的是一个矩阵数据;
// transform:matrix(1.41421,1.41421,-1.41421,1.41421,-50,-50);
let style = window.getComputedStyle(el,null);
let cssTrans = style.transform || style.webkitTransform;
// 按规则进行转换,得到:
let initTrans = _.matrixTo(cssTrans);
// {x:-50,y:-50,scale:2,rotate:45};
// 即该元素设置了:transform:translate(-50px,-50px) scale(2) rotate(45deg);
Эпилог
На данный момент я считаю, что у всех есть базовое понимание принципов жестов.Основываясь на этих принципах, мы можем инкапсулировать больше жестов, таких как двойной щелчок, долгое нажатие, смахивание и даже более крутые трехпальцевые и четырехпальцевые. операции с пальцами и т. д. Сделайте приложение более гуманизированным.
Основываясь на вышеуказанном принципе, я инкапсулирую несколько общих инструментов :( ищу звезду -.-)
Советы: поскольку это только для мобильного терминала, его необходимо открыть на мобильном устройстве.
demo
или включите режим мобильной отладки на ПК!
- mtouch.js : библиотека жестов для мобильного терминала, которая инкапсулирует пять вышеупомянутых жестов. Упрощенный дизайн API охватывает стандартные взаимодействия жестов. Его также можно легко расширить на основе этого.
demo
github - touchkit.js: на основе
mtouch
Инкапсулированный уровень — это инструментарий, более близкий к бизнесу, который можно использовать для создания различных жестовых операций, открытия одним щелчком мыши и универсального обслуживания.
demo
github - mcanvas.js: экспорт изображений одним щелчком мыши на основе открытого и минималистского API холста.
demo
github
Спасибо
- Чжан Синьсюй:Знаком с методом getComputedStyle для получения значения CSS элемента.
- Чжан Синьсюй:Понимание Matrix в преобразовании CSS3
- AlloyTeam
AlloyFinger
- hcysunyangd:Понимание преобразования CSS3 из взаимосвязи между матрицами и пространственными операциями
- Понимание линейной алгебры