Веб-приложения должны предоставлять простые в использовании решения для загрузки и управления многофункциональным контентом. Этот процесс может создать трудности для некоторых пользователей, которые имеют минимальные навыки редактирования фотографий. Обрезка является одним из наиболее часто используемых методов манипулирования фотографиями, и этот пошаговый учебник охватит весь процесс разработки плагина для обрезки изображений для библиотеки jQuery JavaScript.
Краткий обзор
В предыдущем уроке мы рассмотрели:
- как расширить JQuery
- как сделать плагин более гибким с помощью пользовательских опций
- как создать базовое приложение для обрезки изображений
Сегодня мы пойдем дальше и завершим наш плагин: мы определим больше пользовательских опций, добавим обратные вызовы, сделаем выбор перетаскиваемым и изменяющим размер, создадим панель предварительного просмотра и подсказку о размере и напишем некоторый код на стороне сервера для обрезать изображение.
Шаг 1: Добавление дополнительных параметров
Откройте файл jquery.imagecrop.js
расположенный по адресу /resources/js/imageCrop/
и добавьте следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
var defaultOptions = {
allowMove : true,
allowResize : true,
allowSelect : true,
aspectRatio : 0,
displayPreview : false,
displaySizeHint : false,
minSelect : [0, 0],
minSize : [0, 0],
maxSize : [0, 0],
outlineOpacity : 0.5,
overlayOpacity : 0.5,
previewBoundary : 90,
previewFadeOnBlur : 1,
previewFadeOnFocus : 0.35,
selectionPosition : [0, 0],
selectionWidth : 0,
selectionHeight : 0,
// Plug-in’s event handlers
onChange : function() {},
onSelect : function() {}
};
|
Мы добавили больше опций и два обратных вызова, onChange
и onSelect
. Эти два могут быть весьма полезны при получении состояния плагина.
Варианты
Вот краткое изложение опций, которые мы добавляем:
- aspectRatio — Определяет соотношение сторон выделения (значение по умолчанию
0
). - displayPreview — Определяет, является ли панель предварительного просмотра видимой или нет (значение по умолчанию —
false
) - displaySizeHint — Определяет, является ли подсказка размера видимой или нет (значение по умолчанию —
false
) - minSize — Определяет минимальный размер выделения (значение по умолчанию
[0, 0]
) - maxSize — определяет максимальный размер выделения (значение по умолчанию
[0, 0]
) - previewBoundary — Определяет размер панели предварительного просмотра (значение по умолчанию —
90
) - previewFadeOnBlur — Определяет непрозрачность панели предварительного просмотра для размытия (значение по умолчанию равно
1
) - previewFadeOnFocus — указывает непрозрачность области предварительного просмотра в фокусе (значение по умолчанию —
0.35
). - onCahnge — возвращает состояние плагина при изменении выбора
- onSelect — возвращает состояние плагина, когда выбор сделан
Шаг 2: Добавление большего количества слоев
На этом этапе мы собираемся добавить больше слоев. Давайте начнем с подсказки размера.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
…
// Initialize a background layer of size hint and place it above the
// selection layer
var $sizeHintBackground = $(‘<div id=»image-crop-size-hint-background» />’)
.css({
opacity : 0.35,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize a foreground layer of size hint and place it above the
// background layer
var $sizeHintForeground = $(‘<span id=»image-crop-size-hint-foreground» />’)
.css({
position : ‘absolute’
})
.insertAfter($sizeHintBackground);
|
Мы добавили два отдельных слоя, потому что не хотим, чтобы на передний план влияла непрозрачность фона.
Теперь мы добавим еще девять слоев: обработчики изменения размера.
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
|
…
// Initialize a north/west resize handler and place it above the
// selection layer
var $nwResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-nw-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize a north resize handler and place it above the selection
// layer
var $nResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-n-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize a north/east resize handler and place it above the
// selection layer
var $neResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-ne-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize an west resize handler and place it above the selection
// layer
var $wResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-w-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize an east resize handler and place it above the selection
// layer
var $eResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-e-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize a south/west resize handler and place it above the
// selection layer
var $swResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-sw-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize a south resize handler and place it above the selection
// layer
var $sResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-s-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
// Initialize a south/east resize handler and place it above the
// selection layer
var $seResizeHandler = $(‘<div class=»image-crop-resize-handler» id=»image-crop-se-resize-handler» />’)
.css({
opacity : 0.5,
position : ‘absolute’
})
.insertAfter($selection);
|
Мы инициализировали обработчик изменения размера для каждого угла и средней стороны.
И, наконец, панель предварительного просмотра.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
…
// Initialize a preview holder and place it after the outline layer
var $previewHolder = $(‘<div id=»image-crop-preview-holder» />’)
.css({
opacity : options.previewFadeOnBlur,
overflow : ‘hidden’,
position : ‘absolute’
})
.insertAfter($outline);
// Initialize a preview image and append it to the preview holder
var $preview = $(‘<img alt=»Crop preview» id=»image-crop-preview» />’)
.css({
position : ‘absolute’
})
.attr(‘src’, $image.attr(‘src’))
.appendTo($previewHolder);
|
Мы инициализировали два слоя:
- держатель, который работает как маска и
- изображение предварительного просмотра, которое имеет тот же источник, что и исходное изображение.
Мы использовали метод .appendTo()
чтобы вставить изображение предварительного просмотра в конец держателя.
Шаг 3: Улучшение интерфейса
Сначала мы добавим две новые глобальные переменные.
1
2
3
4
5
6
7
8
|
…
// Initialize global variables
var resizeHorizontally = true,
resizeVertically = true,
selectionExists,
selectionOffset = [0, 0],
selectionOrigin = [0, 0];
|
Эти переменные понадобятся нам позже, когда мы обновим resizeSelection()
.
В первой части мы позаботились только о опции allowSelect
. Давайте также обработаем allowMove
и allowResize
.
1
2
3
4
5
6
7
8
9
|
…
if (options.allowMove)
// Bind an event handler to the ‘mousedown’ event of the selection layer
$selection.mousedown(pickSelection);
if (options.allowResize)
// Bind an event handler to the ‘mousedown’ event of the resize handlers
$(‘div.image-crop-resize-handler’).mousedown(pickResizeHandler);
|
Мы прикрепили событие mousedown
к выделению и все обработчики изменения размера.
Теперь нам нужно написать немного больше кода, чтобы обновить новые слои, которые мы добавили ранее.
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
|
…
// Update the size hint
function updateSizeHint(action) {
switch (action) {
case ‘fade-out’ :
// Fade out the size hint
$sizeHintBackground.fadeOut(‘slow’);
$sizeHintForeground.fadeOut(‘slow’);
break;
default :
var display = (selectionExists && options.displaySize) ?
// Update the foreground layer
$sizeHintForeground.css({
cursor : ‘default’,
display : display,
left : options.selectionPosition[0] + 4,
top : options.selectionPosition[1] + 4
})
.html(options.selectionWidth + ‘x’ + options.selectionHeight);
// Update the background layer
$sizeHintBackground.css({
cursor : ‘default’,
display : display,
left : options.selectionPosition[0] + 1,
top : options.selectionPosition[1] + 1
})
.width($sizeHintForeground.width() + 6)
.height($sizeHintForeground.height() + 6);
}
};
|
Функция updateSizeHint()
обрабатывает два случая в зависимости от указанного параметра.
- Если ничего не указано, поведение по умолчанию — отображать и обновлять подсказку о размере (если выбор существует).
- Второе поведение — исчезнуть намек. Это будет использоваться, когда пользователь выполнит изменение размера выделения.
На предыдущем шаге мы только инициализировали обработчики изменения размера. Теперь мы разместим их в правильном положении.
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
|
…
// Update the resize handlers
function updateResizeHandlers(action) {
switch (action) {
case ‘hide-all’ :
$(‘.image-crop-resize-handler’).each(function() {
$(this).css({
display : ‘none’
});
});
break;
default :
var display = (selectionExists && options.allowResize) ?
$nwResizeHandler.css({
cursor : ‘nw-resize’,
display : display,
left : options.selectionPosition[0] — Math.round($nwResizeHandler.width() / 2),
top : options.selectionPosition[1] — Math.round($nwResizeHandler.height() / 2)
});
$nResizeHandler.css({
cursor : ‘n-resize’,
display : display,
left : options.selectionPosition[0] + Math.round(options.selectionWidth / 2 — $neResizeHandler.width() / 2) — 1,
top : options.selectionPosition[1] — Math.round($neResizeHandler.height() / 2)
});
$neResizeHandler.css({
cursor : ‘ne-resize’,
display : display,
left : options.selectionPosition[0] + options.selectionWidth — Math.round($neResizeHandler.width() / 2) — 1,
top : options.selectionPosition[1] — Math.round($neResizeHandler.height() / 2)
});
$wResizeHandler.css({
cursor : ‘w-resize’,
display : display,
left : options.selectionPosition[0] — Math.round($neResizeHandler.width() / 2),
top : options.selectionPosition[1] + Math.round(options.selectionHeight / 2 — $neResizeHandler.height() / 2) — 1
});
$eResizeHandler.css({
cursor : ‘e-resize’,
display : display,
left : options.selectionPosition[0] + options.selectionWidth — Math.round($neResizeHandler.width() / 2) — 1,
top : options.selectionPosition[1] + Math.round(options.selectionHeight / 2 — $neResizeHandler.height() / 2) — 1
});
$swResizeHandler.css({
cursor : ‘sw-resize’,
display : display,
left : options.selectionPosition[0] — Math.round($swResizeHandler.width() / 2),
top : options.selectionPosition[1] + options.selectionHeight — Math.round($swResizeHandler.height() / 2) — 1
});
$sResizeHandler.css({
cursor : ‘s-resize’,
display : display,
left : options.selectionPosition[0] + Math.round(options.selectionWidth / 2 — $seResizeHandler.width() / 2) — 1,
top : options.selectionPosition[1] + options.selectionHeight — Math.round($seResizeHandler.height() / 2) — 1
});
$seResizeHandler.css({
cursor : ‘se-resize’,
display : display,
left : options.selectionPosition[0] + options.selectionWidth — Math.round($seResizeHandler.width() / 2) — 1,
top : options.selectionPosition[1] + options.selectionHeight — Math.round($seResizeHandler.height() / 2) — 1
});
}
};
|
Подобно последней функции, updateResizeHandlers()
тестирует два случая: hide-all
и по default
. В первом случае мы вызываем метод .each()
для итерации по соответствующим элементам.
Давайте создадим updatePreview()
.
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
|
…
// Update the preview
function updatePreview(action) {
switch (action) {
case ‘focus’ :
// Fade in the preview holder layer
$previewHolder.stop()
.animate({
opacity : options.previewFadeOnFocus
});
break;
case ‘blur’ :
// Fade out the preview holder layer
$previewHolder.stop()
.animate({
opacity : options.previewFadeOnBlur
});
break;
case ‘hide’ :
// Hide the preview holder layer
$previewHolder.css({
display : ‘none’
});
break;
default :
var display = (selectionExists && options.displayPreview) ?
// Update the preview holder layer
$previewHolder.css({
display : display,
left : options.selectionPosition[0],
top : options.selectionPosition[1] + options.selectionHeight + 10
});
// Update the preview size
if (options.selectionWidth > options.selectionHeight) {
if (options.selectionWidth && options.selectionHeight) {
// Update the preview image size
$preview.width(Math.round($image.width() * options.previewBoundary / options.selectionWidth));
$preview.height(Math.round($image.height() * $preview.width() / $image.width()));
// Update the preview holder layer size
$previewHolder.width(options.previewBoundary)
.height(Math.round(options.selectionHeight * $preview.height() / $image.height()));
}
} else {
if (options.selectionWidth && options.selectionHeight) {
// Update the preview image size
$preview.height(Math.round($image.height() * options.previewBoundary / options.selectionHeight));
$preview.width(Math.round($image.width() * $preview.height() / $image.height()));
// Update the preview holder layer size
$previewHolder.width(Math.round(options.selectionWidth * $preview.width() / $image.width()))
.height(options.previewBoundary);
}
}
// Update the preview image position
$preview.css({
left : — Math.round(options.selectionPosition[0] * $preview.width() / $image.width()),
top : — Math.round(options.selectionPosition[1] * $preview.height() / $image.height())
});
}
};
|
Код для первых трех случаев должен быть понятен. Мы вызываем метод .animate()
для выполнения пользовательской анимации набора свойств CSS. Далее мы определяем значение display
и устанавливаем положение предварительного просмотра. Затем мы масштабируем изображение предварительного просмотра в соответствии с параметром previewBoundary
и вычисляем его новую позицию.
Нам также необходимо обновить updateCursor()
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
…
// Update the cursor type
function updateCursor(cursorType) {
$trigger.css({
cursor : cursorType
});
$outline.css({
cursor : cursorType
});
$selection.css({
cursor : cursorType
});
$sizeHintBackground.css({
cursor : cursorType
});
$sizeHintForeground.css({
cursor : cursorType
});
};
|
А теперь последняя функция этого шага.
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
|
…
// Update the plug-in interface
function updateInterface(sender) {
switch (sender) {
case ‘setSelection’ :
updateOverlayLayer();
updateSelection();
updateResizeHandlers(‘hide-all’);
updatePreview(‘hide’);
break;
case ‘pickSelection’ :
updateResizeHandlers(‘hide-all’);
break;
case ‘pickResizeHandler’ :
updateSizeHint();
updateResizeHandlers(‘hide-all’);
break;
case ‘resizeSelection’ :
updateSelection();
updateSizeHint();
updateResizeHandlers(‘hide-all’);
updatePreview();
updateCursor(‘crosshair’);
break;
case ‘moveSelection’ :
updateSelection();
updateResizeHandlers(‘hide-all’);
updatePreview();
updateCursor(‘move’);
break;
case ‘releaseSelection’ :
updateTriggerLayer();
updateOverlayLayer();
updateSelection();
updateSizeHint(‘fade-out’);
updateResizeHandlers();
updatePreview();
break;
default :
updateTriggerLayer();
updateOverlayLayer();
updateSelection();
updateResizeHandlers();
updatePreview();
}
};
|
Шаг 4: Улучшение setSelection()
Здесь мы добавим только одну вещь: поддержку панели предварительного просмотра.
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
|
…
// Set a new selection
function setSelection(event) {
// Prevent the default action of the event
event.preventDefault();
// Prevent the event from being notified
event.stopPropagation();
// Bind an event handler to the ‘mousemove’ event
$(document).mousemove(resizeSelection);
// Bind an event handler to the ‘mouseup’ event
$(document).mouseup(releaseSelection);
// If display preview option is enabled
if (options.displayPreview) {
// Bind an event handler to the ‘mouseenter’ event of the preview
// holder
$previewHolder.mouseenter(function() {
updatePreview(‘focus’);
});
// Bind an event handler to the ‘mouseleave’ event of the preview
// holder
$previewHolder.mouseleave(function() {
updatePreview(‘blur’);
});
}
// Notify that a selection exists
selectionExists = true;
// Reset the selection size
options.selectionWidth = 0;
options.selectionHeight = 0;
// Get the selection origin
selectionOrigin = getMousePosition(event);
// And set its position
options.selectionPosition[0] = selectionOrigin[0];
options.selectionPosition[1] = selectionOrigin[1];
// Update only the needed elements of the plug-in interface
// by specifying the sender of the current call
updateInterface(‘setSelection’);
};
|
Мы протестировали опцию displayPreview
и использовали функции .mouseenter()
и .mouseleave()
для присоединения обработчиков событий к держателю предварительного просмотра.
Шаг 5: выбор выбора
Чтобы сделать выбор перетаскиваемым, нам нужно сделать вывод, когда пользователь перемещает и отпускает кнопку мыши.
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
|
…
// Pick the current selection
function pickSelection(event) {
// Prevent the default action of the event
event.preventDefault();
// Prevent the event from being notified
event.stopPropagation();
// Bind an event handler to the ‘mousemove’ event
$(document).mousemove(moveSelection);
// Bind an event handler to the ‘mouseup’ event
$(document).mouseup(releaseSelection);
var mousePosition = getMousePosition(event);
// Get the selection offset relative to the mouse position
selectionOffset[0] = mousePosition[0] — options.selectionPosition[0];
selectionOffset[1] = mousePosition[1] — options.selectionPosition[1];
// Update only the needed elements of the plug-in interface
// by specifying the sender of the current call
updateInterface(‘pickSelection’);
};
|
Также у нас есть смещение выделения относительно позиции мыши. Это понадобится нам позже, в функции moveSelection()
.
Шаг 6: выбор обработчиков изменения размера
Пользователь сможет изменить размер выделения, выбрав и перетащив один из обработчиков изменения размера. И это может быть сделано двумя способами: по обеим осям — если пользователь выбирает перетаскивание обработчика из угла — или по одной оси — если пользователь выбирает перетаскивание обработчика из середины стороны.
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
|
…
// Pick one of the resize handlers
function pickResizeHandler(event) {
// Prevent the default action of the event
event.preventDefault();
// Prevent the event from being notified
event.stopPropagation();
switch (event.target.id) {
case ‘image-crop-nw-resize-handler’ :
selectionOrigin[0] += options.selectionWidth;
selectionOrigin[1] += options.selectionHeight;
options.selectionPosition[0] = selectionOrigin[0] — options.selectionWidth;
options.selectionPosition[1] = selectionOrigin[1] — options.selectionHeight;
break;
case ‘image-crop-n-resize-handler’ :
selectionOrigin[1] += options.selectionHeight;
options.selectionPosition[1] = selectionOrigin[1] — options.selectionHeight;
resizeHorizontally = false;
break;
case ‘image-crop-ne-resize-handler’ :
selectionOrigin[1] += options.selectionHeight;
options.selectionPosition[1] = selectionOrigin[1] — options.selectionHeight;
break;
case ‘image-crop-w-resize-handler’ :
selectionOrigin[0] += options.selectionWidth;
options.selectionPosition[0] = selectionOrigin[0] — options.selectionWidth;
resizeVertically = false;
break;
case ‘image-crop-e-resize-handler’ :
resizeVertically = false;
break;
case ‘image-crop-sw-resize-handler’ :
selectionOrigin[0] += options.selectionWidth;
options.selectionPosition[0] = selectionOrigin[0] — options.selectionWidth;
break;
case ‘image-crop-s-resize-handler’ :
resizeHorizontally = false;
break;
}
// Bind an event handler to the ‘mousemove’ event
$(document).mousemove(resizeSelection);
// Bind an event handler to the ‘mouseup’ event
$(document).mouseup(releaseSelection);
// Update only the needed elements of the plug-in interface
// by specifying the sender of the current call
updateInterface(‘pickResizeHandler’);
};
|
Мы написали кейс для каждого обработчика изменения размера, потому что каждый нуждается в определенных настройках.
Шаг 7: Улучшение resizeSelection()
В отличие от первой версии, resizeSelection()
сможет проверять минимальный / максимальный размер и блокировать пропорции выбора.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
|
…
// Resize the current selection
function resizeSelection(event) {
// Prevent the default action of the event
event.preventDefault();
// Prevent the event from being notified
event.stopPropagation();
var mousePosition = getMousePosition(event);
// Get the selection size
var height = mousePosition[1] — selectionOrigin[1],
width = mousePosition[0] — selectionOrigin[0];
// If the selection size is smaller than the minimum size set it
// accordingly
if (Math.abs(width) < options.minSize[0])
width = (width >= 0) ?
if (Math.abs(height) < options.minSize[1])
height = (height >= 0) ?
// Test if the selection size exceeds the image bounds
if (selectionOrigin[0] + width < 0 || selectionOrigin[0] + width > $image.width())
width = — width;
if (selectionOrigin[1] + height < 0 || selectionOrigin[1] + height > $image.height())
height = — height;
if (options.maxSize[0] > options.minSize[0] &&
options.maxSize[1] > options.minSize[1]) {
// Test if the selection size is bigger than the maximum size
if (Math.abs(width) > options.maxSize[0])
width = (width >= 0) ?
if (Math.abs(height) > options.maxSize[1])
height = (height >= 0) ?
}
// Set the selection size
if (resizeHorizontally)
options.selectionWidth = width;
if (resizeVertically)
options.selectionHeight = height;
// If any aspect ratio is specified
if (options.aspectRatio) {
// Calculate the new width and height
if ((width > 0 && height > 0) || (width < 0 && height < 0))
if (resizeHorizontally)
height = Math.round(width / options.aspectRatio);
else
width = Math.round(height * options.aspectRatio);
else
if (resizeHorizontally)
height = — Math.round(width / options.aspectRatio);
else
width = — Math.round(height * options.aspectRatio);
// Test if the new size exceeds the image bounds
if (selectionOrigin[0] + width > $image.width()) {
width = $image.width() — selectionOrigin[0];
height = (height > 0) ?
}
if (selectionOrigin[1] + height < 0) {
height = — selectionOrigin[1];
width = (width > 0) ?
}
if (selectionOrigin[1] + height > $image.height()) {
height = $image.height() — selectionOrigin[1];
width = (width > 0) ?
}
// Set the selection size
options.selectionWidth = width;
options.selectionHeight = height;
}
if (options.selectionWidth < 0) {
options.selectionWidth = Math.abs(options.selectionWidth);
options.selectionPosition[0] = selectionOrigin[0] — options.selectionWidth;
} else
options.selectionPosition[0] = selectionOrigin[0];
if (options.selectionHeight < 0) {
options.selectionHeight = Math.abs(options.selectionHeight);
options.selectionPosition[1] = selectionOrigin[1] — options.selectionHeight;
} else
options.selectionPosition[1] = selectionOrigin[1];
// Trigger the ‘onChange’ event when the selection is changed
options.onChange(getCropData());
// Update only the needed elements of the plug-in interface
// by specifying the sender of the current call
updateInterface(‘resizeSelection’);
};
|
Кроме того, мы onChange()
обратный вызов onChange()
в конце функции. Функция getCropData()
возвращает текущее состояние плагина. Мы напишем его тело несколькими шагами позже.
Шаг 8: перемещение выделения
Теперь мы напишем moveSelection()
.
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
|
…
// Move the current selection
function moveSelection(event) {
// Prevent the default action of the event
event.preventDefault();
// Prevent the event from being notified
event.stopPropagation();
var mousePosition = getMousePosition(event);
// Set the selection position on the x-axis relative to the bounds
// of the image
if (mousePosition[0] — selectionOffset[0] > 0)
if (mousePosition[0] — selectionOffset[0] + options.selectionWidth < $image.width())
options.selectionPosition[0] = mousePosition[0] — selectionOffset[0];
else
options.selectionPosition[0] = $image.width() — options.selectionWidth;
else
options.selectionPosition[0] = 0;
// Set the selection position on the y-axis relative to the bounds
// of the image
if (mousePosition[1] — selectionOffset[1] > 0)
if (mousePosition[1] — selectionOffset[1] + options.selectionHeight < $image.height())
options.selectionPosition[1] = mousePosition[1] — selectionOffset[1];
else
options.selectionPosition[1] = $image.height() — options.selectionHeight;
else
options.selectionPosition[1] = 0;
// Trigger the ‘onChange’ event when the selection is changed
options.onChange(getCropData());
// Update only the needed elements of the plug-in interface
// by specifying the sender of the current call
updateInterface(‘moveSelection’);
};
|
Как и раньше, мы onChange()
обратный вызов onChange()
в конце функции.
Шаг 9: Улучшение releaseSelection()
Нам также нужно отредактировать releaseSelection()
.
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
|
…
// Release the current selection
function releaseSelection(event) {
// Prevent the default action of the event
event.preventDefault();
// Prevent the event from being notified
event.stopPropagation();
// Unbind the event handler to the ‘mousemove’ event
$(document).unbind(‘mousemove’);
// Unbind the event handler to the ‘mouseup’ event
$(document).unbind(‘mouseup’);
// Update the selection origin
selectionOrigin[0] = options.selectionPosition[0];
selectionOrigin[1] = options.selectionPosition[1];
// Reset the resize constraints
resizeHorizontally = true;
resizeVertically = true;
// Verify if the selection size is bigger than the minimum accepted
// and set the selection existence accordingly
if (options.selectionWidth > options.minSelect[0] &&
options.selectionHeight > options.minSelect[1])
selectionExists = true;
else
selectionExists = false;
// Trigger the ‘onSelect’ event when the selection is made
options.onSelect(getCropData());
// If the selection doesn’t exist
if (!selectionExists) {
// Unbind the event handler to the ‘mouseenter’ event of the
// preview
$previewHolder.unbind(‘mouseenter’);
// Unbind the event handler to the ‘mouseleave’ event of the
// preview
$previewHolder.unbind(‘mouseleave’);
}
// Update only the needed elements of the plug-in interface
// by specifying the sender of the current call
updateInterface(‘releaseSelection’);
};
|
Мы сбросили ограничения на изменение размера и добавили поддержку панели предварительного просмотра. Кроме того, мы onSelect()
обратный вызов onSelect()
таким же образом, как мы делали это раньше с onChange()
.
Шаг 10: Получение текущего состояния
Теперь мы почти готовы. Давайте напишем getCropData()
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
…
// Return an object containing information about the plug-in state
function getCropData() {
return {
selectionX : options.selectionPosition[0],
selectionY : options.selectionPosition[1],
selectionWidth : options.selectionWidth,
selectionHeight : options.selectionHeight,
selectionExists : function() {
return selectionExists;
}
};
};
|
Мы только что написали последнюю функцию этого файла. Сохраните его и подготовьтесь к следующему шагу.
Шаг 11: Минимизация кода
«Сокращение кода уменьшает его размер и сокращает время загрузки».
На этом этапе мы минимизируем код нашего плагина, чтобы уменьшить его размер и сократить время загрузки. Эта практика заключается в удалении ненужных символов, таких как комментарии, пробелы, новые строки и вкладки. Два популярных инструмента для минимизации кода JavaScript — это YUI Compressor (который также может минимизировать CSS) и JSMin . Мы будем использовать первый. Кроме того, он с открытым исходным кодом, так что вы можете взглянуть на код, чтобы понять, как именно он работает.
Использование YUI Compressor
YUI Compressor написан на Java, поэтому не имеет значения, какую операционную систему вы используете. Единственное требование — Java > = 1.4. Загрузите YUI Compressor и распакуйте его в папку /resources/js/imageCrop/
. Откройте командную строку и измените текущий рабочий каталог на тот же путь.
Если вы используете его впервые, вам следует начать с выполнения следующей строки в командной строке и прочитать инструкции по использованию.
1
|
$ java -jar yuicompressor-xyzjar
|
Теперь давайте минимизируем наш код.
1
|
$ java -jar yuicompressor-xyzjar jquery.imagecrop.js -o jquery.imagecrop.js —preserve-semi
|
Не забудьте заменить xyz
версией YUI Compressor, которую вы используете. Вот и все; дождитесь его завершения и закройте окно командной строки.
Шаг 12: стилизация новых элементов
Откройте /resources/js/imageCrop/jquery.imagecrop.css
и добавьте в него следующие строки:
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
|
…
div#image-crop-size-hint-background {
background-color : #000000;
}
span#image-crop-size-hint-foreground {
color : #ffffff;
font-family : ‘Verdana’, ‘Geneva’, sans-serif;
font-size : 12px;
text-shadow : 0 -1px 0 #000000;
}
div#image-crop-preview-holder {
-moz-box-shadow : 0 0 5px #000000;
-webkit-box-shadow : 0 0 5px #000000;
border : 3px #ef2929 solid;
box-shadow : 0 0 5px #000000;
}
img#image-crop-preview {
border : none;
}
div.image-crop-resize-handler {
background-color : #000000;
border : 1px #ffffff solid;
height : 7px;
overflow : hidden;
width : 7px;
}
|
Мы добавили некоторые стили для подсказки по размеру, панели предварительного просмотра и обработчиков изменения размера.
Шаг 13: Тестирование окончательного результата
Сначала давайте загрузим минимизированный плагин.
<script src = "resources / js / imageCrop / jquery.imagecrop.min.js" type = "text / javascript"> </ script>
Чтобы протестировать плагин, нам нужно каким-то образом получить размер и положение выделения. Вот почему мы будем использовать onSelect
вызов onSelect
; возвращает объект с текущим состоянием плагина.
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
|
$(document).ready(function() {
$(‘img#example’).imageCrop({
displayPreview : true,
displaySize : true,
overlayOpacity : 0.25,
onSelect : updateForm
});
});
var selectionExists;
// Update form inputs
function updateForm(crop) {
$(‘input#x’).val(crop.selectionX);
$(‘input#y’).val(crop.selectionY);
$(‘input#width’).val(crop.selectionWidth);
$(‘input#height’).val(crop.selectionHeight);
selectionExists = crop.selectionExists();
};
// Validate form data
function validateForm() {
if (selectionExists)
return true;
alert(‘Please make a selection first!’);
return false;
};
|
Функция updateForm()
устанавливает входные значения и сохраняет их, если выбор существует. Затем функция validateForm()
проверяет, существует ли выбор, и отображает всплывающее окно с предупреждением, если это необходимо.
Давайте добавим форму.
... <br /> <br /> <form action = "crop.php" method = "post" onsubmit = "return validateForm ();"> <input id = "x" name = "x" type = "hidden" /> <input id = "y" name = "y" type = "hidden" /> <input id = "width" name = "width" type = "hidden" /> <input id = "height" name = "height" type = "hidden" /> <input type = "submit" value = "Обрезать изображение" /> </ Форма>
Мы добавили несколько скрытых входов и кнопку отправки.
PHP
В этом примере мы будем использовать PHP с библиотекой gd, но вы можете использовать любой другой серверный язык сценариев, который поддерживает графическую библиотеку.
Создайте пустой файл, назовите его crop.php
и запустите ваш редактор.
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
|
<?php
if ($_SERVER[‘REQUEST_METHOD’] == ‘POST’)
{
// Initialize the size of the output image
$boundary = 150;
$dst_w = $_POST[‘width’];
$dst_h = $_POST[‘height’];
if ($dst_w > $dst_h)
{
$dst_h = $dst_h * $boundary / $dst_w;
$dst_w = $boundary;
}
else
{
$dst_w = $dst_w * $boundary / $dst_h;
$dst_h = $boundary;
}
// Initialize the quality of the output image
$quality = 80;
// Set the source image path
$src_path = ‘resources/images/example.jpg’;
// Create a new image from the source image path
$src_image = imagecreatefromjpeg($src_path);
// Create the output image as a true color image at the specified size
$dst_image = imagecreatetruecolor($dst_w, $dst_h);
// Copy and resize part of the source image with resampling to the
// output image
imagecopyresampled($dst_image, $src_image, 0, 0, $_POST[‘x’],
$_POST[‘y’], $dst_w, $dst_h, $_POST[‘width’],
$_POST[‘height’]);
// Destroy the source image
imagedestroy($src_image);
// Send a raw HTTP header
header(‘Content-type: image/jpeg’);
// Output the image to browser
imagejpeg($dst_image, null, $quality);
// Destroy the output image
imagedestroy($dst_image);
// Terminate the current script
exit();
}
?>
|
Мы использовали метод imagecreatefromjpeg()
для создания нового изображения из исходного пути и метод imagecreatetruecolor()
для создания выходного изображения в виде изображения с истинным цветом. Затем мы вызвали imagecopyresampled()
чтобы скопировать и изменить размер части изображения с повторной выборкой. Текущий тип документа не тот, который нам нужен, поэтому мы вызываем функцию header()
чтобы изменить его на image/jpeg
. Изображения, которые больше не нужны, уничтожаются с помощью функции imagedestroy()
. С помощью exit()
мы прекращаем выполнение текущего скрипта.
Это все
Теперь у нас есть полностью настраиваемый плагин для обрезки изображений jQuery, который позволяет пользователю создавать, перетаскивать и изменять размер выделения, отображает подсказку о размере и панель предварительного просмотра. И да, это выглядит одинаково даже в Internet Explorer 6! Таким образом, это учебник из двух частей! Спасибо за прочтение!