Статьи

Как создать плагин для обрезки изображений jQuery с нуля. Часть I

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


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

Дерево каталогов

Затем вам нужно скачать библиотеку jQuery JavaScript и поместить ее в папку /resources/js/ . Изображение, используемое в этом руководстве, должно называться example.jpg и помещаться в папку /resources/images/ . Вы можете использовать это изображение (спасибо gsso-stock ), поставляемый с исходными файлами этого руководства или одним из ваших собственных. И последний файл — это файл outline.gif , который должен быть помещен в папку /resources/js/imageCrop/ .


Чтобы протестировать наш плагин, нам нужно прикрепить его к изображению. Прежде чем приступить к работе, мы создадим простую страницу с этим изображением.

Откройте файл index.html в вашем любимом текстовом редакторе и напишите следующий код.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Transitional//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd»>
<html lang=»en» xml:lang=»en» xmlns=»http://www.w3.org/1999/xhtml»>
    <head>
        <meta content=»text/html; charset=UTF-8″ http-equiv=»Content-Type» />
        <title>jQuery Image Cropping Plug-In</title>
        <link href=»style.css» media=»screen» rel=»stylesheet» type=»text/css» />
        <link href=»resources/js/imageCrop/jquery.imagecrop.css» media=»screen» rel=»stylesheet» type=»text/css» />
        <script src=»resources/js/jquery-1.6.2.min.js» type=»text/javascript»></script>
        <script src=»resources/js/imageCrop/jquery.imagecrop.js» type=»text/javascript»></script>
    </head>
 
    <body>
        <div id=»wrapper»>
            <h1>jQuery Image Cropping Plug-In</h1>
 
            <div class=»image-decorator»>
                <img alt=»jQuery Image Cropping Plug-In» height=»360″ id=»example» src=»resources/images/example.jpg» width=»480″ />
            </div><!— .image-decorator —>
        </div><!— #wrapper —>
    </body>
</html>

Здесь нет ничего необычного: просто HTML-код. Мы загрузили таблицу стилей для страницы, jQuery, наши плагины (которые в настоящее время пусты) и поместили изображение в документ.

Теперь отредактируйте style.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
32
33
34
35
36
37
38
* {
    margin : 0;
    outline : 0;
    padding : 0;
}
 
body {
    background-color : #ededed;
    color : #646464;
    font-family : ‘Verdana’, ‘Geneva’, sans-serif;
    font-size : 12px;
    text-shadow : 0 1px 0 #ffffff;
}
 
h1 {
    font-size : 24px;
    font-weight : normal;
    margin : 0 0 10px 0;
}
 
div#wrapper {
    margin : 25px 25px 25px 25px;
}
 
div.image-decorator {
    -moz-border-radius : 5px 5px 5px 5px;
    -moz-box-shadow : 0 0 6px #c8c8c8;
    -webkit-border-radius : 5px 5px 5px 5px;
    -webkit-box-shadow : 0 0 6px #c8c8c8;
    background-color : #ffffff;
    border : 1px solid #c8c8c8;
    border-radius : 5px 5px 5px 5px;
    box-shadow : 0 0 6px #c8c8c8;
    display : inline-block;
    height : 360px;
    padding : 5px 5px 5px 5px;
    width : 480px;
}

Мы настроили аспект нашей страницы, изменив цвет фона и добавив некоторые базовые стили к заголовку и изображению.


Давайте начнем с создания базового плагина jQuery.

«Узнайте больше о том, как написать свой собственный плагин, в этом посте . В нем изложены основы, лучшие практики и распространенные ошибки, на которые следует обратить внимание, когда вы начнете писать свой плагин».

