1

Onclick Image We are Providing options for users to upload image from their computer, after user upload image, then we are displaying "Remove" text.... once user click on "Remove", we are deleting the uploaded image....

Requirement :

Before user click on Remove Text , he should't able to upload another image....

it's working for single image....

Issue :

But if there are multiple images , once user upload on 1st image, then its not allowing to upload image on 2nd image.... but this should't happen....

Video showing issue

codepen : https://codepen.io/kidsdial/pen/PgmzOE

Fiddle : https://jsfiddle.net/kidsdial1/5xuLd8yt/

Below is code snippet :

var target;
var imageUrl = "https://i.imgur.com/RzEm1WK.png";

// Json - it includes mask image 
let jsonData = {  
  "layers" : [
    {
      "x" : 0,
      "height" : 612,   
      "layers" : [
        {
          "x" : 0,          
          "y" : 0,         
          "name" : "L2a"
        },
        {
          "x" : 160,         
          "layers" : [
            {
              "x" : 0,            
              "src" : "ax0HVTs.png",
              "y" : 0,              
              "name" : "L2b-1"
            },
            {
              
              "x" : 0,
              "y" : 0,             
              "name" : "L2b-2"
            }
          ],
          "y" : 291,         
          "name" : "user_image_1"
        },
        {
          "x" : 25,         
          "layers" : [
            {
              "x" : 0,             
              "src" : "hEM2kEP.png",
              "y" : 0,             
              "name" : "L2C-1"
            },
            {            
              "x" : 0,
              "y" : 0,            
              "name" : "L2C-2"
            }
          ],
          "y" :22,         
          "name" : "L2"
        }
      ],
      "y" : 0, 
      "width" : 612,   
      "name" : "L1"
    }
  ]
};

$(document).ready(function() {

    // upload image onclick mask image

    $('.container').click(function(e) {

        var res = e.target;
        target = res.id;
        console.log(target);
        if (e.target.getContext) {
            // click only inside Non Transparent part
           // $('.container').css('pointer-events','none');
            var pixel = e.target.getContext('2d').getImageData(e.offsetX, e.offsetY, 1, 1).data;
            
            if (pixel[3] === 255) {
                setTimeout(() => {                
                    $('#fileup').click();                      
                }, 20);
            }
        }
    });

    // Below code will fetch mask image from json file

    function getAllSrc(layers) {
        let arr = [];
        layers.forEach(layer => {
            if (layer.src) {
                arr.push({
                    src: layer.src,
                    x: layer.x,
                    y: layer.y,
                    name: layer.name
                });
            } else if (layer.layers) {
                let newArr = getAllSrc(layer.layers);
                if (newArr.length > 0) {
                    newArr.forEach(({
                        src,
                        x,
                        y,
                        name
                    }) => {
                        arr.push({
                            src,
                            x: (layer.x + x),
                            y: (layer.y + y),
                            name: (name)
                        });
                    });
                }
            }
        });
        return arr;
    }

    function json(data)

    {
        var width = 0;
        var height = 0;

        let arr = getAllSrc(data.layers);

        let layer1 = data.layers;        
        let counter = 0;
        let table = [];

        for (let {
                src,
                x,
                y,
                name
            } of arr) {
            
            var mask = $(".container").mask({
   // icon  : 
                imageUrl: imageUrl,
   // Mask image :
                maskImageUrl: 'https://i.imgur.com/' + src,
                onMaskImageCreate: function(img) {

                    // image positions : 

                    img.css({
                        "position": "absolute",
                        "left": x + "px",
                        "top": y + "px"
                    });

                },
                id: counter
            });
            table.push(mask);
            fileup.onchange = function() {
   //u day code
  $('.container').css('pointer-events','none');
                let mask2 = table[target];
                const newImageLoadedId = mask2.loadImage(URL.createObjectURL(fileup.files[0]));
                
                document.getElementById('fileup').value = "";
                //  Remove image

                $("<br/><span id=\"" + newImageLoadedId + "\" class=\"remove\">Remove image</span>").insertAfter("#fileup");

                $(".remove").click(function(event) {
                $('.container').css('pointer-events','');
                    const canvasId = "canvas#" + event.currentTarget.id;
                    // Delete the image
                    const ctx = $("canvas")[event.currentTarget.id].getContext("2d");
                    ctx.fillStyle = "white"
                    ctx.fillRect(0, 0, 500, 500)
                    // Delete the button
                    $(this).remove();                    
                });

                // Remove image code end here....

            };
            counter++;
        }

    }
    json(jsonData);
}); // end of document ready

