Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JCrop attaches height and width to the original image tag, but doesn't remove them upon destruction #46

Open
bhardwick opened this issue Jun 14, 2012 · 29 comments

Comments

@bhardwick
Copy link

MOVED FROM GOOGLE CODE

What steps will reproduce the problem?

  1. Open JCrop on an image
  2. Replace the image source in the DOM via JavaScript with an image of a different size
  3. Call destroy()
  4. Open JCrop on the updated image

What is the expected output? What do you see instead?
You'll see that the image is now stretched to the dimensions of the previous image.

What version of the product are you using? On what operating system?
0.9.9

Please provide any additional information below.
The reason this happens is because when JCrop is first initialized, it attaches height and width attributes to the tag that you initialize it with. However, after calling destroy(), the height and width attributes are still present, so you can't just update the source and open JCrop again. The fix would be to remove the height and width attributes (or reset them to their original values) in the destroy() method.

@bhardwick
Copy link
Author

My work-around was to remove the tag from the DOM and re-add it, before re-initializing jCrop:

$('#cropImage').replaceWith('<img id="cropImage" src="' + source + '"/>');

$('#cropImage').Jcrop({...});

@MarcusJT
Copy link

+1

The workaround only works for very simple cases / img elements - any number of other properties (e.g. unique IDs, CSS classes, data attributes, etc) could be destroyed.

IMHO jCrop shouldn't need to modify the image at all, it should store the height & width using jQuery data(), which will in turn use data- attributes when available, and otherwise use jQuery's internal data storage system.

These values should also be automatically removed during destroy().

@tomatohater
Copy link

+1 Add 'visibility' to this list

@sjkennedy
Copy link

Yep just hit this issue. Is there a robust work around for this?

@tomatohater
Copy link

I doubt this could be classified as "robust", but this has been working for me:

jcrop_api.destroy();
$('.imagePreviewLarge').removeAttr('style');

@tomaszkomin
Copy link

That is working for me too

jcrop_api.destroy();
$('.imagePreviewLarge').removeAttr('style');

thanks a lot

@bsasikiran
Copy link

yes, this worked fine. and as per my analysis it is the workaround needed.
jcrop_api.destroy();
$('.imagePreviewLarge').removeAttr('style');

@mattolson
Copy link

I'm running up against this too. I had used setImage previously, but for some reason that's not working for me anymore.

@bios42eth
Copy link

+1

1 similar comment
@evanbeard
Copy link

+1

@lemieux
Copy link

lemieux commented Jul 17, 2013

I'm removing the style attribute too. I don't know why jCrop wouldn't return the DOM as it was before.

@ghost
Copy link

ghost commented Aug 29, 2013

I'm signing in just to up this comment:

 tomatohater commented 9 months ago
I doubt this could be classified as "robust", but this has been working for me:

jcrop_api.destroy();
$('.imagePreviewLarge').removeAttr('style');

And many thanks to the topic creator.

zx

@nicoabie
Copy link

+1, removeAttr is old school but it works.

@talofo
Copy link

talofo commented May 5, 2014

This (the removeAttr("style");) worked for me as well. BUT, only on Chrome.
Firefox still doesn't properly destroy, nor the attribute gets removed. :(
Has anyone tested on those two browsers?

@bhardwick
Copy link
Author

My original suggestion, to remove the tag from the DOM and add it again, although not ideal, was working in Firefox. It is not as clean as some of the other solutions though.

@tomatohater
Copy link

@talofo My recent tests show that the destroy/removeAttr method does work in Chrome/FF/Safari/IE11. Are you sure nothing else is bombing out?

I whipped up a barebones example that compares the default behavior with the removeAttr workaround: http://jcrop-issue-46.herokuapp.com/

I do believe that this cleanup would be better off in the .destroy() method itself. But until then...

@talofo
Copy link

talofo commented Jul 15, 2014

