├── .DS_Store
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── assets
└── editor.png
├── dist.tgz
├── dist
├── LICENSE
├── README.md
├── bundles
│ ├── ngx-image-editor.umd.js
│ └── ngx-image-editor.umd.min.js
├── esm2015
│ └── ngx-image-editor.js
├── esm5
│ └── ngx-image-editor.js
├── ngx-image-editor.component.d.ts
├── ngx-image-editor.d.ts
├── ngx-image-editor.metadata.json
├── ngx-image-editor.module.d.ts
├── package.json
└── public_api.d.ts
├── ng-package.json
├── package-lock.json
├── package.json
├── src
├── .DS_Store
├── ngx-image-editor.component.d.ts
├── ngx-image-editor.component.ts
├── ngx-image-editor.module.d.ts
├── ngx-image-editor.module.ts
├── public_api.d.ts
├── public_api.js
└── public_api.ts
├── tsconfig.json
└── yarn.lock
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hggeorgiev/ngx-image-editor/b9b49316ec0f621d356b3bba5ac164487fe4578d/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node generated files
2 | npm-debug.log
3 | node_modules
4 | jspm_packages
5 | .idea
6 | lib
7 | build
8 | demo/node_modules
9 |
10 | # Remove tsc generated js and map file
11 | *.map
12 | src/ngx-image-editor.component.js
13 | src/ngx-image-editor.component.js.map
14 | src/ngx-image-editor.module.js
15 | src/ngx-image-editor.module.js.map
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Node generated files
2 | node_modules
3 | npm-debug.log
4 | # OS generated files
5 | Thumbs.db
6 | .DS_Store
7 | node_modules
8 | src
9 | demo
10 | .idea
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache:
3 | yarn: true
4 | directories:
5 | - .ng_pkg_build
6 | notifications:
7 | email: false
8 | node_js:
9 | - '10'
10 |
11 | install:
12 | - yarn install
13 | after_success:
14 | - npm run semantic-release
15 | branches:
16 | only:
17 | - master
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | ## [1.3.1] - 2017-03-24
5 | ### Changes
6 | - Upgraded Angular, Angular Material, Angular Flex Layout.
7 | - Changed all material attributes and tags from md to mat.
8 | - Now the editor is a regular component not a Material Dialog component.
9 |
10 |
11 | ## [1.4.0] - 2017-05-11
12 | ### Changes
13 | - Upgraded Angular, Angular Material, Angular Flex Layout.
14 | - Removed all color-related styles
15 | - Removed modal functionality (we shouldn't assume ttrthat users may use it in a modal)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Centroida
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
ngx-image-editor
8 |
9 |
10 |
11 | Awesome editor for Angular 6 based on [Angular Material](https://github.com/angular/material2)
12 |
13 | [](https://badge.fury.io/js/ngx-image-editor)
14 | [](https://travis-ci.org/hggeorgiev/ngx-image-editor)
15 |
16 | **[Live Demo on Slackblitz](https://stackblitz.com/edit/ngx-image-editor-demo)**
17 |
18 | ## Getting started
19 |
20 | ##### Step 1: Install Angular Material (+ Material Icons) and Angular Flex Layout
21 |
22 | - [Angular Material](https://material.angular.io/guide/getting-started)
23 | - [Angular Flex-Layout](https://github.com/angular/flex-layout)
24 |
25 | ##### Step 2: Install cropperjs
26 |
27 | ```bash
28 | yarn add cropperjs
29 | ```
30 | ##### Step 3: Add `cropperjs` file paths in your `.angular.json`
31 |
32 | ```json
33 | }
34 | "styles": [
35 | "node_modules/cropperjs/dist/cropper.css"
36 | ],
37 | "scripts": [
38 | "node_modules/cropperjs/dist/cropper.js"
39 | ]
40 | }
41 | ```
42 |
43 |
44 | ##### Step 4: Install `ngx-image-editor`:
45 | ```bash
46 | yarn add ngx-image-editor
47 | ```
48 |
49 | ##### Step 5: Import the `NgxImageEditorModule` within your app:
50 | ```js
51 | import {NgxImageEditorModule} from "ngx-image-editor";
52 |
53 | @NgModule({
54 | imports: [
55 | NgxImageEditorModule
56 | ]
57 | })
58 | ```
59 |
60 |
61 | ##### Step 6: Use within your application:
62 | ```html
63 |
64 | ```
65 |
66 | ### API
67 |
68 | | Property | Description |
69 | | -------------- | -------------------------------------------------------------- |
70 | | `[config]` | An object containing editor configuration (see **Configuration**) |
71 | | `(file)` | The emitted file after editing. |
72 |
73 |
74 | #### Configuration
75 | | Property | Description |
76 | | -------------- | -------------------------------------------------------------- |
77 | | ImageName | Name of the image. |
78 | | ImageUrl | URL of the image (if it coming from a CDN) . |
79 | | File | File object of the image (if it is being uploaded through the browser. |
80 | | ImageType | Type of the image (default is `image/jpeg`). |
81 | | AspectRatios | Array of aspect ratios. Available options: `0:0`, `1:1` , `2:3` ,`4:3`, `16:9`l . (default is `0:0`) |
82 |
83 |
84 | ### Example
85 |
86 | ```typescript
87 |
88 | @Component({
89 | selector: 'app-root',
90 | styleUrls: ['./app.component.css']
91 | template: `
92 |
96 |
97 |
98 | `,
99 |
100 | })
101 | export class AppComponent {
102 | public config = {
103 | ImageName: 'Some image',
104 | AspectRatios: ["4:3", "16:9"],
105 | ImageUrl: 'https://static.pexels.com/photos/248797/pexels-photo-248797.jpeg',
106 | ImageType: 'image/jpeg'
107 | }
108 |
109 | public close() {
110 | // Fired when the editor is closed.
111 | }
112 |
113 | public getEditedFile(file: File) {
114 | // Fired when the file has been processed.
115 | }
116 | }
117 |
118 |
119 | ```
120 |
121 |
122 | ### Contributors
123 |
124 | | [](https://github.com/hggeorgiev) | [](https://github.com/taulantdisha) |
125 | |---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
126 | | [Hristo Georgiev](https://github.com/hggeorgiev) | [Taulant Disha](https://github.com/taulantdisha) |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/assets/editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hggeorgiev/ngx-image-editor/b9b49316ec0f621d356b3bba5ac164487fe4578d/assets/editor.png
--------------------------------------------------------------------------------
/dist.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hggeorgiev/ngx-image-editor/b9b49316ec0f621d356b3bba5ac164487fe4578d/dist.tgz
--------------------------------------------------------------------------------
/dist/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Centroida
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
ngx-image-editor
8 |
9 |
10 |
11 | Awesome editor for Angular 6 based on [Angular Material](https://github.com/angular/material2)
12 |
13 | [](https://badge.fury.io/js/ngx-image-editor)
14 | [](https://travis-ci.org/hggeorgiev/ngx-image-editor)
15 |
16 | **[Live Demo on Slackblitz](https://stackblitz.com/edit/ngx-image-editor-demo)**
17 |
18 | ## Getting started
19 |
20 | ##### Step 1: Install Angular Material (+ Material Icons) and Angular Flex Layout
21 |
22 | - [Angular Material](https://material.angular.io/guide/getting-started)
23 | - [Angular Flex-Layout](https://github.com/angular/flex-layout)
24 |
25 | ##### Step 2: Install cropperjs
26 |
27 | ```bash
28 | npm install --save cropperjs
29 | ```
30 | ##### Step 3: Add `cropperjs` file paths in your `.angular.json`
31 |
32 | ```json
33 | }
34 | "styles": [
35 | "node_modules/cropperjs/dist/cropper.css"
36 | ],
37 | "scripts": [
38 | "node_modules/cropperjs/dist/cropper.js"
39 | ]
40 | }
41 | ```
42 |
43 |
44 | ##### Step 4: Install `ngx-image-editor`:
45 | ```bash
46 | npm install --save ngx-image-editor
47 | ```
48 |
49 | ##### Step 5: Import the `NgxImageEditorModule` within your app:
50 | ```js
51 | import {NgxImageEditorModule} from "ngx-image-editor";
52 |
53 | @NgModule({
54 | imports: [
55 | NgxImageEditorModule
56 | ]
57 | })
58 | ```
59 |
60 |
61 | ##### Step 6: Use within your application:
62 | ```html
63 |
64 | ```
65 |
66 | ### API
67 |
68 | | Property | Description |
69 | | -------------- | -------------------------------------------------------------- |
70 | | `[config]` | An object containing editor configuration (see **Configuration**) |
71 | | `(file)` | The emitted file after editing. |
72 |
73 |
74 | #### Configuration
75 | | Property | Description |
76 | | -------------- | -------------------------------------------------------------- |
77 | | ImageName | Name of the image. |
78 | | ImageUrl | URL of the image (if it coming from a CDN) . |
79 | | File | File object of the image (if it is being uploaded through the browser. |
80 | | ImageType | Type of the image (default is `image/jpeg`). |
81 | | AspectRatios | Array of aspect ratios. Available options: `0:0`, `1:1` , `2:3` ,`4:3`, `16:9`l . (default is `0:0`) |
82 |
83 |
84 | ### Example
85 |
86 | ```typescript
87 |
88 | @Component({
89 | selector: 'app-root',
90 | styleUrls: ['./app.component.css']
91 | template: `
92 |
96 |
97 |
98 | `,
99 |
100 | })
101 | export class AppComponent {
102 | public config = {
103 | ImageName: 'Some image',
104 | AspectRatios: ["4:3", "16:9"],
105 | ImageUrl: 'https://static.pexels.com/photos/248797/pexels-photo-248797.jpeg',
106 | ImageType: 'image/jpeg'
107 | }
108 |
109 | public close() {
110 | // Fired when the editor is closed.
111 | }
112 |
113 | public getEditedFile(file: File) {
114 | // Fired when the file has been processed.
115 | }
116 | }
117 |
118 |
119 | ```
120 |
121 |
122 | ### Contributors
123 |
124 | | [](https://github.com/hggeorgiev) | [](https://github.com/taulantdisha) |
125 | |---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
126 | | [Hristo Georgiev](https://github.com/hggeorgiev) | [Taulant Disha](https://github.com/taulantdisha) |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/dist/bundles/ngx-image-editor.umd.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/flex-layout'), require('@angular/material'), require('@angular/forms'), require('@angular/common'), require('@angular/platform-browser/animations')) :
3 | typeof define === 'function' && define.amd ? define('ngx-image-editor', ['exports', '@angular/core', '@angular/flex-layout', '@angular/material', '@angular/forms', '@angular/common', '@angular/platform-browser/animations'], factory) :
4 | (factory((global['ngx-image-editor'] = {}),global.ng.core,global.ng['flex-layout'],global.ng.material,global.ng.forms,global.ng.common,global.ng.platformBrowser.animations));
5 | }(this, (function (exports,core,flexLayout,material,forms,common,animations) { 'use strict';
6 |
7 | var NgxImageEditorComponent = /** @class */ (function () {
8 | function NgxImageEditorComponent() {
9 | this.file = new core.EventEmitter();
10 | this.zoomIn = 0;
11 | this.sliderValue = 0;
12 | this.loading = true;
13 | this.canvasFillColor = '#fff';
14 | this.state = new EditorOptions();
15 | }
16 | Object.defineProperty(NgxImageEditorComponent.prototype, "config", {
17 | set: function (config) {
18 | this.state = config;
19 | },
20 | enumerable: true,
21 | configurable: true
22 | });
23 | NgxImageEditorComponent.prototype.ngOnInit = function () {
24 | this.handleStateConfig();
25 | };
26 | NgxImageEditorComponent.prototype.ngOnDestroy = function () {
27 | this.cropper.destroy();
28 | };
29 | NgxImageEditorComponent.prototype.ngAfterViewInit = function () {
30 | if (!this.state.File && this.state.ImageUrl) {
31 | this.initializeCropper();
32 | }
33 | };
34 | NgxImageEditorComponent.prototype.handleStateConfig = function () {
35 | this.state.ImageType = this.state.ImageType ? this.state.ImageType : 'image/jpeg';
36 | if (this.state.ImageUrl) {
37 | this.state.File = null;
38 | this.previewImageURL = this.state.ImageUrl;
39 | }
40 | if (this.state.File) {
41 | this.state.ImageUrl = null;
42 | this.convertFileToBase64(this.state.File);
43 | }
44 | if (this.state.AspectRatios) {
45 | this.addRatios(this.state.AspectRatios);
46 | }
47 | else {
48 | this.ratios = NGX_DEFAULT_RATIOS;
49 | }
50 | if (!this.state.ImageUrl && !this.state.File) {
51 | console.error("Property ImageUrl or File is missing, Please provide an url or file in the config options.");
52 | }
53 | if (!this.state.ImageName) {
54 | console.error("Property ImageName is missing, Please provide a name for the image.");
55 | }
56 | };
57 | NgxImageEditorComponent.prototype.convertFileToBase64 = function (file) {
58 | var _this = this;
59 | var reader = new FileReader();
60 | reader.addEventListener("load", function (e) {
61 | _this.previewImageURL = e.target["result"];
62 | }, false);
63 | reader.readAsDataURL(file);
64 | reader.onloadend = (function () {
65 | _this.initializeCropper();
66 | });
67 | };
68 | NgxImageEditorComponent.prototype.addRatios = function (ratios) {
69 | var _this = this;
70 | this.ratios = [];
71 | ratios.forEach(function (ratioType) {
72 | var addedRation = NGX_DEFAULT_RATIOS.find(function (ratio) {
73 | return ratio.text === ratioType;
74 | });
75 | _this.ratios.push(addedRation);
76 | });
77 | };
78 | NgxImageEditorComponent.prototype.handleCrop = function () {
79 | var _this = this;
80 | this.loading = true;
81 | setTimeout(function () {
82 | _this.croppedImage = _this.cropper.getCroppedCanvas({ fillColor: _this.canvasFillColor })
83 | .toDataURL(_this.state.ImageType);
84 | setTimeout(function () {
85 | _this.imageWidth = _this.croppedImg.nativeElement.width;
86 | _this.imageHeight = _this.croppedImg.nativeElement.height;
87 | });
88 | _this.cropper.getCroppedCanvas({ fillColor: _this.canvasFillColor }).toBlob(function (blob) {
89 | _this.blob = blob;
90 | });
91 | _this.zoomIn = 1;
92 | _this.loading = false;
93 | }, 2000);
94 | };
95 | NgxImageEditorComponent.prototype.undoCrop = function () {
96 | var _this = this;
97 | this.croppedImage = null;
98 | this.blob = null;
99 | setTimeout(function () {
100 | _this.initializeCropper();
101 | }, 100);
102 | };
103 | NgxImageEditorComponent.prototype.saveImage = function () {
104 | this.file.emit(new File([this.blob], this.state.ImageName, { type: this.state.ImageType }));
105 | };
106 | NgxImageEditorComponent.prototype.initializeCropper = function () {
107 | var _this = this;
108 | this.cropper = new Cropper(this.previewImage.nativeElement, {
109 | zoomOnWheel: true,
110 | viewMode: 0,
111 | center: true,
112 | ready: function () { return _this.loading = false; },
113 | dragMode: 'move',
114 | crop: function (e) {
115 | _this.imageHeight = Math.round(e.detail.height);
116 | _this.imageWidth = Math.round(e.detail.width);
117 | _this.cropBoxWidth = Math.round(_this.cropper.getCropBoxData().width);
118 | _this.cropBoxHeight = Math.round(_this.cropper.getCropBoxData().height);
119 | _this.canvasWidth = Math.round(_this.cropper.getCanvasData().width);
120 | _this.canvasHeight = Math.round(_this.cropper.getCanvasData().height);
121 | }
122 | });
123 | this.setRatio(this.ratios[0].value);
124 | };
125 | NgxImageEditorComponent.prototype.setRatio = function (value) {
126 | this.cropper.setAspectRatio(value);
127 | };
128 | NgxImageEditorComponent.prototype.zoomChange = function (input, zoom) {
129 | if (this.croppedImage) {
130 | if (zoom) {
131 | zoom === 'zoomIn' ? this.zoomIn += 0.1 : this.zoomIn -= 0.1;
132 | }
133 | else {
134 | if (input < this.sliderValue) {
135 | this.zoomIn = -Math.abs(input / 100);
136 | }
137 | else {
138 | this.zoomIn = Math.abs(input / 100);
139 | }
140 | }
141 | if (this.zoomIn <= 0.1) {
142 | this.zoomIn = 0.1;
143 | }
144 | }
145 | else {
146 | if (zoom) {
147 | this.cropper.zoom(input);
148 | this.zoomIn = input;
149 | }
150 | else {
151 | if (input < this.sliderValue) {
152 | this.cropper.zoom(-Math.abs(input / 100));
153 | }
154 | else {
155 | this.cropper.zoom(Math.abs(input / 100));
156 | }
157 | if (input === 0) {
158 | this.cropper.zoom(-1);
159 | }
160 | }
161 | }
162 | if (!zoom) {
163 | this.sliderValue = input;
164 | }
165 | else {
166 | input > 0 ? this.sliderValue += Math.abs(input * 100) : this.sliderValue -= Math.abs(input * 100);
167 | }
168 | if (this.sliderValue < 0) {
169 | this.sliderValue = 0;
170 | }
171 | };
172 | NgxImageEditorComponent.prototype.setImageWidth = function (canvasWidth) {
173 | if (canvasWidth) {
174 | this.cropper.setCanvasData({
175 | left: this.cropper.getCanvasData().left,
176 | top: this.cropper.getCanvasData().top,
177 | width: Math.round(canvasWidth),
178 | height: this.cropper.getCanvasData().height
179 | });
180 | }
181 | };
182 | NgxImageEditorComponent.prototype.setImageHeight = function (canvasHeight) {
183 | if (canvasHeight) {
184 | this.cropper.setCanvasData({
185 | left: this.cropper.getCanvasData().left,
186 | top: this.cropper.getCanvasData().top,
187 | width: this.cropper.getCanvasData().width,
188 | height: Math.round(canvasHeight)
189 | });
190 | }
191 | };
192 | NgxImageEditorComponent.prototype.setCropBoxWidth = function (cropBoxWidth) {
193 | if (cropBoxWidth) {
194 | this.cropper.setCropBoxData({
195 | left: this.cropper.getCropBoxData().left,
196 | top: this.cropper.getCropBoxData().top,
197 | width: Math.round(cropBoxWidth),
198 | height: this.cropper.getCropBoxData().height
199 | });
200 | }
201 | };
202 | NgxImageEditorComponent.prototype.setCropBoxHeight = function (cropBoxHeight) {
203 | if (cropBoxHeight) {
204 | this.cropper.setCropBoxData({
205 | left: this.cropper.getCropBoxData().left,
206 | top: this.cropper.getCropBoxData().top,
207 | width: this.cropper.getCropBoxData().width,
208 | height: Math.round(cropBoxHeight)
209 | });
210 | }
211 | };
212 | NgxImageEditorComponent.prototype.centerCanvas = function () {
213 | var cropBoxLeft = (this.cropper.getContainerData().width - this.cropper.getCropBoxData().width) / 2;
214 | var cropBoxTop = (this.cropper.getContainerData().height - this.cropper.getCropBoxData().height) / 2;
215 | var canvasLeft = (this.cropper.getContainerData().width - this.cropper.getCanvasData().width) / 2;
216 | var canvasTop = (this.cropper.getContainerData().height - this.cropper.getCanvasData().height) / 2;
217 | this.cropper.setCropBoxData({
218 | left: cropBoxLeft,
219 | top: cropBoxTop,
220 | width: this.cropper.getCropBoxData().width,
221 | height: this.cropper.getCropBoxData().height
222 | });
223 | this.cropper.setCanvasData({
224 | left: canvasLeft,
225 | top: canvasTop,
226 | width: this.cropper.getCanvasData().width,
227 | height: this.cropper.getCanvasData().height
228 | });
229 | };
230 | return NgxImageEditorComponent;
231 | }());
232 | NgxImageEditorComponent.decorators = [
233 | { type: core.Component, args: [{
234 | selector: 'ngx-image-editor',
235 | template: "\n \n \n\n
\n
\n \n
![]()
\n
\n \n
\n \n
![]()
\n
\n \n
\n
\n\n
\n\n
\n\n ",
236 | styles: ["\n\n .ngx-image-editor-component .photo-editor-header {\n display: flex;\n justify-content: space-around;\n align-items: center;\n width: 100%;\n padding: 5px 0;\n z-index: 100;\n margin: 0;\n }\n\n .ngx-image-editor-component .photo-editor-header > .mat-icon {\n padding: 0 10px;\n }\n\n .ngx-image-editor-component .photo-editor-header > .file-name {\n flex: 1 1 100%;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .ngx-image-editor-component mat-progress-spinner {\n position: absolute;\n }\n\n .ngx-image-editor-component .dialog-crop-container {\n width: 800px;\n height: 400px;\n overflow: hidden;\n }\n\n .ngx-image-editor-component .cropper-bg {\n background-image: none !important;\n }\n\n .ngx-image-editor-component .cropper-bg > .cropper-modal {\n opacity: 1 !important;\n background: none;\n }\n\n .ngx-image-editor-component .img-container {\n width: 800px !important;\n height: 400px !important;\n }\n\n .ngx-image-editor-component .cropped-image img {\n width: auto !important;\n height: auto !important;\n max-width: 800px !important;\n max-height: 400px !important;\n }\n\n .ngx-image-editor-component .dialog-button-actions {\n position: relative;\n padding: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions:last-child {\n margin: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {\n margin: 20px;\n }\n\n .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config {\n padding: 5px;\n margin: 0 20px;\n }\n\n \n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {\n width: 200px !important;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {\n display: flex;\n align-items: center;\n padding: 0 10px;\n }\n \n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-horizontal .mat-slider-wrapper .mat-slider-thumb-container {\n cursor: grab;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {\n padding: 0 10px;\n font-size: 14px;\n width: 200px;\n max-width: 200px;\n }\n\n \n\n\n\n\n\n\n\n\n\n\n "],
237 | encapsulation: core.ViewEncapsulation.None
238 | },] },
239 | ];
240 | NgxImageEditorComponent.ctorParameters = function () { return []; };
241 | NgxImageEditorComponent.propDecorators = {
242 | "previewImage": [{ type: core.ViewChild, args: ['previewimg',] },],
243 | "croppedImg": [{ type: core.ViewChild, args: ['croppedImg',] },],
244 | "config": [{ type: core.Input },],
245 | "file": [{ type: core.Output },],
246 | };
247 | var EditorOptions = /** @class */ (function () {
248 | function EditorOptions() {
249 | }
250 | return EditorOptions;
251 | }());
252 | var NGX_DEFAULT_RATIOS = [
253 | {
254 | value: 16 / 9, text: '16:9'
255 | },
256 | {
257 | value: 4 / 3, text: '4:3'
258 | },
259 | {
260 | value: 1 / 1, text: '1:1'
261 | },
262 | {
263 | value: 2 / 3, text: '2:3'
264 | },
265 | {
266 | value: 0 / 0, text: 'Default'
267 | }
268 | ];
269 | var NgxImageEditorModule = /** @class */ (function () {
270 | function NgxImageEditorModule() {
271 | }
272 | NgxImageEditorModule.forRoot = function () {
273 | return {
274 | ngModule: NgxImageEditorModule,
275 | };
276 | };
277 | return NgxImageEditorModule;
278 | }());
279 | NgxImageEditorModule.decorators = [
280 | { type: core.NgModule, args: [{
281 | imports: [
282 | forms.FormsModule,
283 | animations.BrowserAnimationsModule,
284 | common.CommonModule,
285 | forms.ReactiveFormsModule,
286 | flexLayout.FlexLayoutModule,
287 | material.MatButtonModule,
288 | material.MatIconModule,
289 | material.MatDialogModule,
290 | material.MatInputModule,
291 | material.MatMenuModule,
292 | material.MatProgressSpinnerModule,
293 | material.MatTabsModule,
294 | material.MatTooltipModule,
295 | material.MatButtonToggleModule,
296 | material.MatSliderModule,
297 | material.MatAutocompleteModule
298 | ],
299 | declarations: [
300 | NgxImageEditorComponent
301 | ],
302 | exports: [NgxImageEditorComponent]
303 | },] },
304 | ];
305 |
306 | exports.NgxImageEditorModule = NgxImageEditorModule;
307 | exports.NgxImageEditorComponent = NgxImageEditorComponent;
308 | exports.EditorOptions = EditorOptions;
309 | exports.NGX_DEFAULT_RATIOS = NGX_DEFAULT_RATIOS;
310 |
311 | Object.defineProperty(exports, '__esModule', { value: true });
312 |
313 | })));
314 | //# sourceMappingURL=ngx-image-editor.umd.js.map
315 |
--------------------------------------------------------------------------------
/dist/bundles/ngx-image-editor.umd.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@angular/core"),require("@angular/flex-layout"),require("@angular/material"),require("@angular/forms"),require("@angular/common"),require("@angular/platform-browser/animations")):"function"==typeof define&&define.amd?define("ngx-image-editor",["exports","@angular/core","@angular/flex-layout","@angular/material","@angular/forms","@angular/common","@angular/platform-browser/animations"],e):e(t["ngx-image-editor"]={},t.ng.core,t.ng["flex-layout"],t.ng.material,t.ng.forms,t.ng.common,t.ng.platformBrowser.animations)}(this,function(t,e,n,o,a,i,r){"use strict";var p=function(){function t(){this.file=new e.EventEmitter,this.zoomIn=0,this.sliderValue=0,this.loading=!0,this.canvasFillColor="#fff",this.state=new s}return Object.defineProperty(t.prototype,"config",{set:function(t){this.state=t},enumerable:!0,configurable:!0}),t.prototype.ngOnInit=function(){this.handleStateConfig()},t.prototype.ngOnDestroy=function(){this.cropper.destroy()},t.prototype.ngAfterViewInit=function(){!this.state.File&&this.state.ImageUrl&&this.initializeCropper()},t.prototype.handleStateConfig=function(){this.state.ImageType=this.state.ImageType?this.state.ImageType:"image/jpeg",this.state.ImageUrl&&(this.state.File=null,this.previewImageURL=this.state.ImageUrl),this.state.File&&(this.state.ImageUrl=null,this.convertFileToBase64(this.state.File)),this.state.AspectRatios?this.addRatios(this.state.AspectRatios):this.ratios=l,this.state.ImageUrl||this.state.File||console.error("Property ImageUrl or File is missing, Please provide an url or file in the config options."),this.state.ImageName||console.error("Property ImageName is missing, Please provide a name for the image.")},t.prototype.convertFileToBase64=function(t){var e=this,n=new FileReader;n.addEventListener("load",function(t){e.previewImageURL=t.target.result},!1),n.readAsDataURL(t),n.onloadend=function(){e.initializeCropper()}},t.prototype.addRatios=function(t){var n=this;this.ratios=[],t.forEach(function(e){var t=l.find(function(t){return t.text===e});n.ratios.push(t)})},t.prototype.handleCrop=function(){var e=this;this.loading=!0,setTimeout(function(){e.croppedImage=e.cropper.getCroppedCanvas({fillColor:e.canvasFillColor}).toDataURL(e.state.ImageType),setTimeout(function(){e.imageWidth=e.croppedImg.nativeElement.width,e.imageHeight=e.croppedImg.nativeElement.height}),e.cropper.getCroppedCanvas({fillColor:e.canvasFillColor}).toBlob(function(t){e.blob=t}),e.zoomIn=1,e.loading=!1},2e3)},t.prototype.undoCrop=function(){var t=this;this.croppedImage=null,this.blob=null,setTimeout(function(){t.initializeCropper()},100)},t.prototype.saveImage=function(){this.file.emit(new File([this.blob],this.state.ImageName,{type:this.state.ImageType}))},t.prototype.initializeCropper=function(){var e=this;this.cropper=new Cropper(this.previewImage.nativeElement,{zoomOnWheel:!0,viewMode:0,center:!0,ready:function(){return e.loading=!1},dragMode:"move",crop:function(t){e.imageHeight=Math.round(t.detail.height),e.imageWidth=Math.round(t.detail.width),e.cropBoxWidth=Math.round(e.cropper.getCropBoxData().width),e.cropBoxHeight=Math.round(e.cropper.getCropBoxData().height),e.canvasWidth=Math.round(e.cropper.getCanvasData().width),e.canvasHeight=Math.round(e.cropper.getCanvasData().height)}}),this.setRatio(this.ratios[0].value)},t.prototype.setRatio=function(t){this.cropper.setAspectRatio(t)},t.prototype.zoomChange=function(t,e){this.croppedImage?(e?"zoomIn"===e?this.zoomIn+=.1:this.zoomIn-=.1:t\n \n\n \n
\n \n
![]()
\n
\n \n
\n \n
![]()
\n
\n \n
\n
\n\n \n\n \n\n ',styles:["\n\n .ngx-image-editor-component .photo-editor-header {\n display: flex;\n justify-content: space-around;\n align-items: center;\n width: 100%;\n padding: 5px 0;\n z-index: 100;\n margin: 0;\n }\n\n .ngx-image-editor-component .photo-editor-header > .mat-icon {\n padding: 0 10px;\n }\n\n .ngx-image-editor-component .photo-editor-header > .file-name {\n flex: 1 1 100%;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .ngx-image-editor-component mat-progress-spinner {\n position: absolute;\n }\n\n .ngx-image-editor-component .dialog-crop-container {\n width: 800px;\n height: 400px;\n overflow: hidden;\n }\n\n .ngx-image-editor-component .cropper-bg {\n background-image: none !important;\n }\n\n .ngx-image-editor-component .cropper-bg > .cropper-modal {\n opacity: 1 !important;\n background: none;\n }\n\n .ngx-image-editor-component .img-container {\n width: 800px !important;\n height: 400px !important;\n }\n\n .ngx-image-editor-component .cropped-image img {\n width: auto !important;\n height: auto !important;\n max-width: 800px !important;\n max-height: 400px !important;\n }\n\n .ngx-image-editor-component .dialog-button-actions {\n position: relative;\n padding: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions:last-child {\n margin: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {\n margin: 20px;\n }\n\n .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config {\n padding: 5px;\n margin: 0 20px;\n }\n\n \n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {\n width: 200px !important;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {\n display: flex;\n align-items: center;\n padding: 0 10px;\n }\n \n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-horizontal .mat-slider-wrapper .mat-slider-thumb-container {\n cursor: grab;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {\n padding: 0 10px;\n font-size: 14px;\n width: 200px;\n max-width: 200px;\n }\n\n \n\n\n\n\n\n\n\n\n\n\n "],encapsulation:e.ViewEncapsulation.None}]}],p.ctorParameters=function(){return[]},p.propDecorators={previewImage:[{type:e.ViewChild,args:["previewimg"]}],croppedImg:[{type:e.ViewChild,args:["croppedImg"]}],config:[{type:e.Input}],file:[{type:e.Output}]};var s=function(){},l=[{value:16/9,text:"16:9"},{value:4/3,text:"4:3"},{value:1,text:"1:1"},{value:2/3,text:"2:3"},{value:NaN,text:"Default"}],g=function(){function t(){}return t.forRoot=function(){return{ngModule:t}},t}();g.decorators=[{type:e.NgModule,args:[{imports:[a.FormsModule,r.BrowserAnimationsModule,i.CommonModule,a.ReactiveFormsModule,n.FlexLayoutModule,o.MatButtonModule,o.MatIconModule,o.MatDialogModule,o.MatInputModule,o.MatMenuModule,o.MatProgressSpinnerModule,o.MatTabsModule,o.MatTooltipModule,o.MatButtonToggleModule,o.MatSliderModule,o.MatAutocompleteModule],declarations:[p],exports:[p]}]}],t.NgxImageEditorModule=g,t.NgxImageEditorComponent=p,t.EditorOptions=s,t.NGX_DEFAULT_RATIOS=l,Object.defineProperty(t,"__esModule",{value:!0})});
2 | //# sourceMappingURL=ngx-image-editor.umd.min.js.map
3 |
--------------------------------------------------------------------------------
/dist/esm2015/ngx-image-editor.js:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, NgModule } from '@angular/core';
2 | import { FlexLayoutModule } from '@angular/flex-layout';
3 | import { MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, MatIconModule, MatInputModule, MatMenuModule, MatProgressSpinnerModule, MatSliderModule, MatDialogModule, MatTabsModule, MatTooltipModule } from '@angular/material';
4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
5 | import { CommonModule } from '@angular/common';
6 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
7 |
8 | /**
9 | * @fileoverview added by tsickle
10 | * @suppress {checkTypes} checked by tsc
11 | */
12 | class NgxImageEditorComponent {
13 | constructor() {
14 | this.file = new EventEmitter();
15 | this.zoomIn = 0;
16 | this.sliderValue = 0;
17 | this.loading = true;
18 | this.canvasFillColor = '#fff';
19 | this.state = new EditorOptions();
20 | }
21 | /**
22 | * @param {?} config
23 | * @return {?}
24 | */
25 | set config(config) {
26 | this.state = config;
27 | }
28 | /**
29 | * @return {?}
30 | */
31 | ngOnInit() {
32 | this.handleStateConfig();
33 | }
34 | /**
35 | * @return {?}
36 | */
37 | ngOnDestroy() {
38 | this.cropper.destroy();
39 | }
40 | /**
41 | * @return {?}
42 | */
43 | ngAfterViewInit() {
44 | // NOTE if we don't have a file meaning that loading the image will happen synchronously we can safely
45 | // call initializeCropper in ngAfterViewInit. otherwise if we are using the FileReader to load a base64 image
46 | // we need to call onloadend asynchronously..
47 | if (!this.state.File && this.state.ImageUrl) {
48 | this.initializeCropper();
49 | }
50 | }
51 | /**
52 | * @return {?}
53 | */
54 | handleStateConfig() {
55 | this.state.ImageType = this.state.ImageType ? this.state.ImageType : 'image/jpeg';
56 | if (this.state.ImageUrl) {
57 | this.state.File = null;
58 | this.previewImageURL = this.state.ImageUrl;
59 | }
60 | if (this.state.File) {
61 | this.state.ImageUrl = null;
62 | this.convertFileToBase64(this.state.File);
63 | }
64 | if (this.state.AspectRatios) {
65 | this.addRatios(this.state.AspectRatios);
66 | }
67 | else {
68 | this.ratios = NGX_DEFAULT_RATIOS;
69 | }
70 | if (!this.state.ImageUrl && !this.state.File) {
71 | console.error("Property ImageUrl or File is missing, Please provide an url or file in the config options.");
72 | }
73 | if (!this.state.ImageName) {
74 | console.error("Property ImageName is missing, Please provide a name for the image.");
75 | }
76 | }
77 | /**
78 | * @param {?} file
79 | * @return {?}
80 | */
81 | convertFileToBase64(file) {
82 | const /** @type {?} */ reader = new FileReader();
83 | reader.addEventListener("load", (e) => {
84 | this.previewImageURL = e.target["result"];
85 | }, false);
86 | reader.readAsDataURL(file);
87 | reader.onloadend = (() => {
88 | // NOTE since getting the base64 image url happens asynchronously we need to initializeCropper after
89 | // the image has been loaded.
90 | this.initializeCropper();
91 | });
92 | }
93 | /**
94 | * @param {?} ratios
95 | * @return {?}
96 | */
97 | addRatios(ratios) {
98 | this.ratios = [];
99 | ratios.forEach((ratioType) => {
100 | const /** @type {?} */ addedRation = NGX_DEFAULT_RATIOS.find((ratio) => {
101 | return ratio.text === ratioType;
102 | });
103 | this.ratios.push(addedRation);
104 | });
105 | }
106 | /**
107 | * @return {?}
108 | */
109 | handleCrop() {
110 | this.loading = true;
111 | setTimeout(() => {
112 | this.croppedImage = this.cropper.getCroppedCanvas({ fillColor: this.canvasFillColor })
113 | .toDataURL(this.state.ImageType);
114 | setTimeout(() => {
115 | this.imageWidth = this.croppedImg.nativeElement.width;
116 | this.imageHeight = this.croppedImg.nativeElement.height;
117 | });
118 | this.cropper.getCroppedCanvas({ fillColor: this.canvasFillColor }).toBlob((blob) => {
119 | this.blob = blob;
120 | });
121 | this.zoomIn = 1;
122 | this.loading = false;
123 | }, 2000);
124 | }
125 | /**
126 | * @return {?}
127 | */
128 | undoCrop() {
129 | this.croppedImage = null;
130 | this.blob = null;
131 | setTimeout(() => {
132 | this.initializeCropper();
133 | }, 100);
134 | }
135 | /**
136 | * @return {?}
137 | */
138 | saveImage() {
139 | this.file.emit(new File([this.blob], this.state.ImageName, { type: this.state.ImageType }));
140 | }
141 | /**
142 | * @return {?}
143 | */
144 | initializeCropper() {
145 | this.cropper = new Cropper(this.previewImage.nativeElement, {
146 | zoomOnWheel: true,
147 | viewMode: 0,
148 | center: true,
149 | ready: () => this.loading = false,
150 | dragMode: 'move',
151 | crop: (e) => {
152 | this.imageHeight = Math.round(e.detail.height);
153 | this.imageWidth = Math.round(e.detail.width);
154 | this.cropBoxWidth = Math.round(this.cropper.getCropBoxData().width);
155 | this.cropBoxHeight = Math.round(this.cropper.getCropBoxData().height);
156 | this.canvasWidth = Math.round(this.cropper.getCanvasData().width);
157 | this.canvasHeight = Math.round(this.cropper.getCanvasData().height);
158 | }
159 | });
160 | this.setRatio(this.ratios[0].value);
161 | }
162 | /**
163 | * @param {?} value
164 | * @return {?}
165 | */
166 | setRatio(value) {
167 | this.cropper.setAspectRatio(value);
168 | }
169 | /**
170 | * @param {?} input
171 | * @param {?=} zoom
172 | * @return {?}
173 | */
174 | zoomChange(input, zoom) {
175 | if (this.croppedImage) {
176 | if (zoom) {
177 | zoom === 'zoomIn' ? this.zoomIn += 0.1 : this.zoomIn -= 0.1;
178 | }
179 | else {
180 | if (input < this.sliderValue) {
181 | this.zoomIn = -Math.abs(input / 100);
182 | }
183 | else {
184 | this.zoomIn = Math.abs(input / 100);
185 | }
186 | }
187 | if (this.zoomIn <= 0.1) {
188 | this.zoomIn = 0.1;
189 | }
190 | }
191 | else {
192 | if (zoom) {
193 | this.cropper.zoom(input);
194 | this.zoomIn = input;
195 | }
196 | else {
197 | if (input < this.sliderValue) {
198 | this.cropper.zoom(-Math.abs(input / 100));
199 | }
200 | else {
201 | this.cropper.zoom(Math.abs(input / 100));
202 | }
203 | if (input === 0) {
204 | this.cropper.zoom(-1);
205 | }
206 | }
207 | }
208 | if (!zoom) {
209 | this.sliderValue = input;
210 | }
211 | else {
212 | input > 0 ? this.sliderValue += Math.abs(input * 100) : this.sliderValue -= Math.abs(input * 100);
213 | }
214 | if (this.sliderValue < 0) {
215 | this.sliderValue = 0;
216 | }
217 | }
218 | /**
219 | * @param {?} canvasWidth
220 | * @return {?}
221 | */
222 | setImageWidth(canvasWidth) {
223 | if (canvasWidth) {
224 | this.cropper.setCanvasData({
225 | left: this.cropper.getCanvasData().left,
226 | top: this.cropper.getCanvasData().top,
227 | width: Math.round(canvasWidth),
228 | height: this.cropper.getCanvasData().height
229 | });
230 | }
231 | }
232 | /**
233 | * @param {?} canvasHeight
234 | * @return {?}
235 | */
236 | setImageHeight(canvasHeight) {
237 | if (canvasHeight) {
238 | this.cropper.setCanvasData({
239 | left: this.cropper.getCanvasData().left,
240 | top: this.cropper.getCanvasData().top,
241 | width: this.cropper.getCanvasData().width,
242 | height: Math.round(canvasHeight)
243 | });
244 | }
245 | }
246 | /**
247 | * @param {?} cropBoxWidth
248 | * @return {?}
249 | */
250 | setCropBoxWidth(cropBoxWidth) {
251 | if (cropBoxWidth) {
252 | this.cropper.setCropBoxData({
253 | left: this.cropper.getCropBoxData().left,
254 | top: this.cropper.getCropBoxData().top,
255 | width: Math.round(cropBoxWidth),
256 | height: this.cropper.getCropBoxData().height
257 | });
258 | }
259 | }
260 | /**
261 | * @param {?} cropBoxHeight
262 | * @return {?}
263 | */
264 | setCropBoxHeight(cropBoxHeight) {
265 | if (cropBoxHeight) {
266 | this.cropper.setCropBoxData({
267 | left: this.cropper.getCropBoxData().left,
268 | top: this.cropper.getCropBoxData().top,
269 | width: this.cropper.getCropBoxData().width,
270 | height: Math.round(cropBoxHeight)
271 | });
272 | }
273 | }
274 | /**
275 | * @return {?}
276 | */
277 | centerCanvas() {
278 | const /** @type {?} */ cropBoxLeft = (this.cropper.getContainerData().width - this.cropper.getCropBoxData().width) / 2;
279 | const /** @type {?} */ cropBoxTop = (this.cropper.getContainerData().height - this.cropper.getCropBoxData().height) / 2;
280 | const /** @type {?} */ canvasLeft = (this.cropper.getContainerData().width - this.cropper.getCanvasData().width) / 2;
281 | const /** @type {?} */ canvasTop = (this.cropper.getContainerData().height - this.cropper.getCanvasData().height) / 2;
282 | this.cropper.setCropBoxData({
283 | left: cropBoxLeft,
284 | top: cropBoxTop,
285 | width: this.cropper.getCropBoxData().width,
286 | height: this.cropper.getCropBoxData().height
287 | });
288 | this.cropper.setCanvasData({
289 | left: canvasLeft,
290 | top: canvasTop,
291 | width: this.cropper.getCanvasData().width,
292 | height: this.cropper.getCanvasData().height
293 | });
294 | }
295 | }
296 | NgxImageEditorComponent.decorators = [
297 | { type: Component, args: [{
298 | selector: 'ngx-image-editor',
299 | template: `
300 |
301 |
316 |
317 |
323 |
324 |
328 |
![]()
330 |
331 |
332 |
333 |
334 |
![]()
337 |
338 |
339 |
340 |
341 |
342 |
446 |
447 |
448 |
449 | `,
450 | styles: [`
451 |
452 | .ngx-image-editor-component .photo-editor-header {
453 | display: flex;
454 | justify-content: space-around;
455 | align-items: center;
456 | width: 100%;
457 | padding: 5px 0;
458 | z-index: 100;
459 | margin: 0;
460 | }
461 |
462 | .ngx-image-editor-component .photo-editor-header > .mat-icon {
463 | padding: 0 10px;
464 | }
465 |
466 | .ngx-image-editor-component .photo-editor-header > .file-name {
467 | flex: 1 1 100%;
468 | text-overflow: ellipsis;
469 | white-space: nowrap;
470 | overflow: hidden;
471 | }
472 |
473 | .ngx-image-editor-component mat-progress-spinner {
474 | position: absolute;
475 | }
476 |
477 | .ngx-image-editor-component .dialog-crop-container {
478 | width: 800px;
479 | height: 400px;
480 | overflow: hidden;
481 | }
482 |
483 | .ngx-image-editor-component .cropper-bg {
484 | background-image: none !important;
485 | }
486 |
487 | .ngx-image-editor-component .cropper-bg > .cropper-modal {
488 | opacity: 1 !important;
489 | background: none;
490 | }
491 |
492 | .ngx-image-editor-component .img-container {
493 | width: 800px !important;
494 | height: 400px !important;
495 | }
496 |
497 | .ngx-image-editor-component .cropped-image img {
498 | width: auto !important;
499 | height: auto !important;
500 | max-width: 800px !important;
501 | max-height: 400px !important;
502 | }
503 |
504 | .ngx-image-editor-component .dialog-button-actions {
505 | position: relative;
506 | padding: 0;
507 | }
508 |
509 | .ngx-image-editor-component .dialog-button-actions:last-child {
510 | margin: 0;
511 | }
512 |
513 | .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {
514 | margin: 20px;
515 | }
516 |
517 | .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {
518 | position: absolute;
519 | top: 50%;
520 | left: 50%;
521 | transform: translate(-50%, -50%);
522 | }
523 |
524 | .ngx-image-editor-component .dialog-button-actions > .canvas-config {
525 | padding: 5px;
526 | margin: 0 20px;
527 | }
528 |
529 |
530 |
531 | .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {
532 | width: 200px !important;
533 | }
534 |
535 |
536 | .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {
537 | display: flex;
538 | align-items: center;
539 | padding: 0 10px;
540 | }
541 |
542 | .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-horizontal .mat-slider-wrapper .mat-slider-thumb-container {
543 | cursor: grab;
544 | }
545 |
546 |
547 | .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {
548 | padding: 0 10px;
549 | font-size: 14px;
550 | width: 200px;
551 | max-width: 200px;
552 | }
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 | `],
566 | encapsulation: ViewEncapsulation.None
567 | },] },
568 | ];
569 | /** @nocollapse */
570 | NgxImageEditorComponent.ctorParameters = () => [];
571 | NgxImageEditorComponent.propDecorators = {
572 | "previewImage": [{ type: ViewChild, args: ['previewimg',] },],
573 | "croppedImg": [{ type: ViewChild, args: ['croppedImg',] },],
574 | "config": [{ type: Input },],
575 | "file": [{ type: Output },],
576 | };
577 | /**
578 | * @record
579 | */
580 |
581 | class EditorOptions {
582 | }
583 | /**
584 | * @record
585 | */
586 |
587 | const NGX_DEFAULT_RATIOS = [
588 | {
589 | value: 16 / 9, text: '16:9'
590 | },
591 | {
592 | value: 4 / 3, text: '4:3'
593 | },
594 | {
595 | value: 1 / 1, text: '1:1'
596 | },
597 | {
598 | value: 2 / 3, text: '2:3'
599 | },
600 | {
601 | value: 0 / 0, text: 'Default'
602 | }
603 | ];
604 |
605 | /**
606 | * @fileoverview added by tsickle
607 | * @suppress {checkTypes} checked by tsc
608 | */
609 | class NgxImageEditorModule {
610 | /**
611 | * @return {?}
612 | */
613 | static forRoot() {
614 | return {
615 | ngModule: NgxImageEditorModule,
616 | };
617 | }
618 | }
619 | NgxImageEditorModule.decorators = [
620 | { type: NgModule, args: [{
621 | imports: [
622 | FormsModule,
623 | BrowserAnimationsModule,
624 | CommonModule,
625 | ReactiveFormsModule,
626 | FlexLayoutModule,
627 | MatButtonModule,
628 | MatIconModule,
629 | MatDialogModule,
630 | MatInputModule,
631 | MatMenuModule,
632 | MatProgressSpinnerModule,
633 | MatTabsModule,
634 | MatTooltipModule,
635 | MatButtonToggleModule,
636 | MatSliderModule,
637 | MatAutocompleteModule
638 | ],
639 | declarations: [
640 | NgxImageEditorComponent
641 | ],
642 | exports: [NgxImageEditorComponent]
643 | },] },
644 | ];
645 |
646 | /**
647 | * @fileoverview added by tsickle
648 | * @suppress {checkTypes} checked by tsc
649 | */
650 |
651 | /**
652 | * @fileoverview added by tsickle
653 | * @suppress {checkTypes} checked by tsc
654 | */
655 | /**
656 | * Generated bundle index. Do not edit.
657 | */
658 |
659 | export { NgxImageEditorModule, NgxImageEditorComponent, EditorOptions, NGX_DEFAULT_RATIOS };
660 | //# sourceMappingURL=ngx-image-editor.js.map
661 |
--------------------------------------------------------------------------------
/dist/esm5/ngx-image-editor.js:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, NgModule } from '@angular/core';
2 | import { FlexLayoutModule } from '@angular/flex-layout';
3 | import { MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, MatIconModule, MatInputModule, MatMenuModule, MatProgressSpinnerModule, MatSliderModule, MatDialogModule, MatTabsModule, MatTooltipModule } from '@angular/material';
4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
5 | import { CommonModule } from '@angular/common';
6 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
7 |
8 | var NgxImageEditorComponent = /** @class */ (function () {
9 | function NgxImageEditorComponent() {
10 | this.file = new EventEmitter();
11 | this.zoomIn = 0;
12 | this.sliderValue = 0;
13 | this.loading = true;
14 | this.canvasFillColor = '#fff';
15 | this.state = new EditorOptions();
16 | }
17 | Object.defineProperty(NgxImageEditorComponent.prototype, "config", {
18 | set: function (config) {
19 | this.state = config;
20 | },
21 | enumerable: true,
22 | configurable: true
23 | });
24 | NgxImageEditorComponent.prototype.ngOnInit = function () {
25 | this.handleStateConfig();
26 | };
27 | NgxImageEditorComponent.prototype.ngOnDestroy = function () {
28 | this.cropper.destroy();
29 | };
30 | NgxImageEditorComponent.prototype.ngAfterViewInit = function () {
31 | if (!this.state.File && this.state.ImageUrl) {
32 | this.initializeCropper();
33 | }
34 | };
35 | NgxImageEditorComponent.prototype.handleStateConfig = function () {
36 | this.state.ImageType = this.state.ImageType ? this.state.ImageType : 'image/jpeg';
37 | if (this.state.ImageUrl) {
38 | this.state.File = null;
39 | this.previewImageURL = this.state.ImageUrl;
40 | }
41 | if (this.state.File) {
42 | this.state.ImageUrl = null;
43 | this.convertFileToBase64(this.state.File);
44 | }
45 | if (this.state.AspectRatios) {
46 | this.addRatios(this.state.AspectRatios);
47 | }
48 | else {
49 | this.ratios = NGX_DEFAULT_RATIOS;
50 | }
51 | if (!this.state.ImageUrl && !this.state.File) {
52 | console.error("Property ImageUrl or File is missing, Please provide an url or file in the config options.");
53 | }
54 | if (!this.state.ImageName) {
55 | console.error("Property ImageName is missing, Please provide a name for the image.");
56 | }
57 | };
58 | NgxImageEditorComponent.prototype.convertFileToBase64 = function (file) {
59 | var _this = this;
60 | var reader = new FileReader();
61 | reader.addEventListener("load", function (e) {
62 | _this.previewImageURL = e.target["result"];
63 | }, false);
64 | reader.readAsDataURL(file);
65 | reader.onloadend = (function () {
66 | _this.initializeCropper();
67 | });
68 | };
69 | NgxImageEditorComponent.prototype.addRatios = function (ratios) {
70 | var _this = this;
71 | this.ratios = [];
72 | ratios.forEach(function (ratioType) {
73 | var addedRation = NGX_DEFAULT_RATIOS.find(function (ratio) {
74 | return ratio.text === ratioType;
75 | });
76 | _this.ratios.push(addedRation);
77 | });
78 | };
79 | NgxImageEditorComponent.prototype.handleCrop = function () {
80 | var _this = this;
81 | this.loading = true;
82 | setTimeout(function () {
83 | _this.croppedImage = _this.cropper.getCroppedCanvas({ fillColor: _this.canvasFillColor })
84 | .toDataURL(_this.state.ImageType);
85 | setTimeout(function () {
86 | _this.imageWidth = _this.croppedImg.nativeElement.width;
87 | _this.imageHeight = _this.croppedImg.nativeElement.height;
88 | });
89 | _this.cropper.getCroppedCanvas({ fillColor: _this.canvasFillColor }).toBlob(function (blob) {
90 | _this.blob = blob;
91 | });
92 | _this.zoomIn = 1;
93 | _this.loading = false;
94 | }, 2000);
95 | };
96 | NgxImageEditorComponent.prototype.undoCrop = function () {
97 | var _this = this;
98 | this.croppedImage = null;
99 | this.blob = null;
100 | setTimeout(function () {
101 | _this.initializeCropper();
102 | }, 100);
103 | };
104 | NgxImageEditorComponent.prototype.saveImage = function () {
105 | this.file.emit(new File([this.blob], this.state.ImageName, { type: this.state.ImageType }));
106 | };
107 | NgxImageEditorComponent.prototype.initializeCropper = function () {
108 | var _this = this;
109 | this.cropper = new Cropper(this.previewImage.nativeElement, {
110 | zoomOnWheel: true,
111 | viewMode: 0,
112 | center: true,
113 | ready: function () { return _this.loading = false; },
114 | dragMode: 'move',
115 | crop: function (e) {
116 | _this.imageHeight = Math.round(e.detail.height);
117 | _this.imageWidth = Math.round(e.detail.width);
118 | _this.cropBoxWidth = Math.round(_this.cropper.getCropBoxData().width);
119 | _this.cropBoxHeight = Math.round(_this.cropper.getCropBoxData().height);
120 | _this.canvasWidth = Math.round(_this.cropper.getCanvasData().width);
121 | _this.canvasHeight = Math.round(_this.cropper.getCanvasData().height);
122 | }
123 | });
124 | this.setRatio(this.ratios[0].value);
125 | };
126 | NgxImageEditorComponent.prototype.setRatio = function (value) {
127 | this.cropper.setAspectRatio(value);
128 | };
129 | NgxImageEditorComponent.prototype.zoomChange = function (input, zoom) {
130 | if (this.croppedImage) {
131 | if (zoom) {
132 | zoom === 'zoomIn' ? this.zoomIn += 0.1 : this.zoomIn -= 0.1;
133 | }
134 | else {
135 | if (input < this.sliderValue) {
136 | this.zoomIn = -Math.abs(input / 100);
137 | }
138 | else {
139 | this.zoomIn = Math.abs(input / 100);
140 | }
141 | }
142 | if (this.zoomIn <= 0.1) {
143 | this.zoomIn = 0.1;
144 | }
145 | }
146 | else {
147 | if (zoom) {
148 | this.cropper.zoom(input);
149 | this.zoomIn = input;
150 | }
151 | else {
152 | if (input < this.sliderValue) {
153 | this.cropper.zoom(-Math.abs(input / 100));
154 | }
155 | else {
156 | this.cropper.zoom(Math.abs(input / 100));
157 | }
158 | if (input === 0) {
159 | this.cropper.zoom(-1);
160 | }
161 | }
162 | }
163 | if (!zoom) {
164 | this.sliderValue = input;
165 | }
166 | else {
167 | input > 0 ? this.sliderValue += Math.abs(input * 100) : this.sliderValue -= Math.abs(input * 100);
168 | }
169 | if (this.sliderValue < 0) {
170 | this.sliderValue = 0;
171 | }
172 | };
173 | NgxImageEditorComponent.prototype.setImageWidth = function (canvasWidth) {
174 | if (canvasWidth) {
175 | this.cropper.setCanvasData({
176 | left: this.cropper.getCanvasData().left,
177 | top: this.cropper.getCanvasData().top,
178 | width: Math.round(canvasWidth),
179 | height: this.cropper.getCanvasData().height
180 | });
181 | }
182 | };
183 | NgxImageEditorComponent.prototype.setImageHeight = function (canvasHeight) {
184 | if (canvasHeight) {
185 | this.cropper.setCanvasData({
186 | left: this.cropper.getCanvasData().left,
187 | top: this.cropper.getCanvasData().top,
188 | width: this.cropper.getCanvasData().width,
189 | height: Math.round(canvasHeight)
190 | });
191 | }
192 | };
193 | NgxImageEditorComponent.prototype.setCropBoxWidth = function (cropBoxWidth) {
194 | if (cropBoxWidth) {
195 | this.cropper.setCropBoxData({
196 | left: this.cropper.getCropBoxData().left,
197 | top: this.cropper.getCropBoxData().top,
198 | width: Math.round(cropBoxWidth),
199 | height: this.cropper.getCropBoxData().height
200 | });
201 | }
202 | };
203 | NgxImageEditorComponent.prototype.setCropBoxHeight = function (cropBoxHeight) {
204 | if (cropBoxHeight) {
205 | this.cropper.setCropBoxData({
206 | left: this.cropper.getCropBoxData().left,
207 | top: this.cropper.getCropBoxData().top,
208 | width: this.cropper.getCropBoxData().width,
209 | height: Math.round(cropBoxHeight)
210 | });
211 | }
212 | };
213 | NgxImageEditorComponent.prototype.centerCanvas = function () {
214 | var cropBoxLeft = (this.cropper.getContainerData().width - this.cropper.getCropBoxData().width) / 2;
215 | var cropBoxTop = (this.cropper.getContainerData().height - this.cropper.getCropBoxData().height) / 2;
216 | var canvasLeft = (this.cropper.getContainerData().width - this.cropper.getCanvasData().width) / 2;
217 | var canvasTop = (this.cropper.getContainerData().height - this.cropper.getCanvasData().height) / 2;
218 | this.cropper.setCropBoxData({
219 | left: cropBoxLeft,
220 | top: cropBoxTop,
221 | width: this.cropper.getCropBoxData().width,
222 | height: this.cropper.getCropBoxData().height
223 | });
224 | this.cropper.setCanvasData({
225 | left: canvasLeft,
226 | top: canvasTop,
227 | width: this.cropper.getCanvasData().width,
228 | height: this.cropper.getCanvasData().height
229 | });
230 | };
231 | return NgxImageEditorComponent;
232 | }());
233 | NgxImageEditorComponent.decorators = [
234 | { type: Component, args: [{
235 | selector: 'ngx-image-editor',
236 | template: "\n \n \n\n
\n
\n \n
![]()
\n
\n \n
\n \n
![]()
\n
\n \n
\n
\n\n
\n\n
\n\n ",
237 | styles: ["\n\n .ngx-image-editor-component .photo-editor-header {\n display: flex;\n justify-content: space-around;\n align-items: center;\n width: 100%;\n padding: 5px 0;\n z-index: 100;\n margin: 0;\n }\n\n .ngx-image-editor-component .photo-editor-header > .mat-icon {\n padding: 0 10px;\n }\n\n .ngx-image-editor-component .photo-editor-header > .file-name {\n flex: 1 1 100%;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .ngx-image-editor-component mat-progress-spinner {\n position: absolute;\n }\n\n .ngx-image-editor-component .dialog-crop-container {\n width: 800px;\n height: 400px;\n overflow: hidden;\n }\n\n .ngx-image-editor-component .cropper-bg {\n background-image: none !important;\n }\n\n .ngx-image-editor-component .cropper-bg > .cropper-modal {\n opacity: 1 !important;\n background: none;\n }\n\n .ngx-image-editor-component .img-container {\n width: 800px !important;\n height: 400px !important;\n }\n\n .ngx-image-editor-component .cropped-image img {\n width: auto !important;\n height: auto !important;\n max-width: 800px !important;\n max-height: 400px !important;\n }\n\n .ngx-image-editor-component .dialog-button-actions {\n position: relative;\n padding: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions:last-child {\n margin: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {\n margin: 20px;\n }\n\n .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config {\n padding: 5px;\n margin: 0 20px;\n }\n\n \n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {\n width: 200px !important;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {\n display: flex;\n align-items: center;\n padding: 0 10px;\n }\n \n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-horizontal .mat-slider-wrapper .mat-slider-thumb-container {\n cursor: grab;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {\n padding: 0 10px;\n font-size: 14px;\n width: 200px;\n max-width: 200px;\n }\n\n \n\n\n\n\n\n\n\n\n\n\n "],
238 | encapsulation: ViewEncapsulation.None
239 | },] },
240 | ];
241 | NgxImageEditorComponent.ctorParameters = function () { return []; };
242 | NgxImageEditorComponent.propDecorators = {
243 | "previewImage": [{ type: ViewChild, args: ['previewimg',] },],
244 | "croppedImg": [{ type: ViewChild, args: ['croppedImg',] },],
245 | "config": [{ type: Input },],
246 | "file": [{ type: Output },],
247 | };
248 | var EditorOptions = /** @class */ (function () {
249 | function EditorOptions() {
250 | }
251 | return EditorOptions;
252 | }());
253 | var NGX_DEFAULT_RATIOS = [
254 | {
255 | value: 16 / 9, text: '16:9'
256 | },
257 | {
258 | value: 4 / 3, text: '4:3'
259 | },
260 | {
261 | value: 1 / 1, text: '1:1'
262 | },
263 | {
264 | value: 2 / 3, text: '2:3'
265 | },
266 | {
267 | value: 0 / 0, text: 'Default'
268 | }
269 | ];
270 | var NgxImageEditorModule = /** @class */ (function () {
271 | function NgxImageEditorModule() {
272 | }
273 | NgxImageEditorModule.forRoot = function () {
274 | return {
275 | ngModule: NgxImageEditorModule,
276 | };
277 | };
278 | return NgxImageEditorModule;
279 | }());
280 | NgxImageEditorModule.decorators = [
281 | { type: NgModule, args: [{
282 | imports: [
283 | FormsModule,
284 | BrowserAnimationsModule,
285 | CommonModule,
286 | ReactiveFormsModule,
287 | FlexLayoutModule,
288 | MatButtonModule,
289 | MatIconModule,
290 | MatDialogModule,
291 | MatInputModule,
292 | MatMenuModule,
293 | MatProgressSpinnerModule,
294 | MatTabsModule,
295 | MatTooltipModule,
296 | MatButtonToggleModule,
297 | MatSliderModule,
298 | MatAutocompleteModule
299 | ],
300 | declarations: [
301 | NgxImageEditorComponent
302 | ],
303 | exports: [NgxImageEditorComponent]
304 | },] },
305 | ];
306 |
307 | export { NgxImageEditorModule, NgxImageEditorComponent, EditorOptions, NGX_DEFAULT_RATIOS };
308 | //# sourceMappingURL=ngx-image-editor.js.map
309 |
--------------------------------------------------------------------------------
/dist/ngx-image-editor.component.d.ts:
--------------------------------------------------------------------------------
1 | import { AfterViewInit, EventEmitter, OnDestroy, OnInit } from '@angular/core';
2 | export declare class NgxImageEditorComponent implements AfterViewInit, OnInit, OnDestroy {
3 | state: EditorOptions;
4 | cropper: any;
5 | croppedImage: string;
6 | imageWidth: number;
7 | imageHeight: number;
8 | canvasWidth: number;
9 | canvasHeight: number;
10 | cropBoxWidth: number;
11 | cropBoxHeight: number;
12 | canvasFillColor: string;
13 | blob: Blob;
14 | loading: boolean;
15 | private zoomIn;
16 | sliderValue: number;
17 | ratios: NgxAspectRatio[];
18 | previewImageURL: any;
19 | previewImage: any;
20 | croppedImg: any;
21 | config: EditorOptions;
22 | file: EventEmitter;
23 | constructor();
24 | ngOnInit(): void;
25 | ngOnDestroy(): void;
26 | ngAfterViewInit(): void;
27 | private handleStateConfig();
28 | private convertFileToBase64(file);
29 | private addRatios(ratios);
30 | handleCrop(): void;
31 | undoCrop(): void;
32 | saveImage(): void;
33 | private initializeCropper();
34 | setRatio(value: any): void;
35 | zoomChange(input: any, zoom?: string): void;
36 | setImageWidth(canvasWidth: number): void;
37 | setImageHeight(canvasHeight: number): void;
38 | setCropBoxWidth(cropBoxWidth: number): void;
39 | setCropBoxHeight(cropBoxHeight: number): void;
40 | centerCanvas(): void;
41 | }
42 | export interface IEditorOptions {
43 | ImageName: string;
44 | ImageUrl?: string;
45 | ImageType?: string;
46 | File?: File;
47 | AspectRatios?: Array;
48 | }
49 | export declare type RatioType = "16:9" | '4:3' | '1:1' | '2:3' | 'Default';
50 | export declare class EditorOptions implements IEditorOptions {
51 | ImageName: string;
52 | ImageUrl?: string;
53 | ImageType?: string;
54 | File?: File;
55 | AspectRatios?: Array;
56 | }
57 | export interface NgxAspectRatio {
58 | value: number;
59 | text: RatioType;
60 | }
61 | export declare const NGX_DEFAULT_RATIOS: Array;
62 |
--------------------------------------------------------------------------------
/dist/ngx-image-editor.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generated bundle index. Do not edit.
3 | */
4 | export * from './public_api';
5 |
--------------------------------------------------------------------------------
/dist/ngx-image-editor.metadata.json:
--------------------------------------------------------------------------------
1 | {"__symbolic":"module","version":4,"metadata":{"NgxImageEditorModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule","line":16,"character":1},"arguments":[{"imports":[{"__symbolic":"reference","module":"@angular/forms","name":"FormsModule","line":19,"character":8},{"__symbolic":"reference","module":"@angular/platform-browser/animations","name":"BrowserAnimationsModule","line":20,"character":8},{"__symbolic":"reference","module":"@angular/common","name":"CommonModule","line":21,"character":8},{"__symbolic":"reference","module":"@angular/forms","name":"ReactiveFormsModule","line":22,"character":8},{"__symbolic":"reference","module":"@angular/flex-layout","name":"FlexLayoutModule","line":23,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatButtonModule","line":24,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatIconModule","line":25,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatDialogModule","line":26,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatInputModule","line":27,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatMenuModule","line":28,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatProgressSpinnerModule","line":29,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatTabsModule","line":30,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatTooltipModule","line":31,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatButtonToggleModule","line":32,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatSliderModule","line":33,"character":8},{"__symbolic":"reference","module":"@angular/material","name":"MatAutocompleteModule","line":34,"character":8}],"declarations":[{"__symbolic":"reference","name":"NgxImageEditorComponent"}],"exports":[{"__symbolic":"reference","name":"NgxImageEditorComponent"}]}]}],"members":{},"statics":{"forRoot":{"__symbolic":"function","parameters":[],"value":{"ngModule":{"__symbolic":"reference","name":"NgxImageEditorModule"}}}}},"NgxImageEditorComponent":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Component","line":14,"character":1},"arguments":[{"selector":"ngx-image-editor","template":"\n \n \n\n
\n
\n \n
![]()
\n
\n \n
\n \n
![]()
\n
\n \n
\n
\n\n
\n\n
\n\n ","styles":["\n\n .ngx-image-editor-component .photo-editor-header {\n display: flex;\n justify-content: space-around;\n align-items: center;\n width: 100%;\n padding: 5px 0;\n z-index: 100;\n margin: 0;\n }\n\n .ngx-image-editor-component .photo-editor-header > .mat-icon {\n padding: 0 10px;\n }\n\n .ngx-image-editor-component .photo-editor-header > .file-name {\n flex: 1 1 100%;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .ngx-image-editor-component mat-progress-spinner {\n position: absolute;\n }\n\n .ngx-image-editor-component .dialog-crop-container {\n width: 800px;\n height: 400px;\n overflow: hidden;\n }\n\n .ngx-image-editor-component .cropper-bg {\n background-image: none !important;\n }\n\n .ngx-image-editor-component .cropper-bg > .cropper-modal {\n opacity: 1 !important;\n background: none;\n }\n\n .ngx-image-editor-component .img-container {\n width: 800px !important;\n height: 400px !important;\n }\n\n .ngx-image-editor-component .cropped-image img {\n width: auto !important;\n height: auto !important;\n max-width: 800px !important;\n max-height: 400px !important;\n }\n\n .ngx-image-editor-component .dialog-button-actions {\n position: relative;\n padding: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions:last-child {\n margin: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {\n margin: 20px;\n }\n\n .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config {\n padding: 5px;\n margin: 0 20px;\n }\n\n \n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {\n width: 200px !important;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {\n display: flex;\n align-items: center;\n padding: 0 10px;\n }\n \n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-horizontal .mat-slider-wrapper .mat-slider-thumb-container {\n cursor: grab;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {\n padding: 0 10px;\n font-size: 14px;\n width: 200px;\n max-width: 200px;\n }\n\n \n\n\n\n\n\n\n\n\n\n\n "],"encapsulation":{"__symbolic":"select","expression":{"__symbolic":"reference","module":"@angular/core","name":"ViewEncapsulation","line":283,"character":17},"member":"None"}}]}],"members":{"previewImage":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"ViewChild","line":305,"character":3},"arguments":["previewimg"]}]}],"croppedImg":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"ViewChild","line":308,"character":3},"arguments":["croppedImg"]}]}],"config":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Input","line":311,"character":3}}]}],"file":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Output","line":316,"character":3}}]}],"__ctor__":[{"__symbolic":"constructor"}],"ngOnInit":[{"__symbolic":"method"}],"ngOnDestroy":[{"__symbolic":"method"}],"ngAfterViewInit":[{"__symbolic":"method"}],"handleStateConfig":[{"__symbolic":"method"}],"convertFileToBase64":[{"__symbolic":"method"}],"addRatios":[{"__symbolic":"method"}],"handleCrop":[{"__symbolic":"method"}],"undoCrop":[{"__symbolic":"method"}],"saveImage":[{"__symbolic":"method"}],"initializeCropper":[{"__symbolic":"method"}],"setRatio":[{"__symbolic":"method"}],"zoomChange":[{"__symbolic":"method"}],"setImageWidth":[{"__symbolic":"method"}],"setImageHeight":[{"__symbolic":"method"}],"setCropBoxWidth":[{"__symbolic":"method"}],"setCropBoxHeight":[{"__symbolic":"method"}],"centerCanvas":[{"__symbolic":"method"}]}},"IEditorOptions":{"__symbolic":"interface"},"RatioType":{"__symbolic":"interface"},"EditorOptions":{"__symbolic":"class","members":{}},"NgxAspectRatio":{"__symbolic":"interface"},"NGX_DEFAULT_RATIOS":[{"value":1.7777777777777777,"text":"16:9"},{"value":1.3333333333333333,"text":"4:3"},{"value":1,"text":"1:1"},{"value":0.6666666666666666,"text":"2:3"},{"value":null,"text":"Default"}]},"origins":{"NgxImageEditorModule":"./ngx-image-editor.module","NgxImageEditorComponent":"./ngx-image-editor.component","IEditorOptions":"./ngx-image-editor.component","RatioType":"./ngx-image-editor.component","EditorOptions":"./ngx-image-editor.component","NgxAspectRatio":"./ngx-image-editor.component","NGX_DEFAULT_RATIOS":"./ngx-image-editor.component"},"importAs":"ngx-image-editor"}
--------------------------------------------------------------------------------
/dist/ngx-image-editor.module.d.ts:
--------------------------------------------------------------------------------
1 | import { ModuleWithProviders } from '@angular/core';
2 | export * from './ngx-image-editor.component';
3 | export declare class NgxImageEditorModule {
4 | static forRoot(): ModuleWithProviders;
5 | }
6 |
--------------------------------------------------------------------------------
/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-image-editor",
3 | "version": "1.0.0-semantic-release",
4 | "description": "Angular 5 Image Editor",
5 | "main": "bundles/ngx-image-editor.umd.js",
6 | "module": "esm5/ngx-image-editor.js",
7 | "typings": "ngx-image-editor.d.ts",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/Centroida/ngx-image-editor"
11 | },
12 | "keywords": [
13 | "Angular 2",
14 | "Angular 4",
15 | "Angular 5",
16 | "Angular 6",
17 | "Angular",
18 | "ng",
19 | "Editor",
20 | "Image Editor",
21 | "Image Preview",
22 | "Cropper"
23 | ],
24 | "author": "Centroida",
25 | "license": "MIT",
26 | "homepage": "https://github.com/Centroida/ngx-image-editor",
27 | "devDependencies": {
28 | "@angular/animations": "6.0.0",
29 | "@angular/cdk": "^5.2.4",
30 | "@angular/cli": "6.0.0",
31 | "@angular/common": "6.0.0",
32 | "@angular/compiler": "6.0.1",
33 | "@angular/compiler-cli": "6.0.1",
34 | "@angular/core": "6.0.0",
35 | "@angular/flex-layout": "^5.0.0-beta.14",
36 | "@angular/forms": "6.0.0",
37 | "@angular/http": "6.0.0",
38 | "@angular/material": "^5.2.4",
39 | "@angular/platform-browser": "6.0.0",
40 | "@angular/platform-browser-dynamic": "6.0.0",
41 | "@types/core-js": "0.9.46",
42 | "@types/node": "~6.0.60",
43 | "codelyzer": "^4.2.1",
44 | "core-js": "^2.5.3",
45 | "ng-packagr": "^2.4.4",
46 | "rxjs": "^6.1.0",
47 | "semantic-release": "^15.4.1",
48 | "ts-node": "^5.0.1",
49 | "tslint": "^5.9.1",
50 | "typescript": "2.7.2",
51 | "zone.js": "~0.8.26"
52 | },
53 | "peerDependencies": {
54 | "@types/cropperjs": "^1.1.3",
55 | "cropperjs": "^1.0.0",
56 | "ng-packagr": "^2.4.4",
57 | "rxjs-compat": "^6.0.0-rc.0",
58 | "tsickle": "^0.28.0",
59 | "tslib": "^1.9.0"
60 | },
61 | "release": {
62 | "debug": false,
63 | "pkgRoot": "dist",
64 | "analyzeCommits": {
65 | "preset": "angular",
66 | "releaseRules": [
67 | {
68 | "type": "docs",
69 | "scope": "README.md",
70 | "release": "patch"
71 | },
72 | {
73 | "type": "refactor",
74 | "scope": "/src",
75 | "release": "minor"
76 | },
77 | {
78 | "type": "refactor",
79 | "release": "patch"
80 | }
81 | ]
82 | }
83 | },
84 | "es2015": "esm2015/ngx-image-editor.js",
85 | "metadata": "ngx-image-editor.metadata.json",
86 | "dependencies": {
87 | "tslib": "^1.9.0"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/dist/public_api.d.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-image-editor.module";
2 |
--------------------------------------------------------------------------------
/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3 | "lib": {
4 | "entryFile": "src/public_api.ts"
5 | }
6 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-image-editor",
3 | "version": "1.0.0-semantic-release",
4 | "description": "Angular 5 Image Editor",
5 | "scripts": {
6 | "test": "echo \"Test success\"",
7 | "clean": "rm -rf dist ",
8 | "build": "ng-packagr -p ng-package.json",
9 | "semantic-release": "npm run clean && npm run build && semantic-release --prepare && semantic-release --publish "
10 | },
11 | "main": "./bundles/ng.umd.js",
12 | "module": "./ngx-image-editor.es5.js",
13 | "typings": "./ngx-image-editor.d.ts",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/Centroida/ngx-image-editor"
17 | },
18 | "keywords": [
19 | "Angular 2",
20 | "Angular 4",
21 | "Angular 5",
22 | "Angular 6",
23 | "Angular",
24 | "ng",
25 | "Editor",
26 | "Image Editor",
27 | "Image Preview",
28 | "Cropper"
29 | ],
30 | "author": "Centroida",
31 | "license": "MIT",
32 | "homepage": "https://github.com/Centroida/ngx-image-editor",
33 | "devDependencies": {
34 | "@angular/animations": "6.0.0",
35 | "@angular/cdk": "^5.2.4",
36 | "@angular/cli": "6.0.0",
37 | "@angular/common": "6.0.0",
38 | "@angular/compiler": "6.0.1",
39 | "@angular/compiler-cli": "6.0.1",
40 | "@angular/core": "6.0.0",
41 | "@angular/flex-layout": "^5.0.0-beta.14",
42 | "@angular/forms": "6.0.0",
43 | "@angular/http": "6.0.0",
44 | "@angular/material": "^5.2.4",
45 | "@angular/platform-browser": "6.0.0",
46 | "@angular/platform-browser-dynamic": "6.0.0",
47 | "@types/core-js": "0.9.46",
48 | "@types/node": "~6.0.60",
49 | "codelyzer": "^4.2.1",
50 | "core-js": "^2.5.3",
51 | "ng-packagr": "^2.4.4",
52 | "rxjs": "^6.1.0",
53 | "semantic-release": "^15.4.1",
54 | "ts-node": "^5.0.1",
55 | "tslint": "^5.9.1",
56 | "typescript": "2.7.2",
57 | "zone.js": "~0.8.26"
58 | },
59 | "peerDependencies": {
60 | "@types/cropperjs": "^1.1.3",
61 | "cropperjs": "^1.0.0",
62 | "ng-packagr": "^2.4.4",
63 | "rxjs-compat": "^6.0.0-rc.0",
64 | "tsickle": "^0.28.0",
65 | "tslib": "^1.9.0"
66 | },
67 | "release": {
68 | "debug": false,
69 | "pkgRoot": "dist",
70 | "analyzeCommits": {
71 | "preset": "angular",
72 | "releaseRules": [
73 | {
74 | "type": "docs",
75 | "scope": "README.md",
76 | "release": "patch"
77 | },
78 | {
79 | "type": "refactor",
80 | "scope": "/src",
81 | "release": "minor"
82 | },
83 | {
84 | "type": "refactor",
85 | "release": "patch"
86 | }
87 | ]
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hggeorgiev/ngx-image-editor/b9b49316ec0f621d356b3bba5ac164487fe4578d/src/.DS_Store
--------------------------------------------------------------------------------
/src/ngx-image-editor.component.d.ts:
--------------------------------------------------------------------------------
1 | import { AfterViewInit, EventEmitter, OnDestroy, OnInit } from '@angular/core';
2 | export declare class NgxImageEditorComponent implements AfterViewInit, OnInit, OnDestroy {
3 | state: EditorOptions;
4 | cropper: any;
5 | croppedImage: string;
6 | imageWidth: number;
7 | imageHeight: number;
8 | canvasWidth: number;
9 | canvasHeight: number;
10 | cropBoxWidth: number;
11 | cropBoxHeight: number;
12 | canvasFillColor: string;
13 | blob: Blob;
14 | loading: boolean;
15 | private zoomIn;
16 | sliderValue: number;
17 | ratios: NgxAspectRatio[];
18 | previewImageURL: any;
19 | previewImage: any;
20 | croppedImg: any;
21 | config: EditorOptions;
22 | file: EventEmitter;
23 | constructor();
24 | ngOnInit(): void;
25 | ngOnDestroy(): void;
26 | ngAfterViewInit(): void;
27 | private handleStateConfig();
28 | private convertFileToBase64(file);
29 | private addRatios(ratios);
30 | handleCrop(): void;
31 | undoCrop(): void;
32 | saveImage(): void;
33 | private initializeCropper();
34 | setRatio(value: any): void;
35 | zoomChange(input: any, zoom?: string): void;
36 | setImageWidth(canvasWidth: number): void;
37 | setImageHeight(canvasHeight: number): void;
38 | setCropBoxWidth(cropBoxWidth: number): void;
39 | setCropBoxHeight(cropBoxHeight: number): void;
40 | centerCanvas(): void;
41 | }
42 | export interface IEditorOptions {
43 | ImageName: string;
44 | ImageUrl?: string;
45 | ImageType?: string;
46 | File?: File;
47 | AspectRatios?: Array;
48 | }
49 | export declare type RatioType = "16:9" | '4:3' | '1:1' | '2:3' | 'Default';
50 | export declare class EditorOptions implements IEditorOptions {
51 | ImageName: string;
52 | ImageUrl?: string;
53 | ImageType?: string;
54 | File?: File;
55 | AspectRatios?: Array;
56 | }
57 | export interface NgxAspectRatio {
58 | value: number;
59 | text: RatioType;
60 | }
61 | export declare const NGX_DEFAULT_RATIOS: Array;
62 |
--------------------------------------------------------------------------------
/src/ngx-image-editor.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AfterViewInit,
3 | Component,
4 | EventEmitter,
5 | Input,
6 | OnDestroy,
7 | OnInit,
8 | Output,
9 | ViewChild,
10 | ViewEncapsulation
11 | } from '@angular/core';
12 |
13 | declare const Cropper: any;
14 |
15 | @Component({
16 | selector: 'ngx-image-editor',
17 | template: `
18 |
19 |
34 |
35 |
41 |
42 |
46 |
![]()
48 |
49 |
50 |
51 |
52 |
![]()
55 |
56 |
57 |
58 |
59 |
60 |
164 |
165 |
166 |
167 | `,
168 | styles: [`
169 |
170 | .ngx-image-editor-component .photo-editor-header {
171 | display: flex;
172 | justify-content: space-around;
173 | align-items: center;
174 | width: 100%;
175 | padding: 5px 0;
176 | z-index: 100;
177 | margin: 0;
178 | }
179 |
180 | .ngx-image-editor-component .photo-editor-header > .mat-icon {
181 | padding: 0 10px;
182 | }
183 |
184 | .ngx-image-editor-component .photo-editor-header > .file-name {
185 | flex: 1 1 100%;
186 | text-overflow: ellipsis;
187 | white-space: nowrap;
188 | overflow: hidden;
189 | }
190 |
191 | .ngx-image-editor-component mat-progress-spinner {
192 | position: absolute;
193 | }
194 |
195 | .ngx-image-editor-component .dialog-crop-container {
196 | width: 800px;
197 | height: 400px;
198 | overflow: hidden;
199 | }
200 |
201 | .ngx-image-editor-component .cropper-bg {
202 | background-image: none !important;
203 | }
204 |
205 | .ngx-image-editor-component .cropper-bg > .cropper-modal {
206 | opacity: 1 !important;
207 | background: none;
208 | }
209 |
210 | .ngx-image-editor-component .img-container {
211 | width: 800px !important;
212 | height: 400px !important;
213 | }
214 |
215 | .ngx-image-editor-component .cropped-image img {
216 | width: auto !important;
217 | height: auto !important;
218 | max-width: 800px !important;
219 | max-height: 400px !important;
220 | }
221 |
222 | .ngx-image-editor-component .dialog-button-actions {
223 | position: relative;
224 | padding: 0;
225 | }
226 |
227 | .ngx-image-editor-component .dialog-button-actions:last-child {
228 | margin: 0;
229 | }
230 |
231 | .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {
232 | margin: 20px;
233 | }
234 |
235 | .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {
236 | position: absolute;
237 | top: 50%;
238 | left: 50%;
239 | transform: translate(-50%, -50%);
240 | }
241 |
242 | .ngx-image-editor-component .dialog-button-actions > .canvas-config {
243 | padding: 5px;
244 | margin: 0 20px;
245 | }
246 |
247 |
248 |
249 | .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {
250 | width: 200px !important;
251 | }
252 |
253 |
254 | .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {
255 | display: flex;
256 | align-items: center;
257 | padding: 0 10px;
258 | }
259 |
260 | .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-horizontal .mat-slider-wrapper .mat-slider-thumb-container {
261 | cursor: grab;
262 | }
263 |
264 |
265 | .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {
266 | padding: 0 10px;
267 | font-size: 14px;
268 | width: 200px;
269 | max-width: 200px;
270 | }
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 | `],
284 | encapsulation: ViewEncapsulation.None
285 | })
286 |
287 | export class NgxImageEditorComponent implements AfterViewInit, OnInit, OnDestroy {
288 |
289 | public state: EditorOptions;
290 | public cropper: any;
291 | public croppedImage: string;
292 | public imageWidth: number;
293 | public imageHeight: number;
294 | public canvasWidth: number;
295 | public canvasHeight: number;
296 | public cropBoxWidth: number;
297 | public cropBoxHeight: number;
298 | public canvasFillColor: string;
299 | public blob: Blob;
300 | public loading: boolean;
301 | private zoomIn: number;
302 | public sliderValue: number;
303 | public ratios: NgxAspectRatio[];
304 | public previewImageURL: any;
305 |
306 | @ViewChild('previewimg')
307 | public previewImage: any;
308 |
309 | @ViewChild('croppedImg')
310 | public croppedImg: any;
311 |
312 | @Input()
313 | public set config(config: EditorOptions) {
314 | this.state = config;
315 | }
316 |
317 | @Output()
318 | public file: EventEmitter = new EventEmitter();
319 |
320 | public constructor() {
321 | this.zoomIn = 0;
322 | this.sliderValue = 0;
323 | this.loading = true;
324 | this.canvasFillColor = '#fff';
325 | this.state = new EditorOptions();
326 | }
327 |
328 | public ngOnInit() {
329 | this.handleStateConfig();
330 | }
331 |
332 | public ngOnDestroy() {
333 | this.cropper.destroy();
334 | }
335 |
336 | public ngAfterViewInit(): void {
337 |
338 | // NOTE if we don't have a file meaning that loading the image will happen synchronously we can safely
339 | // call initializeCropper in ngAfterViewInit. otherwise if we are using the FileReader to load a base64 image
340 | // we need to call onloadend asynchronously..
341 | if (!this.state.File && this.state.ImageUrl) {
342 | this.initializeCropper();
343 | }
344 | }
345 |
346 | private handleStateConfig() {
347 | this.state.ImageType = this.state.ImageType ? this.state.ImageType : 'image/jpeg';
348 |
349 | if (this.state.ImageUrl) {
350 | this.state.File = null;
351 | this.previewImageURL = this.state.ImageUrl;
352 | }
353 |
354 | if (this.state.File) {
355 | this.state.ImageUrl = null;
356 | this.convertFileToBase64(this.state.File);
357 | }
358 |
359 | if (this.state.AspectRatios) {
360 | this.addRatios(this.state.AspectRatios);
361 | } else {
362 | this.ratios = NGX_DEFAULT_RATIOS;
363 | }
364 |
365 |
366 | if (!this.state.ImageUrl && !this.state.File) {
367 | console.error("Property ImageUrl or File is missing, Please provide an url or file in the config options.");
368 | }
369 |
370 | if (!this.state.ImageName) {
371 | console.error("Property ImageName is missing, Please provide a name for the image.");
372 | }
373 | }
374 |
375 | private convertFileToBase64(file: File) {
376 | const reader = new FileReader();
377 | reader.addEventListener("load", (e: any) => {
378 | this.previewImageURL = e.target["result"];
379 | }, false);
380 | reader.readAsDataURL(file);
381 | reader.onloadend = (() => {
382 | // NOTE since getting the base64 image url happens asynchronously we need to initializeCropper after
383 | // the image has been loaded.
384 | this.initializeCropper();
385 | });
386 | }
387 |
388 | private addRatios(ratios: RatioType[]) {
389 | this.ratios = [];
390 | ratios.forEach((ratioType: RatioType) => {
391 | const addedRation = NGX_DEFAULT_RATIOS.find((ratio: NgxAspectRatio) => {
392 | return ratio.text === ratioType;
393 | });
394 | this.ratios.push(addedRation);
395 | });
396 | }
397 |
398 | public handleCrop() {
399 |
400 | this.loading = true;
401 | setTimeout(() => {
402 | this.croppedImage = this.cropper.getCroppedCanvas({fillColor: this.canvasFillColor})
403 | .toDataURL(this.state.ImageType);
404 |
405 | setTimeout(() => {
406 | this.imageWidth = this.croppedImg.nativeElement.width;
407 | this.imageHeight = this.croppedImg.nativeElement.height;
408 | });
409 | this.cropper.getCroppedCanvas({fillColor: this.canvasFillColor}).toBlob((blob: Blob) => {
410 | this.blob = blob;
411 | });
412 | this.zoomIn = 1;
413 | this.loading = false;
414 | }, 2000);
415 | }
416 |
417 | public undoCrop() {
418 | this.croppedImage = null;
419 | this.blob = null;
420 | setTimeout(() => {
421 | this.initializeCropper();
422 | }, 100);
423 |
424 | }
425 |
426 | public saveImage() {
427 | this.file.emit(new File([this.blob], this.state.ImageName, {type: this.state.ImageType}));
428 | }
429 |
430 | private initializeCropper() {
431 | this.cropper = new Cropper(this.previewImage.nativeElement, {
432 | zoomOnWheel: true,
433 | viewMode: 0,
434 | center: true,
435 | ready: () => this.loading = false,
436 | dragMode: 'move',
437 | crop: (e: CustomEvent) => {
438 | this.imageHeight = Math.round(e.detail.height);
439 | this.imageWidth = Math.round(e.detail.width);
440 | this.cropBoxWidth = Math.round(this.cropper.getCropBoxData().width);
441 | this.cropBoxHeight = Math.round(this.cropper.getCropBoxData().height);
442 | this.canvasWidth = Math.round(this.cropper.getCanvasData().width);
443 | this.canvasHeight = Math.round(this.cropper.getCanvasData().height);
444 | }
445 | });
446 |
447 | this.setRatio(this.ratios[0].value);
448 | }
449 |
450 | public setRatio(value: any) {
451 | this.cropper.setAspectRatio(value);
452 | }
453 |
454 | public zoomChange(input: any, zoom?: string) {
455 | if (this.croppedImage) {
456 | if (zoom) {
457 | zoom === 'zoomIn' ? this.zoomIn += 0.1 : this.zoomIn -= 0.1;
458 | } else {
459 | if (input < this.sliderValue) {
460 | this.zoomIn = -Math.abs(input / 100);
461 | } else {
462 | this.zoomIn = Math.abs(input / 100);
463 | }
464 | }
465 | if (this.zoomIn <= 0.1) {
466 | this.zoomIn = 0.1;
467 | }
468 | } else {
469 | if (zoom) {
470 | this.cropper.zoom(input);
471 | this.zoomIn = input;
472 | } else {
473 | if (input < this.sliderValue) {
474 | this.cropper.zoom(-Math.abs(input / 100));
475 | } else {
476 | this.cropper.zoom(Math.abs(input / 100));
477 | }
478 | if (input === 0) {
479 | this.cropper.zoom(-1);
480 | }
481 | }
482 | }
483 |
484 | if (!zoom) {
485 | this.sliderValue = input;
486 | } else {
487 | input > 0 ? this.sliderValue += Math.abs(input * 100) : this.sliderValue -= Math.abs(input * 100);
488 | }
489 |
490 | if (this.sliderValue < 0) {
491 | this.sliderValue = 0;
492 | }
493 | }
494 |
495 | public setImageWidth(canvasWidth: number) {
496 | if (canvasWidth) {
497 | this.cropper.setCanvasData({
498 | left: this.cropper.getCanvasData().left,
499 | top: this.cropper.getCanvasData().top,
500 | width: Math.round(canvasWidth),
501 | height: this.cropper.getCanvasData().height
502 | });
503 | }
504 | }
505 |
506 | public setImageHeight(canvasHeight: number) {
507 | if (canvasHeight) {
508 | this.cropper.setCanvasData({
509 | left: this.cropper.getCanvasData().left,
510 | top: this.cropper.getCanvasData().top,
511 | width: this.cropper.getCanvasData().width,
512 | height: Math.round(canvasHeight)
513 | });
514 | }
515 | }
516 |
517 | public setCropBoxWidth(cropBoxWidth: number) {
518 | if (cropBoxWidth) {
519 | this.cropper.setCropBoxData({
520 | left: this.cropper.getCropBoxData().left,
521 | top: this.cropper.getCropBoxData().top,
522 | width: Math.round(cropBoxWidth),
523 | height: this.cropper.getCropBoxData().height
524 | });
525 | }
526 | }
527 |
528 | public setCropBoxHeight(cropBoxHeight: number) {
529 | if (cropBoxHeight) {
530 | this.cropper.setCropBoxData({
531 | left: this.cropper.getCropBoxData().left,
532 | top: this.cropper.getCropBoxData().top,
533 | width: this.cropper.getCropBoxData().width,
534 | height: Math.round(cropBoxHeight)
535 | });
536 | }
537 | }
538 |
539 | public centerCanvas() {
540 | const cropBoxLeft = (this.cropper.getContainerData().width - this.cropper.getCropBoxData().width) / 2;
541 | const cropBoxTop = (this.cropper.getContainerData().height - this.cropper.getCropBoxData().height) / 2;
542 | const canvasLeft = (this.cropper.getContainerData().width - this.cropper.getCanvasData().width) / 2;
543 | const canvasTop = (this.cropper.getContainerData().height - this.cropper.getCanvasData().height) / 2;
544 |
545 | this.cropper.setCropBoxData({
546 | left: cropBoxLeft,
547 | top: cropBoxTop,
548 | width: this.cropper.getCropBoxData().width,
549 | height: this.cropper.getCropBoxData().height
550 | });
551 | this.cropper.setCanvasData({
552 | left: canvasLeft,
553 | top: canvasTop,
554 | width: this.cropper.getCanvasData().width,
555 | height: this.cropper.getCanvasData().height
556 | });
557 | }
558 |
559 | }
560 |
561 |
562 | export interface IEditorOptions {
563 | ImageName: string;
564 | ImageUrl?: string;
565 | ImageType?: string;
566 | File?: File;
567 | AspectRatios?: Array;
568 | }
569 |
570 | export type RatioType = "16:9" | '4:3' | '1:1' | '2:3' | 'Default';
571 |
572 | export class EditorOptions implements IEditorOptions {
573 | ImageName: string;
574 | ImageUrl?: string;
575 | ImageType?: string;
576 | File?: File;
577 | AspectRatios?: Array;
578 | }
579 |
580 |
581 | export interface NgxAspectRatio {
582 | value: number;
583 | text: RatioType;
584 | }
585 |
586 |
587 |
588 | export const NGX_DEFAULT_RATIOS: Array = [
589 | {
590 | value: 16 / 9, text: '16:9'
591 | },
592 | {
593 | value: 4 / 3, text: '4:3'
594 | },
595 | {
596 | value: 1 / 1, text: '1:1'
597 | },
598 | {
599 | value: 2 / 3, text: '2:3'
600 | },
601 | {
602 | value: 0 / 0, text: 'Default'
603 | }
604 | ];
605 |
--------------------------------------------------------------------------------
/src/ngx-image-editor.module.d.ts:
--------------------------------------------------------------------------------
1 | import { ModuleWithProviders } from '@angular/core';
2 | export * from './ngx-image-editor.component';
3 | export declare class NgxImageEditorModule {
4 | static forRoot(): ModuleWithProviders;
5 | }
6 |
--------------------------------------------------------------------------------
/src/ngx-image-editor.module.ts:
--------------------------------------------------------------------------------
1 | import {ModuleWithProviders, NgModule} from '@angular/core';
2 | import {NgxImageEditorComponent} from './ngx-image-editor.component';
3 | import {FlexLayoutModule} from "@angular/flex-layout";
4 | import {
5 | MatAutocompleteModule,
6 | MatButtonModule, MatButtonToggleModule, MatIconModule, MatInputModule, MatMenuModule, MatProgressSpinnerModule,
7 | MatSliderModule,MatDialogModule,
8 | MatTabsModule, MatTooltipModule
9 | } from "@angular/material";
10 | import {FormsModule, ReactiveFormsModule} from "@angular/forms";
11 | import {CommonModule} from "@angular/common";
12 | import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
13 |
14 |
15 | export * from './ngx-image-editor.component';
16 |
17 | @NgModule({
18 |
19 | imports: [
20 | FormsModule,
21 | BrowserAnimationsModule,
22 | CommonModule,
23 | ReactiveFormsModule,
24 | FlexLayoutModule,
25 | MatButtonModule,
26 | MatIconModule,
27 | MatDialogModule,
28 | MatInputModule,
29 | MatMenuModule,
30 | MatProgressSpinnerModule,
31 | MatTabsModule,
32 | MatTooltipModule,
33 | MatButtonToggleModule,
34 | MatSliderModule,
35 | MatAutocompleteModule
36 | ],
37 | declarations: [
38 | NgxImageEditorComponent
39 | ],
40 | exports: [NgxImageEditorComponent]
41 | })
42 |
43 |
44 | export class NgxImageEditorModule {
45 | static forRoot(): ModuleWithProviders {
46 | return {
47 | ngModule: NgxImageEditorModule,
48 | };
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/public_api.d.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-image-editor.module";
2 |
--------------------------------------------------------------------------------
/src/public_api.js:
--------------------------------------------------------------------------------
1 | export * from "./ngx-image-editor.module";
2 |
--------------------------------------------------------------------------------
/src/public_api.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-image-editor.module";
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": true,
4 | "module": "es2015",
5 | "target": "es5",
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "declaration": true,
9 | "moduleResolution": "node",
10 | "types": [
11 | "node"
12 | ],
13 | "lib": ["es2015", "dom"]
14 | },
15 | "exclude": [
16 | "node_modules"
17 | ],
18 | "angularCompilerOptions": {
19 | "strictMetadataEmit": true,
20 | "skipTemplateCodegen": true
21 | }
22 | }
--------------------------------------------------------------------------------