Foliotek / Croppie

A Javascript Image Cropper
http://foliotek.github.io/Croppie
MIT License
2.56k stars 884 forks source link

Base64 result giving black image #304

Open tterrag1098 opened 7 years ago

tterrag1098 commented 7 years ago

The base64 data I get from calling result seems to be a blank image. I may be doing something wrong but I can't pinpoint it. Here is my current code:

https://github.com/TeamRocket-COSC425/MathCSClubWebsite/blob/ea70112c6fd0fc2013bfa4478d3e19bdd354ad2f/web/profile.php#L214-L256

The image displays fine in the croppie, and the dimensions of the blank image are correct, it's just...well...blank. Any help would be appreciated.

thedustinsmith commented 7 years ago

Thanks for the code. It looks like it should work to me. How are you determining that the image is blank? Is it blank after you send it to the server and save?

Have you tried debugging by adding an image to the page, and setting the src of that image to your base64 string? Is it still blank if you do that? That would rule out the possibility of the bug being in the server logic or in the front end.

tterrag1098 commented 7 years ago

I took the base64 (from the chrome debugger) and put it in an online base64 decoder so my code wasn't involved at all. However, I can try that next time I get a chance.

aradhell commented 6 years ago

Hello,

I am also facing with this issue, sometimes croppie returns blank image base64 ( if the type is png) or a black image base64 (if the type is jpeg).

I checked croppie result with console. Yes it is black or transparent before saving to the server.

$uploadCrop = $('#my-imagex').croppie({
              viewport: {
                  width: 300,
                  height: 300
              },
              enableExif: true
          });

function readFile(input) {
              if (input.files && input.files[0]) {
                  var reader = new FileReader();

                  reader.onload = function (e) {
                      $('.my-imagex').addClass('ready');
                      $uploadCrop.croppie('bind', {
                          url: e.target.result
                      }).then(function(){
                          console.log('jQuery bind complete');
                          $('#profile-save').show();
                      });

                  }

                  reader.readAsDataURL(input.files[0]);
              }
              else {
                  alert("Sorry - you're browser doesn't support the FileReader API");
              }
          }

$('#upload').on('change', function () { readFile(this);});
          $('#profile-save').on('click', function (ev) {
              $('#profile-save').attr("disabled",true);
              $uploadCrop.croppie('result', {
                  type: 'canvas',
                  size: 'viewport',
                  format: 'jpeg'
              }).then(function (resp) {
                  console.log("base64 : "+resp);
                  $.post("/ajax/requests.php?request=xx", {blob: resp},
                      function(data, status){
                          var response = jQuery.parseJSON(data);
                          if(response.success == 1) {
                              var ipath = '<?=URL_;?>'+response.img_path;
                              $("#profile_photo_holder").css('background-image', 'url(' + ipath + ')');
                              $("#edit-photo-popup").hide();
                              $('#profile-save').attr("disabled",false);

                              //location.reload();
                          } else {
                              alert(data);
                              $('#profile-save').attr("disabled",false);
                          }

                      });
              });
          });
RowanRishi commented 6 years ago

Hey guys!

Well I'm half glad to see that I'm not alone in this world! I have a site currently in production and I'm having the same issue! Here are some details about what's going on:

1) Some of the images are completely black. This is quiterare. 2) Sometimes part of the images (the bottom part) are black. The amount of black (at the bottom of the image) varies: from a thin stripe to a major part of the image being black.

We are helpless and right now I am looking into a different cropping/resizing/rotating responsive solution.

danazkari commented 6 years ago

Bump! Same here, we've got a site that's about to go live, and we have this issue from time to time. Funny thing is, if the user goes back and then forwards again (in the app process, from croppie to the preview result page) then it fixes itself, with no additional input.

dws-arjun commented 6 years ago

I am also facing the same issue. Sometime it renders properly and sometime it gives blank image.

var uploadTopCrop = $('#upload-top-crop');
var uploadFooterCrop = $('#upload-footer-crop');

function callCroppieToCrop(element_id) {
    element_id.croppie({
        enableExif: true,
        viewport: {
            width: 200,
            height: 200,
            type: 'square'
        },
        boundary: {
            width: 300,
            height: 300
        },
        showZoomer: true,
        enableResize: false,
        enableOrientation: true,
        enforceBoundary: false,
        mouseWheelZoom: true
    });
}
callCroppieToCrop(uploadTopCrop);
callCroppieToCrop(uploadFooterCrop);

