3

PS: Is it not a research kind of question! I have been trying to do this from very long time.

I am trying to make web based an image editor where user can select multiple cropping area and after selection save/download all the image area. like below.

As of now I discovered two libraries

1.Cropper.JS where is only single selection feature is available.

2.Jcrop where only single selection area restrictions.

I am currently using cropper.Js but it seems impossible for me to make multiple selection cropping. Any help is much appreciated.if any other method/library available in JavaScript, Angular or PHP or reactJS for multiple image area selection and crop and download in one go as in the image below.

enter image description here

As per @Keyhan Answer I am Updating my Jcrop library Code

 <div style="padding:0 5%;">
   <img id="target" src="https://d3o1694hluedf9.cloudfront.net/market-750.jpg">
 </div> 

 <button id="save">Crop it!</button>

<link rel="stylesheet" href="https://unpkg.com/jcrop/dist/jcrop.css">
<script src="https://unpkg.com/jcrop"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

JavaScript

    <script>


    setImage();
    var jcp;
    var jcp;
    Jcrop.load('target').then(img => {
        //You can enable multiple cropping with this line:
        jcp = Jcrop.attach(img, { multi: true });
    });

    // to fix security issue when trying to convert to Data URI
    function setImage() {
        document.getElementById('target').setAttribute('crossOrigin', 'anonymous');
        document.getElementById('target').src = 'https://d3o1694hluedf9.cloudfront.net/market-750.jpg';
    } 


    var link = document.getElementById('save');
    link.onclick = function () {
        //we check if at least one crop is available
        if (jcp.active) {
            var i = 0;
            var fullImg = document.getElementById("target");
            //we are looping cropped areas
            for (area of jcp.crops) {
                i++;
                //creating temp canvas and drawing cropped area on it
                canvas = document.createElement("canvas");
                canvas.setAttribute('width', area.pos.w);
                canvas.setAttribute('height', area.pos.h);
                ctx = canvas.getContext("2d");
                ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
                //creating temp link for saving/serving new image
                temp = document.createElement('a');
                temp.setAttribute('download', 'area' + i + '.jpg');
                temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
                temp.click();
            }
        }
    };

</script>
kate moss
  • 416
  • 1
  • 5
  • 18
  • `Jcrop.attach('target',{ multi: true });` From Jcrop documentation: https://jcrop.com/guide/#setting-options. And from Jcrop examples: https://jcrop.com/examples/custom-widget.html – keyhan Feb 14 '22 at 12:03
  • @keyhan I have already tried this but there no any option of exporting the multiple crop image areas. [Question](https://stackoverflow.com/questions/71046529/jcrop-how-to-upload-image-from-client-side-in-a-canvas) I already pasted where I am getting issue and error – kate moss Feb 14 '22 at 13:27
  • You have x, y, width and height of cropped areas, so you can draw each of them on temp canvas https://www.w3schools.com/tags/canvas_drawimage.asp and finally save canvas to with canvas.toDataURL() and serve it. – keyhan Feb 14 '22 at 15:21

2 Answers2

2

I tried to explain the code with comments:

var jcp;
    Jcrop.load('target').then(img => {
    //You can enable multiple cropping with this line:
    jcp = Jcrop.attach(img,{multi:true});
});
//assuming you have a button with id="save" for exporting cropped areas
var link=document.getElementById('save');
link.onclick = function(){
    //we check if at least one crop is available
    if(jcp.active){
        var i=0;
        var fullImg = document.getElementById("target");
        //we are looping cropped areas 
        for(area of jcp.crops){
            i++;
            //creating temp canvas and drawing cropped area on it
            canvas = document.createElement("canvas");
            canvas.setAttribute('width',area.pos.w);
            canvas.setAttribute('height',area.pos.h);
            ctx = canvas.getContext("2d");
            ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
            //creating temp link for saving/serving new image
            temp = document.createElement('a');
            temp.setAttribute('download', 'area'+i+'.jpg');
            temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));    
            temp.click();
        }
    }
};

EDIT: As you commented it would be nicer if we have local image loader, we can add a file input to our html

<img id="target" />
<br/>
<input type="file" id="imageLoader" name="imageLoader"/><!-- add this for file picker -->
<button id="save">save</button>

and a function to our js to handle it

var jcp;
var save=document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange=function handleImage(e){//handling our image picker <input>:
    var reader = new FileReader();
    reader.onload = function(event){
        img.src = event.target.result;
    }
    reader.readAsDataURL(e.target.files[0]);     
}
save.onclick = function(){
    if(jcp&&jcp.active){
        var i=0;
        for(area of jcp.crops){
            i++;
            canvas = document.createElement("canvas");
            canvas.setAttribute('width',area.pos.w);
            canvas.setAttribute('height',area.pos.h);
            ctx = canvas.getContext("2d");
            ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
            temp = document.createElement('a');
            temp.setAttribute('download', 'area'+i+'.jpg');
            temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));    
            temp.click();
        }
    }
};
Jcrop.load('target').then(img => {
    jcp = Jcrop.attach(img,{multi:true});
});
keyhan
  • 522
  • 1
  • 7
  • Thank you for your answer @keyhan but I am getting an error `Uncaught TypeError: Cannot set properties of null (setting 'onclick')` I have updated my Question with code also, Error is around here `link.onclick = function ()` – kate moss Feb 14 '22 at 18:01
  • 1
    Add a save button (`save`) to your html – keyhan Feb 14 '22 at 18:08
  • thank you :)! a tonn of thanks to save my day, A last doubt how can I upload image client side rather than giving img link? – kate moss Feb 14 '22 at 18:13
  • something like `` where all this Jcrop code will work. – kate moss Feb 14 '22 at 18:35
  • Its another question, you can implement it using this answer: https://stackoverflow.com/questions/10906734/how-to-upload-image-into-html5-canvas#answer-10906961 – keyhan Feb 15 '22 at 07:18
  • I understand but if it can be possible here a little pseudo code will be helpful :) – kate moss Feb 15 '22 at 07:48
  • [Question](https://stackoverflow.com/questions/71332030/how-to-handle-large-image-in-jcrop) can you help me in this question or any suggestion ? – kate moss Mar 04 '22 at 09:53
2

Yes, @keyhan was right <input type="file"> is another question, but still, I am giving you an idea of how to implement Kayhan's code above.

<div>    
<input type="file" id="image-input" accept="image/*">
  <!-- img id name should be "target" as it is also using by Jcrop -->
<img id="target"></img>
</div>

and Now you can put below JavaScript Code just above setImage()

  <script>
    let imgInput = document.getElementById('image-input');
    imgInput.addEventListener('change', function (e) {
        if (e.target.files) {
            let imageFile = e.target.files[0];
            var reader = new FileReader();
            reader.onload = function (e) {
                var img = document.createElement("img");
                img.onload = function (event) {

                    var MAX_WIDTH = 1600;
                    var MAX_HEIGHT = 800;

                    var width = img.width;
                    var height = img.height;

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

                    // Dynamically create a canvas element
                    var canvas = document.createElement("canvas");

                    canvas.width = width;
                    canvas.height = height;
                    // var canvas = document.getElementById("canvas");
                    var ctx = canvas.getContext("2d");

                    // Actual resizing
                    ctx.drawImage(img, 0, 0, width, height);

                    // Show resized image in preview element
                    var dataurl = canvas.toDataURL(imageFile.type);
                    document.getElementById("target").src = dataurl;
                }
                img.src = e.target.result;
            }
            reader.readAsDataURL(imageFile);
        }
    });
</script>
Utsav Upadhyay
  • 415
  • 6
  • 21