No dice. :( It's so frustrating. I have all a crop php plugin based on this, and it keeps showing two images with visibility: visible, instead of one, and it keeps putting the second image with the wrong withs and heights. I'm completely lost on all this code already, with no clear way to solve this.

@rajeshleo
Copy link

It is really frustrating. Took a long way to understand what was happening, Tried to fix, but no use. Please let me know when this issue will be fixed.

@Jasman
Copy link

Jasman commented Feb 13, 2015

work for me

jQuery(function($) {
        function showCoords(c) {
        // variables can be accessed here as
        // c.x, c.y, c.x2, c.y2, c.w, c.h
    };
    function readURL(input) {
        if (input.files && input.files[0]) {
            var reader = new FileReader();
            reader.onload = function(e) {
                //remove current jcrop 
                $('.jcrop-holder').replaceWith('');

                //replace with new image
                $('#avatar-crop').replaceWith('<img id="avatar-crop" src="' + e.target.result + '" width="100%"/>');

                //run jcrop again
                $("#avatar-crop").Jcrop({
                    onSelect: showCoords,
                    onChange: showCoords
                });
            }
            reader.readAsDataURL(input.files[0]);
        }
    }

    // first run jcrop
    $("#avatar-crop").Jcrop({
        onSelect: showCoords,
        onChange: showCoords
    });

    //input file
    $("#user_avatar").change(function() {
        readURL(this);
    });

});

tested: jquery.Jcrop.js v0.9.12

@hustlzp
Copy link

hustlzp commented Apr 12, 2015

+1

4 similar comments
@Narrator
Copy link

+1

@Demnogonis
Copy link

+1

@desoss
Copy link

desoss commented Oct 7, 2015

+1

@incaib
Copy link

incaib commented Oct 7, 2015

+1

@MarcusJT
Copy link

MarcusJT commented Oct 7, 2015

I've given up on Jcrop and am switching to using Cropper instead - play with the demo, it's fantastic!

@oucil
Copy link

oucil commented Oct 12, 2016

+1, p.s. @MarcusJT thanks for the link

@randolf
Copy link

randolf commented Feb 27, 2019

Unfortunately Cropper is a disaster on cell phones (running Opera or Chrome) and pans the image instead of the cropping area when using the cursor keys on a PC (running Opera or Chrome). I found that Jcrop has much better compatibility.

@MarcusJT
Copy link

Strange, CropperJS works perfectly for me in Chrome on Android, I just tried it.

As for cursor keys panning the image rather than the cropping area, that's an implementation decision which surely you could either fork and change, or there may even be a fully supported way to change this (i.e. by configuring something or writing a little code to override the default keypress event handler)

Anyway, I've not needed to crop images since I last posted and I'm unlikely to again, so I'm going to bow out of & unsubscribe from this thread, all the best!

@fmourtaza
Copy link

Hello Folks,

I had to develop an application using a Webcam as well as Upload File along with JCrop - that with toggle options between Cam and the Upload File.

What worked for me was to reset the canvas height and width while Cropping the image - see $('#btnCrop').click

Here is the entire code:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head >
    <title>Upload Image</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.9/js/jquery.Jcrop.min.js"></script>
    <style>
        .center {
            text-align: center;
        }
    </style>
    <script type="text/javascript">
        $(function () {
            //Create variables (in this scope) to hold the Jcrop API and image size
            var jcrop_api, boundx, boundy;

            //#region WebCam
            // Grab elements, create settings, etc.
            let video = document.getElementById('video');

            // Get access to the camera!
            if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
                // Not adding `{ audio: true }` since we only want video now
                navigator.mediaDevices.getUserMedia({ video: true }).then(function (stream) {
                    //video.src = window.URL.createObjectURL(stream);
                    video.srcObject = stream;
                    video.play();
                });
            }

            // Elements for taking the snapshot
            let canvas = document.getElementById('canvas');
            let context = canvas.getContext('2d');

            // Trigger photo take
            document.getElementById("btnCapture").addEventListener("click", function () {

                //Set Ratio
                var ratio;
                var width = 600;//window.innerWidth;
                var height = 600;//window.innerHeight;
                if (video.width > width) {
                    ratio = width / video.width;
                }
                else if (video.height > height) {
                    ratio = height / video.height;
                }
                else {
                    ratio = 1;
                }
                context.drawImage(video, 0, 0, video.width * ratio, video.height * ratio);

                console.log('Video w:' + video.width * ratio);
                console.log('Video h:' + video.height * ratio);

                //Set the canvas to Image1
                $("#Image1")[0].src = canvas.toDataURL();
                $("#Image1").show();
                $("#canvas").hide();

                // destroy Jcrop if it is existed
                if (typeof jcrop_api != 'undefined') {
                    jcrop_api.destroy();
                    jcrop_api = null;
                }

                $('#Image1').Jcrop({
                    onChange: SetCoordinates,
                    onSelect: SetCoordinates
                },
                    function () {
                        // use the Jcrop API to get the real image size
                        var bounds = this.getBounds();
                        boundx = bounds[0];
                        boundy = bounds[1];
                        // Store the Jcrop API in the jcrop_api variable
                        jcrop_api = this;
                    });
            });

            //#endregion WebCam

            //#region FileUpload, Resize, JCrop, Crop & Clear

            $('#FileUpload1').change(function (event) {
                try {
                    var files = event.target.files;
                    var file = files[0];

                    console.log('FileUpload1 Length:' + files.length);

                    if (file) {
                        $('#Image1').hide();
                        var reader = new FileReader();
                        reader.onload = function (e) {
                            //$('#Image1').show();
                            $('#Image1').attr("src", e.target.result);
                        };
                        reader.readAsDataURL($(this)[0].files[0]);

                        //#region Resize & JCrop

                        var reader = new FileReader();
                        // Set the image for the FileReader
                        reader.onload = function (e) {
                            var img = document.createElement("img");
                            img.src = e.target.result;

                            // Create your canvas
                            var canvas = document.createElement("canvas");
                            var ctx = canvas.getContext("2d");
                            ctx.drawImage(img, 0, 0);

                            var MAX_WIDTH = 400;
                            var MAX_HEIGHT = 400;
                            let width = img.width;
                            let height = img.height;

                            console.log('Image w & h:' + width + '-' + height);

                            if (width == 0 && height == 0) {
                                throw new UserException("An internal error occured - please try again or contact your administrator!");
                                return;
                            }

                            // Add the resizing logic
                            if (width > height) {
                                if (width > MAX_WIDTH) {
                                    height *= MAX_WIDTH / width;
                                    width = MAX_WIDTH;
                                }
                            } else {
                                if (height > MAX_HEIGHT) {
                                    width *= MAX_HEIGHT / height;
                                    height = MAX_HEIGHT;
                                }
                            }

                            //Specify the resizing result
                            canvas.width = width;
                            canvas.height = height;

                            console.log('Canvas w & h:' + canvas.width + '-' + canvas.height);

                            var ctx = canvas.getContext("2d");
                            ctx.drawImage(img, 0, 0, width, height);

                            dataurl = canvas.toDataURL(file.type);
                            document.getElementById("Image1").src = dataurl;
                            $('#Image1').show();

                            // destroy Jcrop if it is existed
                            if (typeof jcrop_api != 'undefined') {
                                jcrop_api.destroy();
                                jcrop_api = null;
                            }

                            $('#Image1').Jcrop({
                                onChange: SetCoordinates,
                                onSelect: SetCoordinates
                            },
                                function () {
                                    // use the Jcrop API to get the real image size
                                    var bounds = this.getBounds();
                                    boundx = bounds[0];
                                    boundy = bounds[1];
                                    // Store the Jcrop API in the jcrop_api variable
                                    jcrop_api = this;
                                });

                        };
                        reader.readAsDataURL(file);
                        //#endregion Resize & JCrop
                    }
                }
                catch (err) {
                    alert(err.message);
                }

            });

            $('#btnCrop').click(function () {
                var x1 = $('#imgX1').val();
                var y1 = $('#imgY1').val();
                var width = $('#imgWidth').val();
                var height = $('#imgHeight').val();
                var canvas = $("#canvas")[0];
                var context = canvas.getContext('2d');
                var img = new Image();
                img.onload = function () {
                    canvas.height = height;
                    canvas.width = width;
                    context.drawImage(img, x1, y1, width, height, 0, 0, width, height);
                    $('#imgCropped').val(canvas.toDataURL());
                    $("#capturedImage")[0].src = canvas.toDataURL();
                    document.getElementById("<%=ImgExSrc.ClientID%>").value = canvas.toDataURL();
                    document.getElementById("btnSubmit").disabled = true;
                    $('#btnSubmit').show();
                    $('#lblTermsConditions').show();
                    $('#chkApprove').show();

                    //Reset the canvas height & width
                    console.log('before:' + canvas.height + '-' + canvas.width);
                    canvas.height = 380;
                    canvas.width = 380;
                    console.log('after:' + canvas.height + '-' + canvas.width);
                };
                img.src = $('#Image1').attr("src");
                $("#canvas").hide();
            });

            $('#btnClear').click(function () {
                Clear();
            });

            //#endregion FileUpload, Resize, JCrop, Crop & Clear
        });

        function Clear() {
            $('#Image1').attr('src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==');
            $("div.jcrop-holder").remove();
            $("div.jcrop-tracker").remove();
            $('#btnCrop').hide();
            $('#btnClear').hide();
        }

        function SetCoordinates(c) {
            $('#imgX1').val(c.x);
            $('#imgY1').val(c.y);
            $('#imgWidth').val(c.w);
            $('#imgHeight').val(c.h);
            $('#btnCrop').show();
            $('#btnClear').show();
        };

        function UserException(message) {
            alert(message);
            console.log(message);
        }

    </script>
