├── .gitignore ├── CHANGELOG.md ├── README.md ├── Widget.php ├── actions └── UploadAction.php ├── assets ├── CropperAsset.php ├── JcropAsset.php └── SimpleAjaxUploaderAsset.php ├── composer.json ├── messages ├── config.php ├── de │ └── cropper.php ├── en │ └── cropper.php ├── es │ └── cropper.php ├── fr │ └── cropper.php ├── ja │ └── cropper.php ├── nl │ └── cropper.php ├── pl │ └── cropper.php ├── ru │ └── cropper.php ├── uk │ └── cropper.php └── zh-CN │ └── cropper.php ├── views └── widget.php └── web ├── css └── cropper.css ├── img └── nophoto.png └── js └── cropper.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### 2.0.19 2 | * Fix for calculating ration 3 | * Add settings for image quality 4 | 5 | #### 2.0.18 6 | * Add aspectRatio setting 7 | 8 | #### 2.0.17 9 | * Fix syntax error when save file 10 | 11 | 12 | #### 2.0.16 13 | * Fix saving image 14 | 15 | #### 2.0.15 16 | * Add aspectRatio at frontend 17 | * Settings for Widget at frontend 18 | * Better quality for uploaded images 19 | * Add translation at Ukrainian 20 | 21 | #### 2.0.14 22 | * Fix incorrect using widget parameters when calculate crop ratio 23 | 24 | #### 2.0.13 25 | * Change ajax uploader version 26 | 27 | #### 2.0.12 28 | * Fix of path to bower assets 29 | 30 | #### 2.0.11 31 | * Change ajax uploader version 32 | 33 | #### 2.0.10 34 | * Message translations of Chinese 35 | 36 | #### 2.0.9 37 | * Added es and fr translations in messages 38 | 39 | #### 2.0.8 40 | * Add Polish translation 41 | 42 | #### 2.0.7 43 | * Add event "onCompleteJcron" 44 | 45 | #### 2.0.6 46 | * Fixes and refactoring 47 | 48 | #### 2.0.5 49 | * Supporting UK language 50 | 51 | 52 | #### 2.0.4 53 | * PHPDoc comments added 54 | * Changelog added 55 | 56 | #### 2.0.3 57 | * Issue with adding image by drag and drop fixed 58 | * Refactoring 59 | 60 | #### 2.0.2 61 | * Clearing error message in case successful cropping 62 | 63 | #### 2.0.1 64 | * Issue with different register in file extension fixed 65 | 66 | #### 2.0.0 67 | * Using JavaScript for creating preview by FileReader 68 | * Default value for propertie noPhotoImage added 69 | * Upload and Crop actions in one UploadAction merged 70 | * CropAction removed 71 | * Clien-side validation added 72 | * Progress bar added 73 | 74 | #### 1.0.0 75 | * Initial version -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cropper 2 | =========== 3 | Yii-Framework extension for uploading and cropping images 4 | 5 | Installation 6 | ------------ 7 | 8 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 9 | 10 | Either run 11 | 12 | ``` 13 | php composer.phar require --prefer-dist budyaga/yii2-cropper "*" 14 | ``` 15 | 16 | or add 17 | 18 | ``` 19 | "budyaga/yii2-cropper": "*" 20 | ``` 21 | 22 | to the require section of your `composer.json` file. 23 | 24 | Usage 25 | ----- 26 | 27 | Once the extension is installed, simply use it in your code by : 28 | 29 | ``` 30 | use budyaga\cropper\Widget; 31 | ``` 32 | 33 | 34 | ``` 35 | 'form-profile']); ?> 36 | field($model, 'photo')->widget(Widget::className(), [ 37 | 'uploadUrl' => Url::toRoute('/user/user/uploadPhoto'), 38 | ]) ?> 39 |
40 | 'btn btn-primary']) ?> 41 |
42 | 43 | ``` 44 | Widget has following properties: 45 | 46 | | Name | Description | Default | Required | 47 | | --------|---------|-------|------| 48 | | uploadParameter | Upload parameter name | file |No | 49 | | width | The final width of the image after cropping | 200 |No | 50 | | height | The final height of the image after cropping | 200 |No | 51 | | label | Hint in box for preview | It depends on application language. You can translate this message on your language and make pull-request. |No | 52 | | uploadUrl | URL for uploading and cropping image | |Yes | 53 | | noPhotoImage | The picture, which is used when a photo is not loaded. | You can see it on screenshots in this instructions |No | 54 | | maxSize | The maximum file size (kb). | 2097152 |No | 55 | | cropAreaWidth | Width box for preview | 300 |No | 56 | | cropAreaHeight | Height box for preview | 300 |No | 57 | | aspectRatio | Fix aspect ratio of cropping area | null |No | 58 | | extensions | Allowed file extensions (string). | jpeg, jpg, png, gif |No | 59 | 60 | 61 | In UserController: 62 | 63 | ``` 64 | public function actions() 65 | { 66 | return [ 67 | 'uploadPhoto' => [ 68 | 'class' => 'budyaga\cropper\actions\UploadAction', 69 | 'url' => 'http://your_domain.com/uploads/user/photo', 70 | 'path' => '@frontend/web/uploads/user/photo', 71 | ] 72 | ]; 73 | } 74 | ``` 75 | Action has following parameters: 76 | 77 | | Name | Description | Default | Required | 78 | | --------|---------|-------|------| 79 | | path | Path for saving image after cripping | |Yes | 80 | | url | URL to which the downloaded images will be available. | |Yes | 81 | | uploadParameter | Upload parameter name. It must match the value of a similar parameter of the widget. | file |No | 82 | | maxSize | The maximum file size (kb). It must match the value of a similar parameter of the widget. | 2097152 |No | 83 | | extensions | Allowed file extensions (string). It must match the value of a similar parameter of the widget. | jpeg, jpg, png, gif |No | 84 | | width | The final width of the image after cropping. It must match the value of a similar parameter of the widget. | 200 |No | 85 | | height | The final height of the image after cropping. It must match the value of a similar parameter of the widget. | 200 |No | 86 | | jpegQuality | Quality of cropped image (JPG) | 100 |No | 87 | | pngCompressionLevel | Quality of cropped image (PNG) | 1 |No | 88 | 89 | 90 | You can use this widget on frontend and backend. For example: user can change his userpic and administrator can change users userpic. 91 | 92 | Operates as follows: 93 | -------------------- 94 | 95 | User click on new photo area or drag file 96 | 97 | ![g4n7fva](https://cloud.githubusercontent.com/assets/7313306/7107319/a09bb4a0-e16a-11e4-9ac5-f57509ba841b.png) 98 | 99 | The picture is loaded by JavaScript FileAPI. 100 | 101 | ![yeul3gy](https://cloud.githubusercontent.com/assets/7313306/7107329/02f3eeba-e16b-11e4-9f9d-fb07944a91df.png) 102 | 103 | This picture is displayed in the widget and users have the ability to crop it or upload another picture 104 | 105 | ![jaungjk](https://cloud.githubusercontent.com/assets/7313306/7107356/8581f3ae-e16b-11e4-8151-d08a4d16f1a0.png) 106 | 107 | When the user clicks "Crop image", a request with file and coordinates is sent to the server. This picture is displayed in the form, and user can save it, or change crop area, or upload another photo. 108 | 109 | ![0ejh55q](https://cloud.githubusercontent.com/assets/7313306/7107359/bddeae36-e16b-11e4-889b-484d7dbad8a5.png) 110 | -------------------------------------------------------------------------------- /Widget.php: -------------------------------------------------------------------------------- 1 | uploadUrl === null) { 38 | throw new InvalidConfigException(Yii::t('cropper', 'MISSING_ATTRIBUTE', ['attribute' => 'uploadUrl'])); 39 | } else { 40 | $this->uploadUrl = rtrim(Yii::getAlias($this->uploadUrl), '/') . '/'; 41 | } 42 | 43 | if ($this->label == '') { 44 | $this->label = Yii::t('cropper', 'DEFAULT_LABEL'); 45 | } 46 | } 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | public function run() 52 | { 53 | $this->registerClientAssets(); 54 | 55 | return $this->render('widget', [ 56 | 'model' => $this->model, 57 | 'widget' => $this 58 | ]); 59 | } 60 | 61 | /** 62 | * Register widget asset. 63 | */ 64 | public function registerClientAssets() 65 | { 66 | $view = $this->getView(); 67 | $assets = CropperAsset::register($view); 68 | 69 | if ($this->noPhotoImage == '') { 70 | $this->noPhotoImage = $assets->baseUrl . '/img/nophoto.png'; 71 | } 72 | 73 | $settings = array_merge([ 74 | 'url' => $this->uploadUrl, 75 | 'name' => $this->uploadParameter, 76 | 'maxSize' => $this->maxSize / 1024, 77 | 'allowedExtensions' => explode(', ', $this->extensions), 78 | 'size_error_text' => Yii::t('cropper', 'TOO_BIG_ERROR', ['size' => $this->maxSize / (1024 * 1024)]), 79 | 'ext_error_text' => Yii::t('cropper', 'EXTENSION_ERROR', ['formats' => $this->extensions]), 80 | 'accept' => 'image/*', 81 | ], $this->pluginOptions); 82 | 83 | if(is_numeric($this->aspectRatio)) { 84 | $settings['aspectRatio'] = $this->aspectRatio; 85 | } 86 | 87 | if ($this->onCompleteJcrop) 88 | $settings['onCompleteJcrop'] = $this->onCompleteJcrop; 89 | 90 | $view->registerJs( 91 | 'jQuery("#' . $this->options['id'] . '").parent().find(".new-photo-area").cropper(' . Json::encode($settings) . ', ' . $this->width . ', ' . $this->height . ');', 92 | $view::POS_READY 93 | ); 94 | } 95 | 96 | /** 97 | * Register widget translations. 98 | */ 99 | public static function registerTranslations() 100 | { 101 | if (!isset(Yii::$app->i18n->translations['cropper']) && !isset(Yii::$app->i18n->translations['cropper/*'])) { 102 | Yii::$app->i18n->translations['cropper'] = [ 103 | 'class' => 'yii\i18n\PhpMessageSource', 104 | 'basePath' => '@budyaga/cropper/messages', 105 | 'forceTranslation' => true, 106 | 'fileMap' => [ 107 | 'cropper' => 'cropper.php' 108 | ] 109 | ]; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /actions/UploadAction.php: -------------------------------------------------------------------------------- 1 | url === null) { 35 | throw new InvalidConfigException(Yii::t('cropper', 'MISSING_ATTRIBUTE', ['attribute' => 'url'])); 36 | } else { 37 | $this->url = rtrim($this->url, '/') . '/'; 38 | } 39 | if ($this->path === null) { 40 | throw new InvalidConfigException(Yii::t('cropper', 'MISSING_ATTRIBUTE', ['attribute' => 'path'])); 41 | } else { 42 | $this->path = rtrim(Yii::getAlias($this->path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; 43 | } 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function run() 50 | { 51 | if (Yii::$app->request->isPost) { 52 | $file = UploadedFile::getInstanceByName($this->uploadParam); 53 | $model = new DynamicModel(compact($this->uploadParam)); 54 | $model->addRule($this->uploadParam, 'image', [ 55 | 'maxSize' => $this->maxSize, 56 | 'tooBig' => Yii::t('cropper', 'TOO_BIG_ERROR', ['size' => $this->maxSize / (1024 * 1024)]), 57 | 'extensions' => explode(', ', $this->extensions), 58 | 'wrongExtension' => Yii::t('cropper', 'EXTENSION_ERROR', ['formats' => $this->extensions]) 59 | ])->validate(); 60 | 61 | if ($model->hasErrors()) { 62 | $result = [ 63 | 'error' => $model->getFirstError($this->uploadParam) 64 | ]; 65 | } else { 66 | $model->{$this->uploadParam}->name = uniqid() . '.' . $model->{$this->uploadParam}->extension; 67 | $request = Yii::$app->request; 68 | 69 | $width = $request->post('width', $this->width); 70 | $height = $request->post('height', $this->height); 71 | 72 | $image = Image::crop( 73 | $file->tempName . $request->post('filename'), 74 | intval($request->post('w')), 75 | intval($request->post('h')), 76 | [$request->post('x'), $request->post('y')] 77 | )->resize( 78 | new Box($width, $height) 79 | ); 80 | 81 | if (!file_exists($this->path) || !is_dir($this->path)) { 82 | $result = [ 83 | 'error' => Yii::t('cropper', 'ERROR_NO_SAVE_DIR')] 84 | ; 85 | } else { 86 | $saveOptions = ['jpeg_quality' => $this->jpegQuality, 'png_compression_level' => $this->pngCompressionLevel]; 87 | if ($image->save($this->path . $model->{$this->uploadParam}->name, $saveOptions)) { 88 | $result = [ 89 | 'filelink' => $this->url . $model->{$this->uploadParam}->name 90 | ]; 91 | } else { 92 | $result = [ 93 | 'error' => Yii::t('cropper', 'ERROR_CAN_NOT_UPLOAD_FILE') 94 | ]; 95 | } 96 | } 97 | } 98 | Yii::$app->response->format = Response::FORMAT_JSON; 99 | 100 | return $result; 101 | } else { 102 | throw new BadRequestHttpException(Yii::t('cropper', 'ONLY_POST_REQUEST')); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /assets/CropperAsset.php: -------------------------------------------------------------------------------- 1 | __DIR__ . DIRECTORY_SEPARATOR . '..', 5 | 'languages' => ['ru', 'en', 'uk', 'pl', 'de', 'nl'], 6 | 'translator' => 'Yii::t', 7 | 'sort' => false, 8 | 'removeUnused' => false, 9 | 'only' => ['*.php'], 10 | 'except' => [ 11 | '.svn', 12 | '.git', 13 | '.gitignore', 14 | '.gitkeep', 15 | '.hgignore', 16 | '.hgkeep', 17 | '/messages', 18 | ], 19 | 'format' => 'php', 20 | 'messagePath' => __DIR__, 21 | 'overwrite' => true, 22 | ]; 23 | -------------------------------------------------------------------------------- /messages/de/cropper.php: -------------------------------------------------------------------------------- 1 | 'Bild Datei hier hineinziehen oder clicken und auswählen', 21 | 'CROP_PHOTO' => 'Bild zuschneiden', 22 | 'DELETE_PHOTO' => 'Bild löschen', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Datei kann nicht hochgeladen werden', 24 | 'MISSING_ATTRIBUTE' => 'Attibut "{attribute}" muss spezifiziert sein', 25 | 'ONLY_POST_REQUEST' => 'Nur POST-Anfragen erlaubt', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Anderes Bild hochladen', 27 | 'TOO_BIG_ERROR' => 'Datei überschreitet die erlaubte Größe von ({size} Mb)', 28 | 'EXTENSION_ERROR' => 'Lediglich die folgenden Formate sind zulässig: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/en/cropper.php: -------------------------------------------------------------------------------- 1 | 'Click and select or drag an image file here', 21 | 'CROP_PHOTO' => 'Crop image', 22 | 'DELETE_PHOTO' => 'Delete image', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Can`t upload file', 24 | 'MISSING_ATTRIBUTE' => 'Attribute "{attribute}" must be specified', 25 | 'ONLY_POST_REQUEST' => 'Allowed only POST-request', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Upload another photo', 27 | 'TOO_BIG_ERROR' => 'Exceeded the allowable size of the file ({size} Mb)', 28 | 'EXTENSION_ERROR' => 'Enable only the following file formats: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/es/cropper.php: -------------------------------------------------------------------------------- 1 | 'Para bajar la photo, pulse aqui o desplace la photo en est cuadro', 21 | 'CROP_PHOTO' => 'Recortar la photo', 22 | 'DELETE_PHOTO' => 'Suprimir photo', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Poroblema : no se puede bajar esta photo', 24 | 'MISSING_ATTRIBUTE' => 'El attibuto "{attribute}" tiene que ser especificado', 25 | 'ONLY_POST_REQUEST' => 'Solamente pedidos en POST-request', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Subir otra photo', 27 | 'TOO_BIG_ERROR' => 'Excedió la talla permisible del fichero ({size} Mb)', 28 | 'EXTENSION_ERROR' => 'Solamente se pueden subir estos formatos: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/fr/cropper.php: -------------------------------------------------------------------------------- 1 | 'Pour ajouter une photo cliquez ici ou déplacez la photo', 21 | 'CROP_PHOTO' => 'Rogner la photo', 22 | 'DELETE_PHOTO' => 'Effacer la photo', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Impossible de télécharger la photo', 24 | 'MISSING_ATTRIBUTE' => 'L\'attibut "{attribute}" doit être spécifié', 25 | 'ONLY_POST_REQUEST' => 'Seulement les requêtes POST-request sont admises', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Charger une autre photo', 27 | 'TOO_BIG_ERROR' => 'Dépassement de la taille limite permise du fichier : ({size} Mb)', 28 | 'EXTENSION_ERROR' => 'Seuls ces formats sont admis: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/ja/cropper.php: -------------------------------------------------------------------------------- 1 | 'クリックして画像ファイルを選択、または、画像ファイルをここにドラッグ', 21 | 'CROP_PHOTO' => '画像を切り抜き', 22 | 'DELETE_PHOTO' => '画像を消去', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'ファイルをアップロード出来ません', 24 | 'MISSING_ATTRIBUTE' => '"{attribute}" は必須です', 25 | 'ONLY_POST_REQUEST' => 'POST リクエストのみ許可されています', 26 | 'UPLOAD_ANOTHER_PHOTO' => '別の画像をアップロード', 27 | 'TOO_BIG_ERROR' => '許可されているファイル・サイズ({size} Mb)を超過しています', 28 | 'EXTENSION_ERROR' => '以下のファイル形式だけが許可されています: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/nl/cropper.php: -------------------------------------------------------------------------------- 1 | 'Een afbeelding hierheen slepen of klikken en selecteren', 21 | 'CROP_PHOTO' => 'Afbeelding bijsnijden', 22 | 'DELETE_PHOTO' => 'Afbeelding verwijderen', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Bestand kan niet worden geüpload', 24 | 'MISSING_ATTRIBUTE' => 'Attribuut "{attribute}" moet gespecificeerd zijn', 25 | 'ONLY_POST_REQUEST' => 'Alleen POST-requests toegestaan', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Upload een andere afbeelding', 27 | 'TOO_BIG_ERROR' => 'De toegestane bestandsgrootte is overschreden ({size} Mb)', 28 | 'EXTENSION_ERROR' => 'Alleen de volgende formaten zijn toegestaan: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/pl/cropper.php: -------------------------------------------------------------------------------- 1 | 'Kliknij lub przeciągnij i upuść tutaj nowe zdjęcie', 5 | 'CROP_PHOTO' => 'Przytnij zdjęcie', 6 | 'DELETE_PHOTO' => 'Skasuj zdjęcie', 7 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Nie udało się załadować zdjęcia', 8 | 'MISSING_ATTRIBUTE' => 'Atrybut "{attribute}" jest wymagany', 9 | 'ONLY_POST_REQUEST' => 'Tylko żądania POST są obsługiwane', 10 | 'UPLOAD_ANOTHER_PHOTO' => 'Wybierz inne zdjęcie', 11 | 'TOO_BIG_ERROR' => 'Maksymalny rozmiar obrazka to ({size} Mb)', 12 | 'EXTENSION_ERROR' => 'Dozwolone są tylko formaty: {formats}' 13 | ]; -------------------------------------------------------------------------------- /messages/ru/cropper.php: -------------------------------------------------------------------------------- 1 | 'Для загрузки новой фотографии, кликните здесь или перетащите файл сюда', 21 | 'CROP_PHOTO' => 'Обрезать фото', 22 | 'DELETE_PHOTO' => 'Удалить фото', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Невозможно загрузить файл', 24 | 'MISSING_ATTRIBUTE' => 'Атрибут "{attribute}" должен быть указан', 25 | 'ONLY_POST_REQUEST' => 'Допускается только POST-запрос', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Загрузить другое фото', 27 | 'TOO_BIG_ERROR' => 'Превышен допустимый размер загружаемого файла ({size} Мб)', 28 | 'EXTENSION_ERROR' => 'Разрешены только следующие форматы файлов: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /messages/uk/cropper.php: -------------------------------------------------------------------------------- 1 | 'Для завантаження нового фото, натисніть тут або перетягніть файл сюди', 21 | 'CROP_PHOTO' => 'Обрізати фото', 22 | 'DELETE_PHOTO' => 'Видалити фото', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => 'Неможливо завантажити файл', 24 | 'MISSING_ATTRIBUTE' => 'Атрибут "{attribute}" повинен бути вказаний', 25 | 'ONLY_POST_REQUEST' => 'Допустимий лише POST-запит', 26 | 'UPLOAD_ANOTHER_PHOTO' => 'Завантажити інше фото', 27 | 'TOO_BIG_ERROR' => 'Максимальний розмір файлу перевищений ({size} Мб)', 28 | 'EXTENSION_ERROR' => 'Дозволені лише наступні формати файлів: {formats}', 29 | 'ERROR_NO_SAVE_DIR' => 'Директорії для збереження не існує!' 30 | ]; 31 | -------------------------------------------------------------------------------- /messages/zh-CN/cropper.php: -------------------------------------------------------------------------------- 1 | '点此上传图片,或把图片拖到这里', 21 | 'CROP_PHOTO' => '裁剪图片', 22 | 'DELETE_PHOTO' => '删除图片', 23 | 'ERROR_CAN_NOT_UPLOAD_FILE' => '上传失败', 24 | 'MISSING_ATTRIBUTE' => '"{attribute}"不能为空', 25 | 'ONLY_POST_REQUEST' => '必须使用POST请求', 26 | 'UPLOAD_ANOTHER_PHOTO' => '重新上传', 27 | 'TOO_BIG_ERROR' => '上传图片的大小超出了限制 ({size} Mb)', 28 | 'EXTENSION_ERROR' => '仅允许上传以下格式的图片: {formats}' 29 | ]; 30 | -------------------------------------------------------------------------------- /views/widget.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | attribute, ['class' => 'photo-field']); ?> 14 | width, ['class' => 'width-input']); ?> 15 | height, ['class' => 'height-input']); ?> 16 | {$widget->attribute} != '' 18 | ? $model->{$widget->attribute} 19 | : $widget->noPhotoImage, 20 | [ 21 | 'style' => 'max-height: ' . $widget->thumbnailHeight . 'px; max-width: ' . $widget->thumbnailWidth . 'px', 22 | 'class' => 'thumbnail', 23 | 'data-no-photo' => $widget->noPhotoImage 24 | ] 25 | ); ?> 26 | 27 |
28 | 31 | 34 | 37 |
38 | 39 |
40 |
41 | label;?> 42 |
43 |
44 | 49 |
-------------------------------------------------------------------------------- /web/css/cropper.css: -------------------------------------------------------------------------------- 1 | .new-photo-area { 2 | border: dashed 2px #EEEEEE; 3 | box-sizing: content-box; 4 | margin: 20px 0px; 5 | text-align: center; 6 | vertical-align: middle; 7 | } 8 | .new-photo-area .cropper-label { 9 | cursor: pointer; 10 | padding: 10px; 11 | display: table; 12 | height: 100%; 13 | width: 100%; 14 | } 15 | .new-photo-area .cropper-label span { 16 | display: table-cell; 17 | vertical-align: middle; 18 | } 19 | .new-photo-area img { 20 | visibility: hidden; 21 | } -------------------------------------------------------------------------------- /web/img/nophoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krivochenko/yii2-cropper/9621dc877b237d013eb955ef4fe4cedcbbdf8f86/web/img/nophoto.png -------------------------------------------------------------------------------- /web/js/cropper.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | $.fn.cropper = function (options, width, height) { 3 | var $widget = $(this).closest('.cropper-widget'), 4 | $progress = $widget.find('.progress'), 5 | cropper = { 6 | $widget: $widget, 7 | $progress: $progress, 8 | $progress_bar: $progress.find('.progress-bar'), 9 | $thumbnail: $widget.find('.thumbnail'), 10 | $photo_field: $widget.find('.photo-field'), 11 | $upload_new_photo: $widget.find('.upload-new-photo'), 12 | $new_photo_area: $widget.find('.new-photo-area'), 13 | $cropper_label: $widget.find('.cropper-label'), 14 | $cropper_buttons: $widget.find('.cropper-buttons'), 15 | $width_input: $widget.find('.width-input'), 16 | $height_input: $widget.find('.height-input'), 17 | uploader: null, 18 | reader: null, 19 | selectedFile: null, 20 | init: function () { 21 | cropper.reader = new FileReader(); 22 | cropper.reader.onload = function (e) { 23 | cropper.clearOldImg(); 24 | 25 | cropper.$new_photo_area.append(''); 26 | cropper.$img = cropper.$new_photo_area.find('img'); 27 | 28 | var image = new Image(); 29 | image.src = e.target.result; 30 | 31 | image.onload = function() { 32 | var x1 = (this.width - width) / 2; 33 | var y1 = (this.height - height) / 2; 34 | var x2 = x1 + width; 35 | var y2 = y1 + height; 36 | var aspectRatio = (options.aspectRatio !== null && typeof options.aspectRatio !== 'undefined') ? options.aspectRatio : width / height; 37 | 38 | cropper.$img.Jcrop({ 39 | aspectRatio: aspectRatio, 40 | setSelect: [x1, y1, x2, y2], 41 | boxWidth: cropper.$new_photo_area.width(), 42 | boxHeight: cropper.$new_photo_area.height(), 43 | keySupport: false 44 | }); 45 | }; 46 | 47 | cropper.setProgress(0); 48 | }; 49 | 50 | var settings = $.extend({ 51 | button: [ 52 | cropper.$cropper_label, 53 | cropper.$upload_new_photo 54 | ], 55 | dropzone: cropper.$cropper_label, 56 | responseType: 'json', 57 | noParams: true, 58 | multipart: true, 59 | onChange: function () { 60 | if (cropper.selectedFile) { 61 | cropper.selectedFile = null; 62 | cropper.uploader._queue = []; 63 | } 64 | return true; 65 | }, 66 | onSubmit: function () { 67 | if (cropper.selectedFile) { 68 | return true; 69 | } 70 | cropper.selectedFile = cropper.uploader._queue[0]; 71 | 72 | cropper.setProgress(55); 73 | cropper.showError(''); 74 | cropper.reader.readAsDataURL(this._queue[0].file); 75 | return false; 76 | }, 77 | onComplete: function (filename, response) { 78 | cropper.$progress.addClass('hidden'); 79 | if (response['error']) { 80 | cropper.showError(response['error']); 81 | return; 82 | } 83 | cropper.showError(''); 84 | 85 | cropper.$thumbnail.attr({'src': response['filelink']}); 86 | cropper.$photo_field.val(response['filelink']); 87 | if ((typeof options.onCompleteJcrop !== "undefined") && (typeof options.onCompleteJcrop === "string")) { 88 | eval('var onCompleteJcrop = ' + options.onCompleteJcrop); 89 | onCompleteJcrop(filename, response); 90 | } 91 | }, 92 | onSizeError: function () { 93 | cropper.showError(options['size_error_text']); 94 | cropper.cropper.setProgress(0); 95 | }, 96 | onExtError: function () { 97 | cropper.showError(options['ext_error_text']); 98 | cropper.setProgress(0); 99 | } 100 | }, options); 101 | 102 | cropper.uploader = new ss.SimpleUpload(settings); 103 | 104 | cropper.$widget 105 | .on('click', '.delete-photo', function () { 106 | cropper.deletePhoto(); 107 | }) 108 | .on('click', '.crop-photo', function () { 109 | var data = cropper.$img.data('Jcrop').tellSelect(); 110 | data[yii.getCsrfParam()] = yii.getCsrfToken(); 111 | data['width'] = cropper.$width_input.val(); 112 | data['height'] = cropper.$height_input.val(); 113 | 114 | if (cropper.uploader._queue.length) { 115 | cropper.selectedFile = cropper.uploader._queue[0]; 116 | } else { 117 | cropper.uploader._queue[0] = cropper.selectedFile; 118 | } 119 | cropper.uploader.setData(data); 120 | 121 | cropper.setProgress(1); 122 | cropper.uploader.setProgressBar(cropper.$progress_bar); 123 | 124 | cropper.readyForSubmit = true; 125 | cropper.uploader.submit(); 126 | }); 127 | }, 128 | showError: function (error) { 129 | if (error == '') { 130 | cropper.$widget.parents('.form-group').removeClass('has-error').find('.help-block').text(''); 131 | } else { 132 | cropper.$widget.parents('.form-group').addClass('has-error').find('.help-block').text(error); 133 | } 134 | }, 135 | setProgress: function (value) { 136 | if (value) { 137 | cropper.$cropper_buttons.find('button').removeClass('hidden'); 138 | cropper.$cropper_label.addClass('hidden'); 139 | cropper.$progress.removeClass('hidden'); 140 | cropper.$progress_bar.css({'width': value + '%'}); 141 | } else { 142 | cropper.$progress.addClass('hidden'); 143 | cropper.$progress_bar.css({'width': 0}); 144 | } 145 | }, 146 | deletePhoto: function () { 147 | cropper.$photo_field.val(''); 148 | cropper.$thumbnail.attr({'src': cropper.$thumbnail.data('no-photo')}); 149 | }, 150 | clearOldImg: function () { 151 | if (cropper.$img) { 152 | cropper.$img.data('Jcrop').destroy(); 153 | cropper.$img.remove(); 154 | cropper.$img = null; 155 | } 156 | } 157 | }; 158 | 159 | cropper.init(); 160 | }; 161 | })(jQuery); 162 | --------------------------------------------------------------------------------