// upload image & drag code

(function($) {
    var JQmasks = [];
    $.fn.mask = function(options) {
        // This is the easiest way to have default options.
        var settings = $.extend({
            // These are the defaults.
            maskImageUrl: undefined,
            imageUrl: undefined,
            scale: 1,
            id: new Date().getUTCMilliseconds().toString(),
            x: 0, // image start position
            y: 0, // image start position
            onMaskImageCreate: function(div) {},
        }, options);


        var container = $(this);

        let prevX = 0,
            prevY = 0,
            draggable = false,
            img,
            canvas,
            context,
            image,
            timeout,
            initImage = false,
            startX = settings.x,
            startY = settings.y,
            div;

        container.mousePosition = function(event) {
            return {
                x: event.pageX || event.offsetX,
                y: event.pageY || event.offsetY
            };
        }

        container.selected = function(ev) {
            var pos = container.mousePosition(ev);
            var item = $(".masked-img canvas").filter(function() {
                var offset = $(this).offset()
                var x = pos.x - offset.left;
                var y = pos.y - offset.top;
                var d = this.getContext('2d').getImageData(x, y, 1, 1).data;
                return d[0] > 0
            });

            JQmasks.forEach(function(el) {
                var id = item.length > 0 ? $(item).attr("id") : "";
                if (el.id == id)
                    el.item.enable();
                else el.item.disable();
            });
        };

        container.enable = function() {
            draggable = true;
            $(canvas).attr("active", "true");
            div.css({
                "z-index": 2
            });
        }

        container.disable = function() {
            draggable = false;
            $(canvas).attr("active", "false");
            div.css({
                "z-index": 1
            });
        }

        container.updateStyle = function() {
            return new Promise((resolve, reject) => {
                context.beginPath();
                context.globalCompositeOperation = "source-over";
                image = new Image();
                image.setAttribute('crossOrigin', 'anonymous');
                image.src = settings.maskImageUrl;
                image.onload = function() {
                    canvas.width = image.width;
                    canvas.height = image.height;
                    context.drawImage(image, 0, 0, image.width, image.height);
                    div.css({
                        "width": image.width,
                        "height": image.height
                    });
                    resolve();
                };
            });
        };

        function renderInnerImage() {
            img = new Image();
            img.setAttribute('crossOrigin', 'anonymous');
            img.src = settings.imageUrl;
            img.onload = function() {
                settings.x = settings.x == 0 && initImage ? (canvas.width - (img.width * settings.scale)) / 2 : settings.x;
                settings.y = settings.y == 0 && initImage ? (canvas.height - (img.height * settings.scale)) / 2 : settings.y;
                context.globalCompositeOperation = 'source-atop';
                context.drawImage(img, settings.x, settings.y, img.width * settings.scale, img.height * settings.scale);
                initImage = false;
            };
        }

        // change the draggable image

        container.loadImage = function(imageUrl) {
            console.log("load");
            //if (img)
            // img.remove();
            // reset the code.
            settings.y = startY;
            settings.x = startX;
            prevX = prevY = 0;
            settings.imageUrl = imageUrl;
            initImage = true;
            container.updateStyle().then(renderInnerImage);           
            return settings.id;
        };


        // change the masked Image
        container.loadMaskImage = function(imageUrl, from) {
            canvas = document.createElement("canvas");
            context = canvas.getContext('2d');
            canvas.setAttribute("draggable", "true");
            canvas.setAttribute("id", settings.id);
            settings.maskImageUrl = imageUrl;
            div = $("<div/>", {
                "class": "masked-img"
            }).append(canvas);

            // div.find("canvas").on('touchstart mousedown', function(event)
            div.find("canvas").on('dragstart', function(event) {
                if (event.handled === false) return;
                event.handled = true;
                container.onDragStart(event);
            });

            div.find("canvas").on('touchend mouseup', function(event) {
                if (event.handled === false) return;
                event.handled = true;
                container.selected(event);
            });

            div.find("canvas").bind("dragover", container.onDragOver);
            container.append(div);
            if (settings.onMaskImageCreate)
                settings.onMaskImageCreate(div);
            container.loadImage(settings.imageUrl);
        };
        container.loadMaskImage(settings.maskImageUrl);
        JQmasks.push({
            item: container,
            id: settings.id
        })
        return container;
    };
}(jQuery));
.container 
{
 background: gold;
 position: relative;
 width:612px;
 height:612px;
}