Откройте /resources/js/imageCrop/jquery.imagecrop.js и добавьте следующий код.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Always wrap a plug-in in ‘(function($) { // Plug-in goes here }) (jQuery);’
(function($) {
    $.imageCrop = function(object, customOptions) {};
 
    $.fn.imageCrop = function(customOptions) {
        //Iterate over each object
        this.each(function() {
            var currentObject = this,
                image = new Image();
 
            // And attach imageCrop when the object is loaded
            image.onload = function() {
                $.imageCrop(currentObject, customOptions);
            };
 
            // Reset the src because cached images don’t fire load sometimes
            image.src = currentObject.src;
        });
 
        // Unless the plug-in is returning an intrinsic value, always have the
        // function return the ‘this’ keyword to maintain chainability
        return this;
    };
}) (jQuery);

Мы только что расширили jQuery, добавив новое свойство функции к объекту jQuery.fn . Теперь у нас есть очень простой плагин, который перебирает каждый объект и присоединяет imageCrop при загрузке объекта. Обратите внимание, что кэшированные изображения иногда не load , поэтому для устранения этой проблемы мы сбрасываем атрибут src .


Возможность настройки параметров делает плагин гораздо более гибким для пользователя.

Мы определили массив с параметрами по умолчанию, а затем объединили их с пользовательскими параметрами, вызвав функцию setOptions . Давайте пойдем дальше и напишем тело этой функции.

Функция $.extend() объединяет содержимое двух или более объектов вместе в первый объект.

Следующий список описывает каждую опцию плагина.

  • allowMove — указывает, можно ли переместить выбор (значение по умолчанию — true ).
  • allowResize — указывает, можно ли изменить размер выделения (значение по умолчанию — true ).
  • allowSelect — указывает, может ли пользователь сделать новый выбор (значение по умолчанию — true ).
  • minSelect — минимальный размер области для регистрации нового выбора (значение по умолчанию [0, 0] ).
  • outlineOpacity — непрозрачность контура (значение по умолчанию 0.5 ).
  • overlayOpacity — непрозрачность оверлея (значение по умолчанию 0.5 ).
  • selectionPosition — позиция выбора (значение по умолчанию [0, 0] ).
  • selectionWidth — Ширина выделения (значение по умолчанию 0 ).
  • selectionHeight — Высота выделения (значение по умолчанию 0 ).

На этом шаге мы изменим DOM, чтобы подготовиться к следующему шагу: интерфейсу плагина.

Обзор слоев

Сначала мы инициализируем слой изображения.

Теперь инициализируйте держатель изображения.

Как вы можете видеть, слой держателя имеет тот же размер, что и изображение, и относительную позицию. Далее мы вызываем функцию .wrap() чтобы поместить изображение в держатель.

Над изображением будет наложенный слой.

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

Следующий уровень — уровень триггера; мы разместим его после наложенного слоя, как мы делали с предыдущими.

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

Мы разместим контурный слой над слоем триггера.

И наконец последний слой.

Метод .attr() возвращает значение указанного атрибута. Мы использовали его, чтобы получить изображение src и установить его в качестве фона для слоя выделения.

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

Отличное объяснение этого трюка рассматривается в этой статье .


Сначала мы инициализируем некоторые переменные.

selectionExists сообщит нам, если выбор существует. selectionOffset будет содержать смещение относительно источника изображения, а selectionOrigin укажет источник выделения. После нескольких шагов все станет намного понятнее.

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

Далее мы впервые вызовем updateInterface() для инициализации интерфейса.

Мы напишем тело этой функции в ближайшее время. Прямо сейчас давайте позаботимся о нашем первом мероприятии.

Мы вызываем .mousedown() если options.allowSelect имеет значение true . Это свяжет обработчик события с событием mousedown уровня триггера. Таким образом, если пользователь щелкает изображение, setSelection() .

Первая функция, getElementOffset() , возвращает левую и верхнюю координаты указанного объекта относительно документа. Мы .offset() это значение, вызвав метод .offset() . Вторая функция, getMousePosition() , возвращает текущую позицию мыши, но относительно позиции изображения. Итак, мы будем работать со значениями, которые находятся только между 0 и шириной / высотой изображения на оси x / y соответственно.

Давайте напишем функцию для обновления наших слоев.

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

Функция updateTriggerLayer() меняет курсор на crosshair или default , в зависимости от значения options.allowSelect .

Далее мы напишем updateSelection() . Он обновит не только слой выделения, но и контурный слой.

Сначала эта функция устанавливает свойства слоя структуры: курсор, отображение, размер и его положение. Далее идет слой выбора; новое значение положения фона заставит изображения плавно перекрываться.

Теперь нам нужна функция для обновления курсора при необходимости. Например, когда мы делаем выделение, мы хотим, чтобы курсор оставался crosshair независимо от того, над каким слоем мы находимся.

Да, это так просто, как кажется. Просто измените тип курсора на указанный!

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

Как видите, updateInterface() фильтрует некоторые случаи и вызывает необходимые функции, которые мы только что написали.


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

Во-первых, функция setSelection вызывает два метода: event.preventDefault() и event.stopPropagation() . Это предотвращает уведомление о событии действия по умолчанию и любых родительских обработчиков. Метод .mousemove() связывает обработчик события с событием mousemove . Это будет вызывать resizeSelection() каждый раз, когда пользователь перемещает указатель мыши. Чтобы уведомить, что делается новый выбор, переменная selectionExists становится true а размер выбора устанавливается getMousePosition() 0. Далее, мы получаем источник выбора, вызывая нашу ранее написанную функцию getMousePosition() , и передаем ее значение options.selectionPosition . Наконец, мы вызываем updateInterface() чтобы обновить интерфейс плагина в соответствии с внесенными изменениями.


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

Чтобы изменить размер выделения, нам нужно получить текущую позицию мыши. Поскольку возвращаемое значение относится к размеру изображения, нам нужно заботиться только о отрицательных значениях. Это никогда не будет превышать границы изображения. Как вы знаете, мы не можем иметь отрицательное значение для свойств width или height элемента. Чтобы решить эту проблему, мы вызываем Math.abs() чтобы получить абсолютное значение, а затем перемещаем выбор.


И теперь последняя функция:

Когда выбор освобождается, releaseSelection() удаляет ранее присоединенные обработчики событий в функции setSelection() , вызывая метод .unbind() . Затем он обновляет источник выбора и проверяет минимальный размер, принятый для существования выбора.

Теперь мы почти готовы. Закройте этот файл и подготовьтесь к следующему шагу.


Откройте /resources/js/imageCrop/jquery.imagecrop.css таблицу стилей и добавьте следующие строки.

1
2
3
4
5
6
7
8
9
div#image-crop-overlay {
    background-color : #ffffff;
    overflow : hidden;
}
 
div#image-crop-outline {
    background : #ffffff url(‘outline.gif’);
    overflow : hidden;
}

Здесь нет ничего сложного; мы добавили некоторые стили для слоев наложения и контура.


Чтобы протестировать наш плагин, нам нужно прикрепить его к изображению. Давайте сделаем это и отредактируем страницу index.html .

Откройте тег script

… и напишите следующий код JavaScript.

Мы подключили наш плагин к элементу изображения с example id и установили некоторые пользовательские параметры. Мы использовали метод .ready() чтобы определить, когда DOM полностью загружен.

Предварительный просмотр плагина

Вот и все! Сохраните файл и откройте браузер, чтобы проверить его.


Теперь у нас есть базовый плагин для обрезки изображений, который позволяет нам выбрать область изображения. В следующем уроке мы добавим больше параметров настройки, создадим панель предварительного просмотра, напишем некоторые сценарии на стороне сервера, чтобы обрезать изображение … и многое другое. Надеюсь, вам понравилось время, которое мы провели вместе, и сочли этот урок полезным. Спасибо за прочтение!