// top logo
$('#uploadImage').on('change', function () {

    if (!this.files[0].type.match('image.*')) {
        $('#errlogo').html('%%web_data.INVAL_IMAGE%%');
        return false;
    }

    if (this.files[0].size > 2097152) { // 2 mb for bytes.
        $('#errlogo').html('File size should not be more than 2MB');
        return false;
    }
    $('#errlogo').html('');

    var reader = new FileReader();
    reader.onload = function (e) {
        uploadTopCrop.croppie('bind', {
            url: e.target.result
        }).then(function(){
            console.log('top bind complete');
        });
    }
    reader.readAsDataURL(this.files[0]);

    $('#croppedTopImage').modal('show');

});
$('#image-top-crop-okay').on('click', function (ev) {

    uploadTopCrop.croppie('result', {
        type: "canvas",
        size: "viewport",
        format: "png"
    }).then(function (resp) {
        $('#submitForm').css({'pointer-events':'none', 'opacity': '0.5'});
        $.ajax({
            url: window.location.href,
            type: "POST",
            data: {
                "image" : resp, 
                "wa_image_upload_form" : "website_analyzer_top_logo",
                "user_id" : '<?php echo $user_id; ?>'
            },
            success: function (data) {

                $('#submitForm').css({'pointer-events':'auto', 'opacity': '1'});

                if (data == 'none') 
                    $('#errlogo').html('something went wrong');
                else {
                    $('#errlogo').html('');
                    $('#top-logo-cropped').val(data);
                }
            }
        });
        html = '<img src="' + resp + '" />';
        $("#showImage").html(html);
        $('#croppedTopImage').modal('hide');
    });
});

// footer logo
$('#uploadFooterImage').on('change', function () {

    if (!this.files[0].type.match('image.*')) {
        $('#errfooterImg').html('%%web_data.INVAL_IMAGE%%');
        return false;
    }

    if (this.files[0].size > 2097152) { // 2 mb for bytes.
        $('#errfooterImg').html('File size should not be more than 2MB');
        return false;
    }
    $('#errfooterImg').html('');

    var reader = new FileReader();
    reader.onload = function (e) {
        uploadFooterCrop.croppie('bind', {
            url: e.target.result
        }).then(function(){
            console.log('footer bind complete');
        });
    }
    reader.readAsDataURL(this.files[0]);

    $('#croppedFooterImage').modal('show');

});
$('#image-footer-crop-okay').on('click', function (ev) {

    uploadFooterCrop.croppie('result', {
        type: "canvas",
        size: "viewport",
        format: "png"
    }).then(function (resp) {
        $('#submitForm').css({'pointer-events':'none', 'opacity': '0.5'});
        $.ajax({
            url: window.location.href,
            type: "POST",
            data: {
                "image" : resp, 
                "wa_image_upload_form" : "website_analyzer_footer_logo",
                "user_id" : '<?php echo $user_id; ?>'
            },
            success: function (data) {

                $('#submitForm').css({'pointer-events':'auto', 'opacity': '1'});

                if (data == 'none') 
                    $('#errfooterImg').html('something went wrong'); 
                else {
                    $('#errfooterImg').html('');
                    $('#footer-logo-cropped').val(data);                   
                }
            }
        });
        html = '<img src="' + resp + '" />';
        $("#showfootImage").html(html);
        $('#croppedFooterImage').modal('hide');
    });
});
thedustinsmith commented 6 years ago

@dws-arjun Your issue is because you're binding the croppie instance before your modal is shown. Croppie has a very strict requirement that all elements need to be visible before calling bind. You need to call modal('show') and then wait for it to be shown before calling croppie.bind(...).

The intermittent-ness of this issue makes me believe that this might be the issue for all of you. If not, maybe you can throw together a jsbin/fiddle/codepen to demonstrate the issue. That would at least clear up if it's an issue with croppie or with the setup.

bhavikji commented 6 years ago

hey @thedustinsmith I'm facing the same issue and unlike you said my modal is showing before binding the croppie instance yet I'm getting this issue, for proof of concept I have my jsfiddle link here

thedustinsmith commented 6 years ago

Thanks for the jsfiddle, but it seems a bit incomplete. Next time try referencing all of the libraries js and their css. It makes it easier to see the issue if it's all working like you expect it to.

Either way, your issue is that the modal isn't completely shown before binding croppie. Just because you say $something.modal('show'); $croppie.croppie('bind'...) doesn't mean that show has completed. In fact, show takes a while. They have several animations that need to complete before the dialog is completely positioned.

There's an example on the demos page specifically for bootstrap modals, I suggest you check it out: http://foliotek.github.io/Croppie/#important-notes

bhavikji commented 6 years ago

hey, I got the fix for the above issue also with modal it will work fine! I got this in codepen and now I am unable to find that pen however luckily I forked that pen and here I am attaching that forked pen I guess that's what everyone where looking for whoever participated here