</head>
<body>
    <form id="form2" >
        <div class="container">
            <div class="jumbotron">
                <h1>Upload Image</h1>
                <p class="lead">This application offer to capture image along with crop functionality using either a Live Camera or an Upload File Control.</p>
            </div>
            <div class="row">
                <div class="col-md-4" style="background-color: lavender;">
                    <div class="center">
                        <p><b>Live Camera</b></p>
                        <video id="video" width="400" height="400" autoplay></video>
                        <br />
                        <input type="button" id="btnCapture" value="Capture" />
                        <br />
                        <br />

                    </div>
                </div>
                <div class="col-md-4" style="background-color: orange;">
                    <div class="center">
                        <p><b>Upload File</b></p>
                        <input type="file" id="FileUpload1" accept=".jpg,.png,.gif" />
                        <br />
                        <br />
                    </div>
                    <table class="table" border="0">
                        <tbody>
                            <tr>
                                <td>
                                    <img id="Image1" src="" style="display: none" />
                                </td>
                                <td>
                                    <canvas id="canvas" height="380" width="380"></canvas>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                    <br />
                    <input type="hidden" name="imgX1" id="imgX1" />
                    <input type="hidden" name="imgY1" id="imgY1" />
                    <input type="hidden" name="imgWidth" id="imgWidth" />
                    <input type="hidden" name="imgHeight" id="imgHeight" />
                    <input type="hidden" name="imgCropped" id="imgCropped" />
                    <div class="align-items-center">
                        <input type="button" id="btnCrop" value="Crop" style="display: none" />
                        <input type="button" id="btnClear" value="Clear" style="display: none" />
                        <br />
                        <br />
                    </div>
                </div>
                <div class="col-md-4" style="background-color: lavender;">
                    <div class="center">
                        <p><b>Captured Image</b></p>
                        <img id="capturedImage" src=""  />
                        <asp:HiddenField runat="server" ID="ImgExSrc" />
                        <br />
                        <br />
                        <input type="checkbox" id="chkApprove" onchange="document.getElementById('btnSubmit').disabled = !this.checked;" style="display: none">
                        <asp:Label ID="lblTermsConditions" runat="server" Style="display: none" Text="I have read and understood the declaration of consent. I agree to the terms and conditions."></asp:Label>
                        <br />
                        <br />
                        <asp:Button ID="btnSubmit" OnClientClick="alert('Submitted successfully!'); return false;" runat="server" Text="Submit" Style="display: none" />
                        <br />
                        <br />
                    </div>
                </div>
            </div>
        </div>
    </form>
</body>
</html>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests