koorgoo / ngCropper

AngularJS wrapper for https://github.com/fengyuanchen/cropper
107 stars 72 forks source link

Uploading rotated and cropped image #17

Open igorauad opened 9 years ago

igorauad commented 9 years ago

Anybody can give me some recommendation on how to crop a rotated image? Is there a straightforward way of implementing this using this library?

As a reference, find below my controller. It is used in a modal, which sends back the cropped imageURI through events to the parent controller. Before uploading, the parent controller converts back this image URI to a Blob and adds to the form. Hence, the most important result within the controller attached below is the image URL in $scope.$emit('finishedCrop', imgUrl);.

Obviously, in the rotate() function I could update the global file, by converting the image URL to blob. Then, when $scope.finish was called, the file that would be cropped would be the updated one (rotated). The problem is that this crashes on iOS8 (which apparently only supports square canvas aside from known image size issues). Anybody see another solution?

Note the file that comes from the parent controller in this case was already resized to comply with iOS issues. Cropping works when the image is not rotated.

Thanks in advance.

angular.module('ideas').controller('CropModalController', function($scope, $timeout, $modalInstance, Cropper) {
    var data, file;

    $scope.init = function() {
        /**
         * Croppers container object should be created in controller's scope
         * for updates by directive via prototypal inheritance.
         * Pass a full proxy name to the `ng-cropper-proxy` directive attribute to
         * enable proxing.
         */
        $scope.cropper = {};
        $scope.cropperProxy = 'cropper.first';
        // File comes from parent controller
        file = $scope.$parent.imgFile;

        Cropper.encode(file).then(function(dataUrl) {
            $scope.dataUrl = dataUrl;
            $timeout(showCropper); // wait for $digest to set image's src
        });
    };

    $scope.rotate = function(angle) {
        $scope.cropper.first('rotate', angle);
    };

    $scope.finish = function() {
        // Resize image
        Cropper.crop(file, data).then(Cropper.encode).then(function(imgUrl) {
            $scope.$emit('finishedCrop', imgUrl);
            $modalInstance.dismiss('cancel');
        });
    };

    /**
     * Object is used to pass options to initalize a cropper.
     * More on options - https://github.com/fengyuanchen/cropper#options
     */
    $scope.options = {
        maximize: true,
        aspectRatio: 4 / 3,
        strict: false,
        zoomable: false,
        movable: false,
        crop: function(dataNew) {
            data = dataNew;
        }
    };

    /**
     * Showing (initializing) and hiding (destroying) of a cropper are started by
     * events. The scope of the `ng-cropper` directive is derived from the scope of
     * the controller. When initializing the `ng-cropper` directive adds two handlers
     * listening to events passed by `ng-cropper-show` & `ng-cropper-hide` attributes.
     * To show or hide a cropper `$broadcast` a proper event.
     */
    $scope.showEvent = 'show';
    $scope.hideEvent = 'hide';

    function showCropper() {
        $scope.$broadcast($scope.showEvent);
    }

    function hideCropper() {
        $scope.$broadcast($scope.hideEvent);
    }

});
eduardogdo commented 8 years ago

The same happend to my. I rotate the image but when crop not appear rotate. Sorry my english

lenciel commented 8 years ago

Same problem here. I read the code and it seems that the wrapper function Cropper.crop(file, data) do not handle rotate parameter in data. You should call getCanvasData using your proxy and then convert the canvas object you get to dataurl before uploading.

eduardogdo commented 8 years ago

Thanks for your comment but how can convert canvas object to dataurl with the angular mudule.

lenciel commented 8 years ago

The canvas object has the method called toDataUrl(), so you can write something like this:

$scope. dataUrl = $scope.cropper.first('getCroppedCanvas', {
    width:150, 
    height: 150}).toDataURL();

You can of course change the width/height there in my example code (150/150) as you wish.

eduardogdo commented 8 years ago

thanks I'll try and tell you.

artuska commented 8 years ago

+1. Same issue with rotate — ngCropper just not rotates the image.

artuska commented 8 years ago

Is there any way to refresh the cropper or refresh the duplicated image cropper uses? (There are two images — original image, which is hidden with .cropper-hidden class and duplicated original image which is used by cropper.)

mattmeye commented 7 years ago

+1

maurobilotti commented 5 years ago

This made the magic for me:

       this.crop = function (file, data) {
            var _decodeBlob = this.decode;
            return this.encode(file).then(_createImage).then(function (image) {
                var canvas = createCanvas(data);
                var context = canvas.getContext('2d');

                drawImage(context, image, canvas, data.rotate);

                var encoded = canvas.toDataURL(file.type);
                removeElement(canvas);

                return _decodeBlob(encoded);
            });
        };

        function drawImage(ctx, image, canvas ,degrees) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.save();
            ctx.translate(canvas.width / 2, canvas.height / 2);
            ctx.rotate(degrees * Math.PI / 180);
            ctx.drawImage(image, -image.width / 2, -image.width / 2);
            ctx.restore();
        }
josephlaw commented 4 years ago

@maurobilotti thanks your suggestion, but got the cropped image shifted down and out the boundary.