check this codepen

epetre commented 5 years ago

@dws-arjun Your issue is because you're binding the croppie instance before your modal is shown. Croppie has a very strict requirement that all elements need to be visible before calling bind. You need to call modal('show') and then wait for it to be shown before calling croppie.bind(...).

The intermittent-ness of this issue makes me believe that this might be the issue for all of you. If not, maybe you can throw together a jsbin/fiddle/codepen to demonstrate the issue. That would at least clear up if it's an issue with croppie or with the setup.

That was it for me. A simple way to test this without changing too much of the logic is to set a high timeout of a few seconds before binding and if it works properly, it means croppie.bind was called too early.

jawad-aziz-farhad commented 5 years ago

I was having the same issue in my angular 7 project. I was calling the result method without waiting for the promise response of bind method. I got it working with this:

import Croppie, {CroppieOptions, ResultOptions} from "croppie/croppie";

@ViewChild('imageElement') imageElement; ElementRef; cropper: any;

let options: CroppieOptions = { viewport: { width: 500, height: 500, type: 'square' }, points: [77,469,280,739], showZoomer: true, enableResize: false, enableOrientation: true, boundary: { width: 500, height: 500 } }; this.cropper = new Croppie(this.imageElement.nativeElement, options); this.cropper.bind( { url: 'assets/images/image_1.jpg'}).then( response => { this.cropper.result({ type: 'base64', size: 'viewport', format: 'png' }).then(result => { console.log('Result', result); }); });

tosifpathan commented 3 years ago

I was having the same issue in my angular 7 project. I was calling the result method without waiting for the promise response of bind method. I got it working with this:

import Croppie, {CroppieOptions, ResultOptions} from "croppie/croppie";

@ViewChild('imageElement') imageElement; ElementRef; cropper: any;

let options: CroppieOptions = { viewport: { width: 500, height: 500, type: 'square' }, points: [77,469,280,739], showZoomer: true, enableResize: false, enableOrientation: true, boundary: { width: 500, height: 500 } }; this.cropper = new Croppie(this.imageElement.nativeElement, options); this.cropper.bind( { url: 'assets/images/image_1.jpg'}).then( response => { this.cropper.result({ type: 'base64', size: 'viewport', format: 'png' }).then(result => { console.log('Result', result); }); });

hi @jawad-aziz-farhad i have same issue ho can i used in angular.

tosifpathan commented 3 years ago

hi @jawad-aziz-farhad I used code like this as per you comment but still not working getting blank base64 can you please suggest how your code work.

import { Component, OnInit, Input, EventEmitter, Output, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; import * as Croppie from 'croppie'; import { CroppieOptions, ResultOptions, CropData } from 'croppie';

export type Type = 'canvas' | 'base64' | 'html' | 'blob' | 'rawcanvas';

@Component({ selector: 'image-croppie', template: <div #imageEdit (update)="newResult()"></div> }) export class ImageCroppieComponent implements OnInit, AfterViewInit { @ViewChild('imageEdit', { static: false }) imageEdit: ElementRef; @Input() croppieOptions: CroppieOptions; @Input() points: number[]; @Input() outputFormatOptions: ResultOptions = { type: 'base64', size: 'viewport' }; @Input() defaultZoom = null; @Output() result: EventEmitter<string | HTMLElement | Blob | HTMLCanvasElement> = new EventEmitter<string | HTMLElement | Blob | HTMLCanvasElement>();

private _croppie: Croppie;
private imgUrl: string;
get imageUrl(): string {
    return this.imgUrl;
}
@Input() set imageUrl(url: string) {
    if (this.imgUrl === url) { return; }
    this.imgUrl = url;
    if (this._croppie) {
        this.bindToCroppie(this.imageUrl, this.points, this.defaultZoom);
    }
}
tempResult: any;

ngOnInit(): void { }

ngAfterViewInit() {
    this._croppie = new Croppie(this.imageEdit.nativeElement, this.croppieOptions);
    this.bindToCroppie(this.imageUrl, this.points, this.defaultZoom);
}

private bindToCroppie(url: string, points: number[], zoom: number) {
    this._croppie.bind({ url, points, zoom }).then(response => {
        this._croppie.result(this.outputFormatOptions).then(result => {
            console.log('Result', result);
            this.tempResult = result;
        });
    });
}

newResult() {
    this.result.emit(this.tempResult);
}

rotate(degrees: 90 | 180 | 270 | -90 | -180 | -270) {
    this._croppie.rotate(degrees);
}

get(): CropData {
    return this._croppie.get();
}

}