.container img {
   position:absolute;
   top:0;
   bottom:250px;
   left:0;
   right:0;
   margin:auto;
   z-index:999;
}

.masked-img {
 overflow: hidden; 
 position: relative;
}

.pip {
  display: inline-block;
  margin: 10px 10px 0 0;
}
.remove {
  display: block;
  background: #444;
  border: 1px solid black;
  color: white;
  text-align: center;
  cursor: pointer;
}
.remove:hover {
  background: white;
  color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<input id="fileup" name="fileup" type="file" style="display:none" >

<div class="container">
</div>

I used below code to restrict uploading image before click on Remove text :

$('.container').css('pointer-events','none');
$('.container').css('pointer-events','');

Before adding above 2 lines, It was working fine as here : codepen2

  • In some cases, i have more then 2 images.... i need solution dynamically.... –  Apr 17 '19 at 13:44

1 Answers1

0

Since you have a common div for both image holders in your markup, the following statement will select the whole div and disable the events.

$('.container').css('pointer-events','none');

This is what is causing your issue to my understanding since it is not in the second codepen. To correct it you can differentiate between the two image holders by either storing position/name of holder clicked and compare it with the next event to check which one is clicked and act accordingly if it is the other one.

I was able to make some changes to you js code. Now the selected canvas masked is disabled by the ID and only when image is loaded. I also modified the remove button function to reverse the disabling after remove is deleted

var target;
var imageUrl = "https://i.imgur.com/RzEm1WK.png";

// Json - it includes mask image 
let jsonData = {  
  "layers" : [
    {
      "x" : 0,
      "height" : 612,     
      "layers" : [
        {
          "x" : 0,          
          "y" : 0,         
          "name" : "L2a"
        },
        {
          "x" : 160,         
          "layers" : [
            {
              "x" : 0,            
              "src" : "ax0HVTs.png",
              "y" : 0,              
              "name" : "L2b-1"
            },
            {

              "x" : 0,
              "y" : 0,             
              "name" : "L2b-2"
            }
          ],
          "y" : 291,         
          "name" : "user_image_1"
        },
        {
          "x" : 25,         
          "layers" : [
            {
              "x" : 0,             
              "src" : "hEM2kEP.png",
              "y" : 0,             
              "name" : "L2C-1"
            },
            {            
              "x" : 0,
              "y" : 0,            
              "name" : "L2C-2"
            }
          ],
          "y" :22,         
          "name" : "L2"
        }
      ],
      "y" : 0, 
      "width" : 612,      
      "name" : "L1"
    }
  ]
};

$(document).ready(function() {

    // upload image onclick mask image

    $('.container').click(function(e) {

        var res = e.target;
        target = res.id;
        console.log(target);
        if (e.target.getContext) {
            // click only inside Non Transparent part
            var pixel = e.target.getContext('2d').getImageData(e.offsetX, e.offsetY, 1, 1).data;
            if (pixel[3] === 255) {
                setTimeout(() => {
                    $('#fileup').click();
                }, 20);

            }

        }
    });

    // Below code will fetch mask image from json file

    function getAllSrc(layers) {
        let arr = [];
        layers.forEach(layer => {
            if (layer.src) {
                arr.push({
                    src: layer.src,
                    x: layer.x,
                    y: layer.y,
                    name: layer.name
                });
            } else if (layer.layers) {
                let newArr = getAllSrc(layer.layers);
                if (newArr.length > 0) {
                    newArr.forEach(({
                        src,
                        x,
                        y,
                        name
                    }) => {
                        arr.push({
                            src,
                            x: (layer.x + x),
                            y: (layer.y + y),
                            name: (name)
                        });
                    });
                }
            }
        });
        return arr;
    }

    function json(data)

    {
        var width = 0;
        var height = 0;

        let arr = getAllSrc(data.layers);

        let layer1 = data.layers;
        width = layer1[0].width;
        height = layer1[0].height;
        let counter = 0;
        let table = [];

        for (let {
                src,
                x,
                y,
                name
            } of arr) {
            $(".container").css('width', width + "px").css('height', height + "px").addClass('temp');
            if (name.indexOf('mask_') !== -1) {
                var imageUrl1 = imageUrl;
            } else {
                var imageUrl1 = '';
            }
            var mask = $(".container").mask({
            // icon  : 
                imageUrl: imageUrl1,
            // Mask image :
                maskImageUrl: 'https://i.imgur.com/' + src,
                onMaskImageCreate: function(img) {

                    // image positions : 

                    img.css({
                        "position": "absolute",
                        "left": x + "px",
                        "top": y + "px"
                    });

                },
                id: counter
            });
            table.push(mask);
            fileup.onchange = function() {
                $("#"+String(target)).css("pointer-events", "none");
                let mask2 = table[target];
                const newImageLoadedId = mask2.loadImage(URL.createObjectURL(fileup.files[0]));
                document.getElementById('fileup').value = "";

                //  Remove image

                $("<br/><span id=\"" + newImageLoadedId + "\" class=\"remove\">Remove image</span>").insertAfter("#fileup");

                $(".remove").click(function(event) {
                    const canvasId = "canvas#" + event.currentTarget.id;
                    // $(canvasId).css('pointer-events','auto')
                    // Delete the image
                    const ctx = $("canvas")[event.currentTarget.id].getContext("2d");
                    ctx.fillStyle = "white"
                    ctx.fillRect(0, 0, 500, 500)
                    // Delete the button
                    $(this).remove();
                    $(canvasId).css("pointer-events","");

                });

                // Remove image code end here....

            };
            counter++;
        }

    }
    json(jsonData);
}); // end of document ready

// upload image & drag code

(function($) {
    var JQmasks = [];
    $.fn.mask = function(options) {
        // This is the easiest way to have default options.
        var settings = $.extend({
            // These are the defaults.
            maskImageUrl: undefined,
            imageUrl: undefined,
            scale: 1,
            id: new Date().getUTCMilliseconds().toString(),
            x: 0, // image start position
            y: 0, // image start position
            onMaskImageCreate: function(div) {},
        }, options);


        var container = $(this);

        let prevX = 0,
            prevY = 0,
            draggable = false,
            img,
            canvas,
            context,
            image,
            timeout,
            initImage = false,
            startX = settings.x,
            startY = settings.y,
            div;

        container.mousePosition = function(event) {
            return {
                x: event.pageX || event.offsetX,
                y: event.pageY || event.offsetY
            };
        }

        container.selected = function(ev) {
            var pos = container.mousePosition(ev);
            var item = $(".masked-img canvas").filter(function() {
                var offset = $(this).offset()
                var x = pos.x - offset.left;
                var y = pos.y - offset.top;
                var d = this.getContext('2d').getImageData(x, y, 1, 1).data;
                return d[0] > 0
            });

            JQmasks.forEach(function(el) {
                var id = item.length > 0 ? $(item).attr("id") : "";
                if (el.id == id)
                    el.item.enable();
                else el.item.disable();
            });
        };

        container.enable = function() {
            draggable = true;
            $(canvas).attr("active", "true");
            div.css({
                "z-index": 2
            });
        }

        container.disable = function() {
            draggable = false;
            $(canvas).attr("active", "false");
            div.css({
                "z-index": 1
            });
        }

        container.updateStyle = function() {
            return new Promise((resolve, reject) => {
                context.beginPath();
                context.globalCompositeOperation = "source-over";
                image = new Image();
                image.setAttribute('crossOrigin', 'anonymous');
                image.src = settings.maskImageUrl;
                image.onload = function() {
                    canvas.width = image.width;
                    canvas.height = image.height;
                    context.drawImage(image, 0, 0, image.width, image.height);
                    div.css({
                        "width": image.width,
                        "height": image.height
                    });
                    resolve();
                };
            });
        };

        function renderInnerImage() {
            img = new Image();
            img.setAttribute('crossOrigin', 'anonymous');
            img.src = settings.imageUrl;
            img.onload = function() {
                settings.x = settings.x == 0 && initImage ? (canvas.width - (img.width * settings.scale)) / 2 : settings.x;
                settings.y = settings.y == 0 && initImage ? (canvas.height - (img.height * settings.scale)) / 2 : settings.y;
                context.globalCompositeOperation = 'source-atop';
                context.drawImage(img, settings.x, settings.y, img.width * settings.scale, img.height * settings.scale);
                initImage = false;
            };
        }

        // change the draggable image

        container.loadImage = function(imageUrl) {
            console.log("load");
            //if (img)
            // img.remove();
            // reset the code.
            settings.y = startY;
            settings.x = startX;
            prevX = prevY = 0;
            settings.imageUrl = imageUrl;
            initImage = true;
            container.updateStyle().then(renderInnerImage);           
            return settings.id;
        };


        // change the masked Image
        container.loadMaskImage = function(imageUrl, from) {
            canvas = document.createElement("canvas");
            context = canvas.getContext('2d');
            canvas.setAttribute("draggable", "true");
            canvas.setAttribute("id", settings.id);
            settings.maskImageUrl = imageUrl;
            div = $("<div/>", {
                "class": "masked-img"
            }).append(canvas);

            // div.find("canvas").on('touchstart mousedown', function(event)
            div.find("canvas").on('dragstart', function(event) {
                if (event.handled === false) return;
                event.handled = true;
                container.onDragStart(event);
            });

            div.find("canvas").on('touchend mouseup', function(event) {
                if (event.handled === false) return;
                event.handled = true;
                container.selected(event);
            });

            div.find("canvas").bind("dragover", container.onDragOver);
            container.append(div);
            if (settings.onMaskImageCreate)
                settings.onMaskImageCreate(div);
            container.loadImage(settings.imageUrl);
        };
        container.loadMaskImage(settings.maskImageUrl);
        JQmasks.push({
            item: container,
            id: settings.id
        })
        return container;
    };
}(jQuery));

There is some issue with the file selection in codepen due to firestore not loading correctly. Try reloading frame by right clicking out of the yellow part!

Here is the codepen https://codepen.io/icy121/pen/oOqZNy?editors=0110

icy121
  • 131
  • 7
  • Thanks for suggestion, i need solution dynamically, in some cases i have more then 2 images..... if possible please help me with code –  Apr 12 '19 at 06:33
  • Considering you're adding individual canvases for each image with unique counter ID. Add event listeners to them when you create them. Block that canvas with canvasID for future events using your own logic. `$('canvas#'+canvasID).css('pointer-events','none');` That way you'll be able to add even the number of canvases as required – icy121 Apr 12 '19 at 07:15
  • here is an example for 6 images : https://codepen.io/kidsdial/pen/bJRoPg –  Apr 12 '19 at 07:20
  • 1
    I'll try and get back to you! – icy121 Apr 12 '19 at 07:23
  • I added the code.Sorry for the delay, I had a busy week! @vickeycolors Accept the answer if you find it as per you need! :) – icy121 Apr 18 '19 at 09:36
  • first of all , thanks a lot that you took time & helped me in busy schedule.... but once i added your code : `$("#"+String(target)).css("pointer-events", "none"); $(canvasId).css("pointer-events","");` , i cant able to drag the uploaded image.... here is codepen : https://codepen.io/kidsdial/pen/YMaQRv , but if i comment your code, dragging will work : https://codepen.io/kidsdial/pen/PgRjvL –  Apr 18 '19 at 10:54
  • That's because all pointer events are disabled with `$("#"+String(target)).css("pointer-events", "none");` I didn't know that you want drag functionality within. In that case you'll have to remove event listener functions from each canvas when clicked and re-add them when you click remove button! One way is to make the callback function standalone. – icy121 Apr 18 '19 at 11:56
  • i really have no idea on how to make `callback function standalone` , can you please help me with code here : https://codepen.io/kidsdial/pen/YMaQRv –  Apr 18 '19 at 13:26
  • The problem you're facing is due to event being attached to the containerElement. If you block the event on a canvas, the parent will still trigger it since the event is attached to it. If you block event on your parent , all children will be blocked on that event.(Initial problem) See here https://codepen.io/icy121/pen/vMjLdP?editors=0010 Else you'll face the non-draggable issue you're facing rn with the pointer events since parent events is disabled! You should try attaching click events on individual canvas as they are made to upload image and disable them. – icy121 Apr 19 '19 at 05:04
  • is it possible to attach click events dynamically ? because i have many templates as in [link1](http://139.59.24.243/ecom1/site/test/nisharg8images) & [link2](http://139.59.24.243/ecom1/site/test/nisharg9images) –  Apr 24 '19 at 05:28
  • Yes. You can refer to this answer. https://stackoverflow.com/questions/6658752/click-event-doesnt-work-on-dynamically-generated-elements/14533243 Since your canvases are generated at the start only, you can bind a common function to all of them and unbind it from the canvas that is clicked when image is uploaded/changed, and finally bind it again when remove button for that canvas is clicked. – icy121 Apr 24 '19 at 06:26