Недавно я купил свой первый Bamboo, планшет Wacom, который распознает буквы из фигур, нарисованных стилусом. Это вызвало у меня воспоминания о моем первом опыте работы с приложением с управлением жестами: использование жестов мыши, веб-браузеры, такие как Maxthon (и более поздние версии Opera ), позволяли пользователям быстро перемещаться по страницам истории в истории, переключаться между различными вкладками и так далее. Мне понравился его аккуратный пользовательский интерфейс, так как он убирает традиционные щелчки мышью. Конечно, теперь доступны сложные устройства с управлением жестами, такие как Kinect, iPad и iPhone — но все это началось со старого доброго ПК. В этом уроке вы узнаете, как создать фотогалерею, которая распознает особые жесты мыши.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться. Для панорамирования галере в четырех основных направлениях щелкните и перетащите мышь в соответствующем направлении. Чтобы масштабировать фотографию, перетащите мышь «Юго-восток», чтобы увеличить масштаб, и перетащите мышь «Северо-Запад», чтобы уменьшить масштаб по умолчанию.
(Примечание: при панорамировании не происходит центрирования фотографии; она чувствительна к длине линии, которую вы рисуете.)
Шаг 1: учебный процесс
Вот что вы выучите в этом уроке, и порядок, в котором вы его выучите:
- Векторная интерпретация жестов мыши
- Жестко запрограммированная реализация мышиных жестов
- Класс для обнаружения особых жестов мыши
- Пример приложения (фотогалерея) с использованием указанного класса
Шаг 2: Обнаружение жестов: векторный анализ
Важно понимать векторную математику, связанную с обнаружением жестов мыши. Поняв обнаружение одного направления, можно легко расширить понимание, чтобы применить его ко всем восьми направлениям.
В представлении Flash ниже показаны этапы обнаружения единственного жеста мыши справа. Для прокрутки кадров в представленной ниже презентации Flash (мышь вниз — движение мыши — вверх) в любом из следующих направлений:
- На восток, чтобы прокрутить кадр вперед
- На запад, чтобы прокрутить кадр назад
- На север, чтобы перейти к последнему кадру
- На юг, чтобы перейти к первому кадру
Шаг 3: Обнаружение жестов: Угловые снижения
Реализация шага 2 будет легкой. Однако вероятность того, что жесты пользователей потерпят неудачу, составляет 90%. Диаграмма ниже показывает жесты, которые обычно совершаются (в середине); они редко соответствуют жесткому вектору, направленному вправо (влево). Таким образом, лучше дать смягчение за неточности жестов (справа).
Например, мы можем задать угол 30 ° по обеим сторонам вектора, указывающего вправо, так что угол вектора любого жеста, попадающий в этот диапазон, будет принят и интерпретирован как жест вправо.
Шаг 4: Обнаружение жестов: пример реализации
Ниже приведена реализация обнаружения жестов справа налево. Нажмите на мышь, переместите мышь вправо и отпустите мышь в представлении Flash ниже. Попробуйте немного отмахнуться от абсолютного права, чтобы проверить реализацию облегчения.
Шаг 5: переменные
Давайте рассмотрим переменные в нашей жестко запрограммированной реализации на шаге 4. Я выделил важные переменные Vector2D. Обратите внимание на комментарии, которые я поместил в конце каждой переменной.
1
2
3
4
|
private var t:TextField;
private var earlier:Vector2D //store mouse location upon first click
private var latter:Vector2D //store mouse location upon release
private var RIGHT:Vector2D = new Vector2D(1, 0);
|
Шаг 6: жестко запрограммированная реализация
Я предполагаю, что вы уже знаете основы размещения TextField
в своем проекте, поэтому я сосредоточусь на реализации ActionScript жеста мыши. Реализация, как указано ниже, тщательно прокомментирована. Важные векторные вычисления также выделены. Я призываю читателей изучить эти комментарии, особенно выделенные, чтобы понять операции при различных событиях во время выполнения.
(Класс Vector2D тот же, что я использовал в предыдущих уроках, например, этот .)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
public function HardCoded()
{
//Creating a textbox
t = new TextField();
t.selectable = false;
t.width = 300;
tx = stage.stageWidth/2;
ty = stage.stageHeight — 30;
addChild(t);
//Start of gesture detection
stage.addEventListener(MouseEvent.MOUSE_DOWN, start);
}
//Register mouse location upon mouse down
private function start(e:MouseEvent):void
{
//Register mouse suppress location
earlier = new Vector2D(e.localX, e.localY);
//Start to draw line
graphics.lineStyle(3);
graphics.moveTo(earlier.x, earlier.y);
//add mouse move and mouse release listeners
stage.addEventListener(MouseEvent.MOUSE_MOVE, move);
stage.addEventListener(MouseEvent.MOUSE_UP, up);
}
//Draw gesture upon mouse move
private function move(e:MouseEvent):void
{
graphics.lineTo(e.localX, e.localY);
}
//Evaluate gesture upon mouse release
private function up(e:MouseEvent):void
{
//Register mouse release location
latter = new Vector2D(e.localX, e.localY);
//Calculating vector of mouse gesture
var result:Vector2D = latter.minus(earlier);
//Calculating angle from absolute RIGHT to gesture vector.
var deviation:Number = RIGHT.angleBetween(result);
deviation = Math2.degreeOf(deviation);
//Interpreting gesture with alleviation
if (Math.abs(deviation) < 30) t.text = «RIGHT gesture detected»;
else t.text = «»;
//Clear screen of previous drawing.
graphics.clear();
//remove mouse move and mouse up listeners
stage.removeEventListener(MouseEvent.MOUSE_MOVE, move);
stage.removeEventListener(MouseEvent.MOUSE_UP, up);
}
|
Шаг 7: Резюме
Для ясности, вот краткое изложение жестко закодированной реализации:
- При наведении мыши, начать обнаружение жестов.
- При перемещении мыши обновите последнюю позицию указателя мыши.
- При наведении мыши оцените весь жест, начиная с (1).
Шаг 8: Углы жестов
Сделать точный жест с помощью мыши сложно. Трудно делать прямые линии (восток, юг, запад, север), но еще сложнее делать диагональные линии (юго-восток, юго-запад, северо-запад, северо-восток), потому что мы должны оценить эти дополнительные 45 °. Поэтому я дал диагональным линиям больше облегчений, чем прямых. Обратите внимание на больший серый цвет для диагонального вектора по сравнению с прямым вектором.
Шаг 9: Чувствительность жестов
Я хотел бы указать на другую проблему — чувствительные жесты. Чувствительные жесты определяют направления жестов, когда указатель мыши делает малейшие сдвиги в местоположении, даже к смежным пикселям. Диаграмма ниже иллюстрирует сценарии чувствительных жестов.
Если пользователь передумает после нажатия мыши и немедленного отпускания мыши, жест все равно будет обнаружен, если его указатель сделает малейшее движение к соседним пикселям. Мы должны разрешить пользователям отменять обнаружение жестов. В этом уроке я установил минимальную величину, которую Вектор текущего жеста должен превышать, чтобы быть действительным. Я включил диаграмму, как показано ниже.
Шаг 10: Переменные класса
Для обнаружения особых жестов мыши я реализовал MGesture
. Загрузите и изучите этот файл ActionScript . Сначала я рассмотрю его переменные класса, а затем методы класса.
переменная | Тип данных | Цель |
mainBox |
DisplayObjectContainer |
Контейнер, из которого обнаруживаются жесты |
directions |
Vector. |
Векторы стандартных направлений |
_deviationFromMains |
Number |
Угловое смягчение допускается жестом Вектор из 4 основных направлений (0 ~ 3 в directions ) |
_deviationFromDiagonals |
Number |
Разрешение углов допускается жестом Вектор из 4 диагональных направлений (4 ~ 7 в directions ) |
_minDist |
Number |
Минимальная величина для текущего жеста Вектор должен быть действительным |
_earlier |
Vector2D |
Расположение первого клика |
_latter |
Vector2D |
Расположение непрерывного указателя после первого клика |
Ниже приведена реализация кода переменных класса. Я допустил отклонение 10 ° от основных направлений. Например, -10 ° -10 ° считается на востоке, -80 ° -100 ° считается на юге и т. Д. Я также допустил отклонение на 30 ° от диагональных направлений. Таким образом, вектор с ориентацией между 15 ° -75 ° будет считаться юго-восточным и т. Д. Кроме того, минимальная величина для превышения составляет 10 пикселей.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
private var mainBox:DisplayObjectContainer;
private var directions:Vector.<Vector2D> = new <Vector2D>[
new Vector2D(1, 0), //East
new Vector2D(0, 1), //South
new Vector2D(-1, 0), //West
new Vector2D(0, -1), //North
new Vector2D(1, 1), //South — east
new Vector2D(-1, 1), //South — west
new Vector2D( -1, -1), //North — west
new Vector2D(1, -1) //North — east
];
private var diagonals:Boolean = false;
private var _deviationFromMains:Number = Math2.radianOf(10);
private var _deviationFromDiagonals:Number = Math2.radianOf(30);
private var _minDist:Number = 10;
private var _earlier:Vector2D;
private var _latter:Vector2D;
|
Шаг 11: Направления нумерации
Возможно, вы уже догадались об этом из ссылки на реализацию кода directions
. Для пояснения, вот основные целочисленные представления направлений.
Шаг 12: Методы класса и свойство
Ниже приведены методы класса для MGesture
.
методы | вход | Выход | Описание |
MGesture | Контейнер, в котором обнаружены жесты | недействительным | Инициирование класса, установка контейнера, из которого обнаруживаются жесты |
Начало | недействительным | недействительным | Переменные для обнаружения жестов ( _earlier , _latter ) инициирует |
Обновить | недействительным | Вектор текущего жеста (без учета _minDist ), Vector2D |
_latter и возвращает текущий вектор жеста ( _earlier to _latter ) |
validMagnitude | недействительным | Вектор текущего жеста (соответствует минимальной величине _minDist ), Vector2D |
Проверяет, превышает _minDist величина текущего жеста _minDist |
evalDirections | недействительным | Целое число, указывающее направление, int |
Оценивает текущий жест, сравнивая его вектор с указанными в directions |
Шаг 13: Методы
Все основные методы, представленные на шаге 12, описаны здесь. Прочитайте комментарии.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
/**
* Initiate variables
* @param container where mouse is detected from
*/
public function MGesture(container:DisplayObjectContainer) {
//setting container from which mouse is moving
mainBox = container;
}
/**
* Method to register initial mouse location
*/
public function start ():void {
var startMX:Number = mainBox.mouseX;
var startMY:Number = mainBox.mouseY;
_earlier = new Vector2D(startMX, startMY);
_latter = new Vector2D(startMX, startMY);
}
/**
* Method to update mouse location
* @return a Vector2D of current mouse location relative to that when start() is called;
*/
public function update ():Vector2D {
_latter = new Vector2D(mainBox.mouseX, mainBox.mouseY);
var vecUpdate:Vector2D = _latter.minus(_earlier);
return vecUpdate;
}
/**
* Method to validate a gesture.
* @param newLoc Vector2D to new mouse location
* @return null if invalid gesture, a Vector2D if valid
*/
private function validMagnitude ():Vector2D {
var gestureVector:Vector2D = update();
var newMag:Number = gestureVector.getMagnitude();
//if magnitude condition is not fulfilled, reset gestureVector to null
if (newMag < _minDist) gestureVector = null;
return gestureVector;
}
/**
* Method to evaluate gesture direction
* @return Integer indicative of direction.
*/
public function evalDirections():int {
//Pessimistic search (initialise with unsuccessful search)
var detectedDirection:int = -1;
//validate magnitude condition
var newDirection:Vector2D = validMagnitude();
//if gesture exceed minimum magnitude
if (newDirection != null) {
//evaluation against all directions
for (var i:int = 0; i < directions.length; i++)
{
var angle:Number = directions[i].angleBetween(newDirection);
angle = Math.abs(angle);
//check against main directions
if ( i < 4 && angle < _deviationFromMains) {
detectedDirection = i;
break;
}
//check against diagonal directions
else if (i > 3 && angle < _deviationFromDiagonals) {
detectedDirection = i;
break;
}
}
//update mouse location for next evaluation
_earlier = _latter;
}
//return detected direction
return detectedDirection
}
|
Шаг 14: Фотогалерея
Теперь, MGesture
класс MGesture
установлен, мы приступим к его демонстрационному приложению (фотогалерее). Я включил исходный файл здесь. Загрузите и следуйте инструкциям. Прежде всего, поместите все изображения в папку «lib» в вашем существующем проекте. Изображения, которые я использовал здесь, любезно предоставлены моей женой и дочерью.
Шаг 15: Вставить изображения
Создайте новый класс Actionscript и назовите его PhotoView
. Мы будем использовать эти изображения для построения нашей галереи.
- Создать код для вставки изображений. Они будут распознаны как универсальный объект
Class
. - Преобразуйте
Class
вBitmap
объекты, чтобы мы могли манипулировать им дальше. - Поместите все эти
Bitmap
объекты в массивVector
для удобства выбора позже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
[Embed(source = ‘../lib/Week3.jpg’)]
private var Week3:Class
[Embed(source = ‘../lib/Family.jpg’)]
private var Family:Class
[Embed(source = ‘../lib/FatherDaughter.jpg’)]
private var Daughter:Class
[Embed(source = ‘../lib/Jovial.jpg’)]
private var Jovial:Class
[Embed(source = ‘../lib/NewBorn.jpg’)]
private var NewBorn:Class;
[Embed(source = ‘../lib/Posing.jpg’)]
private var Posing:Class
[Embed(source = ‘../lib/Smile.jpg’)]
private var Smile:Class
[Embed(source = ‘../lib/Surrender.jpg’)]
private var Surrender:Class
private var list:Vector.<Bitmap> = new <Bitmap> [
new Week3 as Bitmap,
new Family as Bitmap,
new Daughter as Bitmap,
new Jovial as Bitmap,
new NewBorn as Bitmap,
new Posing as Bitmap,
new Smile as Bitmap,
new Week3 as Bitmap,
new Surrender as Bitmap
]
|
Шаг 16: Показать список управления
Здесь важно уточнить управление списком отображения PhotoView
. Я включил презентацию Flash здесь. Чтобы использовать это, сделайте жест направо или налево. Для получения дополнительной информации обратитесь к шагу 2.
Шаг 17: Позиционирование изображения
В конструкторе PhotoView
мы инициируем все необходимые экранные объекты и размещаем их на месте. Кроме того, мы запускаем MGesture
и подключаем прослушиватели событий, чтобы начать обнаружение жестов. Я выделил слушателей событий. Их детали объясняются в течение следующих двух шагов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public function PhotoView()
{
panel = new Sprite();
panel.x = stage.stageWidth / 2;
panel.y = stage.stageHeight / 2;
addChild(panel);
var currentBmp:int = 0;
var bmpGaps:Number = 60;
var bmpOnX:int = 3;
var bmpOnY:int = 3;
var bmp:Bitmap;
var container:Sprite;
//scrolling through Y
for (var j:int = -1*Math.floor(bmpOnY/2); j < Math.ceil(bmpOnY/2); j++)
{
//scrolling through X
for (var i:int = -1*Math.floor(bmpOnX/2); i < Math.ceil(bmpOnX/2); i++)
{
bmp = list[currentBmp];
bmp.x = -1 * bmp.width / 2;
bmp.y = -1 * bmp.height / 2;
container = new Sprite();
container.x = (bmp.width + bmpGaps )* i;
container.y = (bmp.height + bmpGaps )* j;
container.addChild(bmp);
container.addEventListener(MouseEvent.MOUSE_DOWN, select);
panel.addChild(container);
currentBmp++ //Scroll to next bitmap
}
}
gesture = new MGesture(stage);
stage.addEventListener(MouseEvent.MOUSE_DOWN, start);
}
|
Шаг 18: выбор изображения для масштабирования
Выделенная строка 99 относится не к обнаружению жеста, а просто к выбору изображения для масштабирования и размещения его поверх всех других изображений.
1
2
3
4
5
6
7
|
private function select(e:MouseEvent):void
{
//Setting current image to scale &
//Placing it on top of all other images
ImgSelected = e.currentTarget as Sprite;
panel.swapChildrenAt(panel.numChildren — 1, panel.getChildIndex(ImgSelected));
}
|
Шаг 19: Запустите, закончите и оцените жест мыши
Первая функция ниже выполняется при нажатии мыши. Вторая выполняется при наведении мыши. Я выделил start()
и evalGesture()
а также эван-слушателей.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
private function start(e:MouseEvent):void
{
//Start gesture detection &
//Listen for mouse up event
gesture.start();
stage.addEventListener(MouseEvent.MOUSE_UP, end);
}
private function end(e:MouseEvent):void
{
//Prepare current gesture’s magnitude for animation purpose
//implement a maximum cap on gesture’s magnitude
gestureMag = gesture.update().getMagnitude() / 2;
gestureMag = Math.min(gestureMag, maxMag);
//Evaluate current gesture
direction = gesture.evalGesture();
//Once a valid gesture is detected, perform animation
//No further gestures will be detected until animation ends
if (direction > -1) {
stage.addEventListener(Event.ENTER_FRAME, move);
stage.removeEventListener(MouseEvent.MOUSE_DOWN, start);
}
}
|
Шаг 20: Анимация панели и изображений
Как только направления будут обнаружены, начнется анимация. В зависимости от сделанного жеста вся панель может перемещаться в четырех направлениях или одно изображение может увеличиваться или уменьшаться.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
private function move(e:Event):void
{
var currentMag:Number
//Motion of panel translation
if (direction < 4) {
//Function of easing motion
currentMag = gestureMag * Math.cos(currentAngle += 0.1);
if (direction == 0) panel.x += currentMag;
else if (direction == 1) panel.y += currentMag;
else if (direction == 2) panel.x -= currentMag;
else if (direction == 3) panel.y -= currentMag;
}
//Motion of image scaling
else {
//Setting a maximum cap on motion
gestureMag = Math.min(0.30, gestureMag);
//Function of easing motion
currentMag = gestureMag * Math.cos(currentAngle += 0.1);
//Conditions to scale up:
//Gesture is to South-East &
//Image is not scaled up already
if (direction == 4 && ImgSelected.scaleX < 1.30){
ImgSelected.scaleX = ImgSelected.scaleY = -1 * currentMag + 1.30
}
//Conditions to scale down:
//Gesture is to North-West &
//Image is scaled up
else if (direction == 6 && ImgSelected.scaleX > 1){
ImgSelected.scaleX = ImgSelected.scaleY = currentMag + 1;
}
}
//If angle on easing function exceeds 90 degrees/ 0.5 Pi radian,
//stop animation &
if (currentAngle > Math.PI/2) {
stage.removeEventListener(Event.ENTER_FRAME, move);
stage.addEventListener(MouseEvent.MOUSE_DOWN, start);
direction = -1;
currentAngle = 0;
}
}
|
Шаг 21: Опубликовать PhotoView
Теперь все готово. Наконец, вы можете опубликовать свою работу, нажав Ctrl + Enter на FlashDevelop. Опять таки. вот кусок конечного продукта.
Вывод
Это не конец этого. В следующей части мы рассмотрим обнаружение последовательности жестов, которая будет даже более интересной, чем эта часть (которая действительно только что показала основы). MGesture
комментарии и дайте мне знать, если MGesture
был полезен для вас, а также любые ошибки, если вы столкнулись с какими-либо. Наконец, терима касых за время чтения. Я надеюсь развлечь моих коллег-читателей во второй части.