Блог хеллоуворлдщика

Кадрирование изображения до загрузки на сервер для Yii2


Это реализация и улучшение виджета для Yii2 представленного здесь ранее мной для Yii первой версии. Данный виджет потерпел некоторые изменения, а именно:

Ссылка на виджет и инструкция по установке и использованию на GitHub https://github.com/bupy7/yii2-widget-cropbox

Ссылка на Packagist https://packagist.org/packages/bupy7/yii2-widget-cropbox

share via vkontakte share via facebook share via mailru share via odnoklassniki share via twitter

Комментарии [28]

[Юрий] 20 дек. 2014 г., 0:01:40

Приветствую! Отличный виджет, простой и понятный. Не подскажешь, нет ли у тебя в планах его функционал расширить? Например разрешить помещать в область, где, собственно и вырезается изображение, оригинальное изображение после обрезания и добавить возможность настроить внешний вид изображения preview в div.cropped ?

Администратор [BUPY7] 20 дек. 2014 г., 14:15:36

Привет!

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

Функционал, в ближайшее время, думаю, что нет. Если есть желание помочь сделать его лучше - я только за. =)

Администратор [BUPY7] 20 дек. 2014 г., 14:18:47

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

Администратор [BUPY7] 20 дек. 2014 г., 14:32:04

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

$module = Yii::$app->getModule('module-name'); //здесь я получил экземпляр класса модели     
 
//здесь я обрабатываю всю полученную информацию после обрезки
$cropInfo = \yii\helpers\Json::decode($this->crop_info);
$cropInfo['dw'] = (int)$cropInfo['dw'];
$cropInfo['dh'] = (int)$cropInfo['dh'];
$cropInfo['x'] = abs($cropInfo['x']);
$cropInfo['y'] = abs($cropInfo['y']);
$cropInfo['ratio'] = $cropInfo['ratio'] == 0 ? 1.0 : (float)$cropInfo['ratio']; //вот как раз этот параметр нам необходим для обрезки сохраняя пропорцию изображения, а не обрезка под размер казанной области
 
$image = Image::getImagine()->open($this->image->tempName);
 
/*
$module->middleImageSize это массив, где [0] это ширина рамки для обрезки, [1] это высота
 
выполнив простые манипуляции с ratio мы получим размеры и точки рамки для обрезки относительно оригинального изображения,
т.е. сохраняя пропорции, а не обрежем и не ужмём изображение, как мы это видим
*/
$cropSizeLarge = new \Imagine\Image\Box($module->middleImageSize[0] / $cropInfo['ratio'], $module->middleImageSize[1] / $cropInfo['ratio']);
$cropPointLarge = new \Imagine\Image\Point($cropInfo['x'] / $cropInfo['ratio'], $cropInfo['y'] / $cropInfo['ratio']);
$pathLargeImage = Yii::getAlias($module->pathToImages) . '/' . $module->largeImagePrefix . $this->id . '.' . $this->image->getExtension(); //здесь просто путь для сохранения изображения
 
//ну и собственно обрезаем изображение
$image->copy()
    ->crop($cropPointLarge, $cropSizeLarge)
    ->save($pathLargeImage, ['quality' => $module->qualityLarge]);
Администратор [BUPY7] 20 дек. 2014 г., 14:19:52

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

[Михаил] 15 апр. 2015 г., 22:35:26

Да не, я думаю, человек имел в виду возможность повторной обрезки уже загруженного изображения на сервер. Сейчас попытаюсь сделать на своём проекте, если получится, то могу поделиться. Правда, с yii2 я только начинаю работать серьёзно

[DASHKA] 31 янв. 2015 г., 22:58:55

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

Администратор [BUPY7] 31 янв. 2015 г., 23:55:22

Привет! Если помещать целиком в область кадрирования, то ничего уезжать не должно. Такое происходит, когда область кадрирования "нереальна", т.е. таких координат на изображении нет. Задача по кадрированию изображения за действительной областью пока в плане. Когда будет реализовано - не могу сказать. Если есть желание - можете мне помочь. =) Не забудьте только отправить пул-реквест. =)

[DASHKA] 01 февр. 2015 г., 19:06:14

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

Администратор [BUPY7] 01 февр. 2015 г., 21:44:12

На самом деле, почему-то мне показалось, что в превью обрезка за действительной областью работает не верно, хотя всё норм и всё корректно отображается. Поэтому, смотрите в сторону обработки изображения в afterSave(), скорее всего, нужно использовать ветвление, когда координаты x и y получаются положительными, т.е. обрезка изображения уже выполняется иначе, т.к. размеры самого изображения придется высчитывать относительно того, насколько вы далеко зашли за действительную область.

[POLUMERK] 13 марта 2015 г., 15:42:21

Добрый день! Пытаюсь установить ваш код с помощью composer, но выдается ошибка:

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for bupy7/yii2-widget-cropbox dev-master -> satisfiable by bupy7/yii2-widget-cropbox[dev-master].
    - bupy7/yii2-widget-cropbox dev-master requires kartik-v/yii2-icons dev-master -> no matching package found.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.

Read <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

Installation failed, reverting ./composer.json to its original content.

