tapmodo / Jcrop

Jcrop - The Javascript Image Cropping Engine
https://jcrop.com
Other
4.31k stars 939 forks source link

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

Open bhardwick opened 12 years ago

bhardwick commented 12 years ago

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 commented 12 years ago

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 commented 12 years ago

+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 commented 12 years ago

+1 Add 'visibility' to this list

sjkennedy commented 11 years ago

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

tomatohater commented 11 years ago

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

jcrop_api.destroy();
$('.imagePreviewLarge').removeAttr('style');
tomaszkomin commented 11 years ago

That is working for me too jcrop_api.destroy(); $('.imagePreviewLarge').removeAttr('style'); thanks a lot

bsasikiran commented 11 years ago

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

mattolson commented 11 years ago

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

bios42eth commented 11 years ago

+1

evanbeard commented 11 years ago

+1

lemieux commented 11 years ago

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

ghost commented 11 years ago

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 commented 10 years ago

+1, removeAttr is old school but it works.

talofo commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

@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 commented 10 years ago

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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

+1

Narrator commented 9 years ago

+1

Demnogonis commented 9 years ago

+1

desoss commented 9 years ago

+1

incaib commented 9 years ago

+1

MarcusJT commented 9 years ago

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

oucil commented 8 years ago

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

randolf commented 5 years ago

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 commented 5 years ago

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 commented 3 years ago

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>