=0 && quality<=1){
307 | resImgQuality = quality;
308 | }
309 | };
310 |
311 | this.setAreaType=function(type) {
312 | var curSize=theArea.getSize(),
313 | curMinSize=theArea.getMinSize(),
314 | curX=theArea.getX(),
315 | curY=theArea.getY();
316 |
317 | var AreaClass=CropAreaCircle;
318 | if(type==='square') {
319 | AreaClass=CropAreaSquare;
320 | }
321 | theArea = new AreaClass(ctx, events);
322 | theArea.setMinSize(curMinSize);
323 | theArea.setSize(curSize);
324 | theArea.setX(curX);
325 | theArea.setY(curY);
326 |
327 | // resetCropHost();
328 | if(image!==null) {
329 | theArea.setImage(image);
330 | }
331 |
332 | drawScene();
333 | };
334 |
335 | /* Life Cycle begins */
336 |
337 | // Init Context var
338 | ctx = elCanvas[0].getContext('2d');
339 |
340 | // Init CropArea
341 | theArea = new CropAreaCircle(ctx, events);
342 |
343 | // Init Mouse Event Listeners
344 | $document.on('mousemove',onMouseMove);
345 | elCanvas.on('mousedown',onMouseDown);
346 | $document.on('mouseup',onMouseUp);
347 |
348 | // Init Touch Event Listeners
349 | $document.on('touchmove',onMouseMove);
350 | elCanvas.on('touchstart',onMouseDown);
351 | $document.on('touchend',onMouseUp);
352 |
353 | // CropHost Destructor
354 | this.destroy=function() {
355 | $document.off('mousemove',onMouseMove);
356 | elCanvas.off('mousedown',onMouseDown);
357 | $document.off('mouseup',onMouseMove);
358 |
359 | $document.off('touchmove',onMouseMove);
360 | elCanvas.off('touchstart',onMouseDown);
361 | $document.off('touchend',onMouseMove);
362 |
363 | elCanvas.remove();
364 | };
365 | };
366 |
367 | }]);
368 |
--------------------------------------------------------------------------------
/source/js/classes/crop-pubsub.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | crop.factory('cropPubSub', [function() {
4 | return function() {
5 | var events = {};
6 | // Subscribe
7 | this.on = function(names, handler) {
8 | names.split(' ').forEach(function(name) {
9 | if (!events[name]) {
10 | events[name] = [];
11 | }
12 | events[name].push(handler);
13 | });
14 | return this;
15 | };
16 | // Publish
17 | this.trigger = function(name, args) {
18 | angular.forEach(events[name], function(handler) {
19 | handler.call(null, args);
20 | });
21 | return this;
22 | };
23 | };
24 | }]);
--------------------------------------------------------------------------------
/source/js/init.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var crop = angular.module('ngImgCrop', []);
--------------------------------------------------------------------------------
/source/js/ng-img-crop.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | crop.directive('imgCrop', ['$timeout', 'cropHost', 'cropPubSub', function($timeout, CropHost, CropPubSub) {
4 | return {
5 | restrict: 'E',
6 | scope: {
7 | image: '=',
8 | resultImage: '=',
9 |
10 | changeOnFly: '=',
11 | areaType: '@',
12 | areaMinSize: '=',
13 | resultImageSize: '=',
14 | resultImageFormat: '@',
15 | resultImageQuality: '=',
16 |
17 | onChange: '&',
18 | onLoadBegin: '&',
19 | onLoadDone: '&',
20 | onLoadError: '&'
21 | },
22 | template: '',
23 | controller: ['$scope', function($scope) {
24 | $scope.events = new CropPubSub();
25 | }],
26 | link: function(scope, element/*, attrs*/) {
27 | // Init Events Manager
28 | var events = scope.events;
29 |
30 | // Init Crop Host
31 | var cropHost=new CropHost(element.find('canvas'), {}, events);
32 |
33 | // Store Result Image to check if it's changed
34 | var storedResultImage;
35 |
36 | var updateResultImage=function(scope) {
37 | var resultImage=cropHost.getResultImageDataURI();
38 | if(storedResultImage!==resultImage) {
39 | storedResultImage=resultImage;
40 | if(angular.isDefined(scope.resultImage)) {
41 | scope.resultImage=resultImage;
42 | }
43 | scope.onChange({$dataURI: scope.resultImage});
44 | }
45 | };
46 |
47 | // Wrapper to safely exec functions within $apply on a running $digest cycle
48 | var fnSafeApply=function(fn) {
49 | return function(){
50 | $timeout(function(){
51 | scope.$apply(function(scope){
52 | fn(scope);
53 | });
54 | });
55 | };
56 | };
57 |
58 | // Setup CropHost Event Handlers
59 | events
60 | .on('load-start', fnSafeApply(function(scope){
61 | scope.onLoadBegin({});
62 | }))
63 | .on('load-done', fnSafeApply(function(scope){
64 | scope.onLoadDone({});
65 | }))
66 | .on('load-error', fnSafeApply(function(scope){
67 | scope.onLoadError({});
68 | }))
69 | .on('area-move area-resize', fnSafeApply(function(scope){
70 | if(!!scope.changeOnFly) {
71 | updateResultImage(scope);
72 | }
73 | }))
74 | .on('area-move-end area-resize-end image-updated', fnSafeApply(function(scope){
75 | updateResultImage(scope);
76 | }));
77 |
78 | // Sync CropHost with Directive's options
79 | scope.$watch('image',function(){
80 | cropHost.setNewImageSource(scope.image);
81 | });
82 | scope.$watch('areaType',function(){
83 | cropHost.setAreaType(scope.areaType);
84 | updateResultImage(scope);
85 | });
86 | scope.$watch('areaMinSize',function(){
87 | cropHost.setAreaMinSize(scope.areaMinSize);
88 | updateResultImage(scope);
89 | });
90 | scope.$watch('resultImageSize',function(){
91 | cropHost.setResultImageSize(scope.resultImageSize);
92 | updateResultImage(scope);
93 | });
94 | scope.$watch('resultImageFormat',function(){
95 | cropHost.setResultImageFormat(scope.resultImageFormat);
96 | updateResultImage(scope);
97 | });
98 | scope.$watch('resultImageQuality',function(){
99 | cropHost.setResultImageQuality(scope.resultImageQuality);
100 | updateResultImage(scope);
101 | });
102 |
103 | // Update CropHost dimensions when the directive element is resized
104 | scope.$watch(
105 | function () {
106 | return [element[0].clientWidth, element[0].clientHeight];
107 | },
108 | function (value) {
109 | cropHost.setMaxDimensions(value[0],value[1]);
110 | updateResultImage(scope);
111 | },
112 | true
113 | );
114 |
115 | // Destroy CropHost Instance when the directive is destroying
116 | scope.$on('$destroy', function(){
117 | cropHost.destroy();
118 | });
119 | }
120 | };
121 | }]);
--------------------------------------------------------------------------------
/source/scss/ng-img-crop.scss:
--------------------------------------------------------------------------------
1 | img-crop {
2 | width:100%;
3 | height:100%;
4 | display:block;
5 | position:relative;
6 | overflow:hidden;
7 | canvas {
8 | display:block;
9 | position:absolute;
10 | top:50%;
11 | left:50%;
12 |
13 | // Disable Outline
14 | outline: none;
15 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */
16 | }
17 | }
--------------------------------------------------------------------------------
/test/ng-img-crop.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ngImgCrop Test Page
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Container size:
35 |
36 |
37 |
38 |
39 |
40 | Area type:
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Result Image Format:
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
98 |
99 |
100 |
Result
101 |
102 |
![]()
103 |
104 |
105 |
106 |
146 |
147 |
--------------------------------------------------------------------------------
/test/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexk111/ngImgCrop/585fd8c87d453ab27ce29082d62dd7c7a5891ea5/test/test.jpg
--------------------------------------------------------------------------------