Проверяю тот пакет на который вы ссылаетесь есть, не могу понять в чем может быть проблема тогда((

Администратор [BUPY7] 14 марта 2015 г., 5:53:04

Выполните установку снова. Сейчас должно работать.

[MULAT] 19 апр. 2015 г., 23:32:59

Спасибо за виджет! Вариант для вывода в статьях сделал по приведённому примеру - всё отлично, но для thumbnails немного поизвращаться пришлось. Хотел выводить миниатюры на страницах категорий заданного размера, скажем 160*120. Проблема сохранения пропорций с обрезкой лишнего решается так: // models/Post.php

use Imagine\Image\ImageInterface;
 
public function afterSave($insert, $changedAttributes)  {
        ...
 
        // saving thumbnail-image
        $pathThumbImage = Yii::getAlias('@images/thumb/') . $this->id . '.' . $this->image->getExtension();
        $image->thumbnail(new Box(160, 120), ImageInterface::THUMBNAIL_OUTBOUND)->save($pathThumbImage, ['quality' => 100]);
 
        ...
}
Администратор [BUPY7] 20 апр. 2015 г., 20:43:40

Пожалуйста. =) Хороший совет для тех, кто его ищет. =)

Не забывайте оборачивать код в

~~~ [PHP] //здесь ваш код ~~~

=)

[GESER] 27 янв. 2016 г., 14:13:21

Добрый день. Виджет не работает корректно в модальном окне. Не загружается изображение в область редактирования. В чем может быть причина?

Администратор [BUPY7] 27 янв. 2016 г., 16:06:16

Какая версия кропера?

[STAS] 29 марта 2017 г., 13:25:07

Лаги в хроме наблюдаются только у меня? Протестировал на нескольких машинах. В фоксе все норм, в гугле - хрень.

Администратор [BUPY7] 29 марта 2017 г., 15:18:23

Проблема не в браузере, а в чем-то другом. У некоторых людей тоже наблюдаются проблемы, но мне пока не доводилось лично столкнуться. Если можете определить причину лагов - буду рад пулл-реквесту.

[PATISON] 09 июня 2017 г., 12:58:17

Добрый день. Настроил виджет, работает. Но появилась одна проблема, как задать для рамки кропа пропорцию, например 3,6 к 2,2. Обрезать фото после сохранения я смог, а вот именно зафиксировать область обрезания - нет

[PATISON] 10 июня 2017 г., 13:37:43

Если кому понадобится view

<?= $form2->field($category_image, 'image')->widget(CropboxWidget::className(), [
                            'croppedDataAttribute' => 'crop_info',
                            'pluginOptions' => [
                                'variants' => [
                                    [
                                        'width' => 360,
                                        'height' => 220,
                                        'minWidth' => 360,
                                        'minHeight' => 220,
                                        'maxWidth' => 360,
                                        'maxHeight' => 220,
                                    ],
                                ],
                            ],
                        ]); ?>

model

//saving thumbnail
        $newSizeThumb = new Box($cropInfo['dWidth'], $cropInfo['dHeight']);
        $cropSizeThumb = new Box(360, 220); //frame size of crop
[PATISON] 09 июня 2017 г., 13:16:38

И еще вопрос, если форма работает через pjax, как вернуть загруженное изображение. 'croppedImagesUrl' возвращает только после перезагрузки страницы

Администратор [BUPY7] 10 июня 2017 г., 8:06:39

Если работает через Pjax, то должно все возвращать. Ведь это и есть фактически перезагрузка страницы. Виджет должен быть обернут полностью.

[PATISON] 10 июня 2017 г., 9:50:02

Тут проблема была в том, что имя изображения остается прежним и аякс подгружал его из кеша браузера. Если при сохранении менять имя изображения (я использовал time()), то все будет хорошо

Администратор [BUPY7] 10 июня 2017 г., 11:15:53

Больше проблемы нет, получается?

[PATISON] 10 июня 2017 г., 13:34:29

для тех, кому понадобится model

public $image;
    public $crop_info;
    public $image_name;
 
    public function rules()
    {
        return [
            [
                'image',
                'image',
                'extensions' => ['jpg', 'jpeg', 'png', 'gif'],
                'mimeTypes' => ['image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'],
            ],
            ['crop_info', 'safe'],
            ['src', 'safe'],
            ['image_name', 'safe'],
        ];
    }
 
 
 
 
        $pathThumbImage = Yii::getAlias('@frontend') . '/web/image/catalog/Category/'
            . 'thumb_'
            . $this->image_name
            . '.'
            . $this->image->getExtension();
 
        //saving original
        $this->image->saveAs(
            Yii::getAlias('@frontend') . '/web/image/catalog/Category/'
            . $this->image_name
            . '.'
            . $this->image->getExtension()
        );

controller

if ($category_image->load(Yii::$app->request->post())) {
            if ($category_image->src) {
                if (file_exists(Yii::getAlias(Yii::getAlias('@frontend') . '...') . $category_image->src))
                    unlink(Yii::getAlias(Yii::getAlias('@frontend') . '...') . $category_image->src);
                if (file_exists(Yii::getAlias(Yii::getAlias('@frontend') . '...') . 'thumb_' .
                    $category_image->src))
                    unlink(Yii::getAlias(Yii::getAlias('@frontend') . '...') . 'thumb_' .
                    $category_image->src);
            }
            $image = UploadedFile::getInstance($category_image, 'image');
            $category_image->image = $image;
            $category_image->category_id = $category_id;
            $image_name = time();
            $category_image->src = $image_name . '.' . $category_image->image->extension;
            $category_image->image_name = $image_name;
            if(!$category_image->save()){
                return var_dump($category_image->errors);
            }
        }
[DAMIR] 22 авг. 2017 г., 15:04:57

Спасибо за виджет! У меня такая проблема: Если выбрать фото и сохранить форму, не нажав кнопку Crop, то выкидывает ошибку - Length of either side cannot be 0 or negative, current size is 0x0.

Администратор [BUPY7] 22 авг. 2017 г., 15:09:09

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