Search code examples
javascriptjqueryhtmlzooming

Zooming one image affects the other image


Background :

We are displaying two Masks inside container....

User click on both Masks & upload the image....

Now Edit Text is displaying on both images....

Once user click on Edit Text, we are displaying pop up box....

There we can see Zoomin & Zoomout buttons are displaying....

Steps to Reproduce the Issue :

1.Click on Zoomin for 1st image 5 times....

2.Now click on Zoomin for 2nd image....

Zoom value of 1st image is now equal to 2nd image....

But if we zoom one image, it should't change zoom value of other image....

Here is 16 seconds Video to show the issue

enter image description here

Requirement :

I can solve the issue statically by using multiple variables for each images....

but I need solution dynamically as i am using only one variable for zoom_in and zoom_out called scale....

Here is Codepen

Below is Snippet :

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

    let jsonData = {
        "layers": [{
            "x": 0,
            "height": 612,
            "layers": [{
                "x": 160,
                "src": "ax0HVTs.png",
                "y": 291,
                "height": 296,
                "width": 429,
                "name": "mask_1"
            },
                {
                    "x": 25,
                    "src": "hEM2kEP.png",
                    "height": 324,
                    "width": 471,
                    "y": 22,
                    "name": "mask_2"
                }
            ],
            "y": 0,
            "width": 612
        }]
    };

    const containerElement = $('#container');
    const fileUp = $('#fileup');

    $(function() {

        // Upload image onclick mask image

        containerElement.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);
                }
            }
        });

        // Fetch mask images from json file - IGNORE this code

        function getAllSrc(layers) {
            let arr = [];
            layers.forEach(layer => {
                if (layer.src) {
                    arr.push({
                        src: layer.src,
                        x: layer.x,
                        y: layer.y,
                        height: layer.height,
                        width: layer.width,
                        name: layer.name
                    });
                } else if (layer.layers) {
                    let newArr = getAllSrc(layer.layers);
                    if (newArr.length > 0) {
                        newArr.forEach(({
                                            src,
                                            x,
                                            y,
                                            height,
                                            width,
                                            name
                                        }) => {
                            arr.push({
                                src,
                                x: (layer.x + x),
                                y: (layer.y + y),
                                height,
                                width,
                                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 = [];

            // container dimensions
            containerElement.css('width', width + "px").css('height', height + "px").addClass('temp');
            //end

            for (let {
                src,
                x,
                y,
                name
            } of arr) {

                //Get Height and width of mask image [ edit button ]
                var ImagePosition = arr;
                //code end

                var mask = $(".container").mask({
                    imageUrl: imageUrl,

                    // Fetch Mask images
                    maskImageUrl: 'https://i.imgur.com/' + src,
                    // end

                    onMaskImageCreate: function(img) {
                        // Mask image positions
                        img.css({
                            "position": "absolute",
                            "left": x + "px",
                            "top": y + "px"
                        });
                        // end

                    },
                    id: counter
                });
                table.push(mask);
                fileup.onchange = function() {

                    let mask2 = table[target];
                    const imgView = URL.createObjectURL(fileup.files[0]);
                    const newImageLoadedId = mask2.loadImage(URL.createObjectURL(fileup.files[0]));
                    document.getElementById('fileup').value = "";

                    if (($(".masked-img" + newImageLoadedId + ' #renderImage').length) === 0) {
                        $('.masked-img' + newImageLoadedId).append("<img id='renderImage' style='width: 300px' src=" + imgView + ">");
                    } else {
                        $('#renderImage').attr('src', imgView);
                    }

                    //  Edit image - IGNORE this code

                    if ($(".masked-img" + newImageLoadedId).length === 1) {
                        $("<span class=\"pip pip" + newImageLoadedId + "\">" +
                            "<a onclick='document.getElementById(\"dark" + newImageLoadedId + "\").style.display=\"block\";'><span class=\"edit edit" + newImageLoadedId + "\" >Edit </span></a>" +
                            "</span>").insertAfter(".masked-img" + newImageLoadedId).css({
                            "left": ImagePosition[newImageLoadedId].x + (ImagePosition[newImageLoadedId].width / 2) + "px",
                            "top": ImagePosition[newImageLoadedId].y + (ImagePosition[newImageLoadedId].height / 2) + "px"
                        });;
                        $("<div id=\'dark" + newImageLoadedId + "\' class=\'dark_content\'>" +
                            $('#demoTemplate').html() +
                            "<a href=\"javascript:void(0)\" onclick=\"document.getElementById(\'dark" + newImageLoadedId + "\').style.display=\'none\'\">Close</a>" + "</div>").appendTo(".pip" + newImageLoadedId).css({
                            "left": $('.edit' + newImageLoadedId).width() + 2 + "px",
                            "top": "0px"
                        });
                    }
                    //  end
                };
                counter++;
            }
        }
        json(jsonData);
    }); // end of function

    // Image 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.getImagePosition = function() {
                return {
                    x: settings.x,
                    y: settings.y,
                    scale: settings.scale
                };
            };

            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;
                    // console.log(image.src);
                    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 = $('#renderImage'); // new Image()
                img = new Image();
                img.setAttribute('crossOrigin', 'anonymous');
                img.src = settings.imageUrl;
                // console.log(image.src);
                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");
                settings.y = startY;
                settings.x = startX;
                // console.log(settings.y);
                // console.log(settings.x);
                prevX = prevY = 0;
                settings.imageUrl = imageUrl;
                // console.log(settings.imageUrl);
                initImage = true;
                container.updateStyle().then(renderInnerImage);
                // sirpepole  Add this
                return settings.id;
            };

            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
            });
            // Edit image
            div.addClass('masked-img' + settings.id);
            // end
            return container;
        };
    }(jQuery));

    // zoom

    var angle = 0;
    var scale = 1;

    function zoom_in(data) {
        var getParent = data.parentElement.parentElement.parentElement;
        var getId = getParent.id.substring(getParent.id.length - 1);
        console.log(getId);
        scale += .25;
        $('.masked-img' + getId + ' #renderImage').css({
            transform: 'scale('+ scale +')'
        });
    }

    function zoom_out(data) {
        var getParent = data.parentElement.parentElement.parentElement;
        var getId = getParent.id.substring(getParent.id.length - 1);
        console.log(getId);
        scale -= .25;
        $('.masked-img' + getId + ' #renderImage').css({
            transform: 'scale('+ scale +')'
        });
    }
.container {
        background: silver;
        position: relative;
    }

    .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: 0;
        position: absolute;
    }

    .edit {
        display: block;
        background: #444;
        border: 1px solid black;
        color: white;
        text-align: center;
        cursor: pointer;
        position: absolute;
        z-index: 3;
    }

    .edit:hover {
        background: white;
        color: black;
        position: absolute;
        z-index: 3;
    }

    .dark_content {
        display: none;
        position: relative;
        top: 25%;
        left: 25%;
        width: 250px;
        height: 250px;
        padding: 16px;
        border: 16px solid orange;
        background-color: white;
        z-index: 1002;
        overflow: auto;
    }
<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 id="container"class="container">
</div>

<template id='demoTemplate'>
   <span>
      <div class="btn-group">
         <button type="button" class="js-zoom-in" onclick="zoom_in(this)">Zoom In</button>
         <button type="button" class="js-zoom-out" onclick="zoom_out(this)">Zoom Out</button>
      </div>
      <img id="image" src ="" style ="display:none">
   </span>
</template>


Solution

  • The problem is that you use the same var scale every time you call the functions, and every time the scale has the previous value assigned to it. So you could use an array of different scales for each element.

    Create an empty array:

    var scales = [];
    

    Check if the var (for this element) exists already when you call the functions:

    if (scales[getId] === undefined) {
        scales[getId] = 1.25;
    } else {
        scales[getId] += 0.25;  
    }
    

    and

    if (scales[getId] === undefined) {
        scales[getId] = 0.75;
    } else {
        scales[getId] -= 0.25;  
    }
    

    Full code:

    var target;
    const imageUrl = "https://i.imgur.com/RzEm1WK.png";
    
    let jsonData = {
      "layers": [{
        "x": 0,
        "height": 612,
        "layers": [{
            "x": 160,
            "src": "ax0HVTs.png",
            "y": 291,
            "height": 296,
            "width": 429,
            "name": "mask_1"
          },
          {
            "x": 25,
            "src": "hEM2kEP.png",
            "height": 324,
            "width": 471,
            "y": 22,
            "name": "mask_2"
          }
        ],
        "y": 0,
        "width": 612
      }]
    };
    
    const containerElement = $('#container');
    const fileUp = $('#fileup');
    
    $(function() {
    
      // Upload image onclick mask image
    
      containerElement.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);
          }
        }
      });
    
      // Fetch mask images from json file - IGNORE this code
    
      function getAllSrc(layers) {
        let arr = [];
        layers.forEach(layer => {
          if (layer.src) {
            arr.push({
              src: layer.src,
              x: layer.x,
              y: layer.y,
              height: layer.height,
              width: layer.width,
              name: layer.name
            });
          } else if (layer.layers) {
            let newArr = getAllSrc(layer.layers);
            if (newArr.length > 0) {
              newArr.forEach(({
                src,
                x,
                y,
                height,
                width,
                name
              }) => {
                arr.push({
                  src,
                  x: (layer.x + x),
                  y: (layer.y + y),
                  height,
                  width,
                  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 = [];
    
        // container dimensions
        containerElement.css('width', width + "px").css('height', height + "px").addClass('temp');
        //end
    
        for (let {
            src,
            x,
            y,
            name
          } of arr) {
    
          //Get Height and width of mask image [ edit button ]
          var ImagePosition = arr;
          //code end
    
          var mask = $(".container").mask({
            imageUrl: imageUrl,
    
            // Fetch Mask images
            maskImageUrl: 'https://i.imgur.com/' + src,
            // end
    
            onMaskImageCreate: function(img) {
              // Mask image positions
              img.css({
                "position": "absolute",
                "left": x + "px",
                "top": y + "px"
              });
              // end
    
            },
            id: counter
          });
          table.push(mask);
          fileup.onchange = function() {
    
            let mask2 = table[target];
            const imgView = URL.createObjectURL(fileup.files[0]);
            const newImageLoadedId = mask2.loadImage(URL.createObjectURL(fileup.files[0]));
            document.getElementById('fileup').value = "";
    
            if (($(".masked-img" + newImageLoadedId + ' #renderImage').length) === 0) {
              $('.masked-img' + newImageLoadedId).append("<img id='renderImage' style='width: 300px' src=" + imgView + ">");
            } else {
              $('#renderImage').attr('src', imgView);
            }
    
            //  Edit image - IGNORE this code
    
            if ($(".masked-img" + newImageLoadedId).length === 1) {
              $("<span class=\"pip pip" + newImageLoadedId + "\">" +
                "<a onclick='document.getElementById(\"dark" + newImageLoadedId + "\").style.display=\"block\";'><span class=\"edit edit" + newImageLoadedId + "\" >Edit </span></a>" +
                "</span>").insertAfter(".masked-img" + newImageLoadedId).css({
                "left": ImagePosition[newImageLoadedId].x + (ImagePosition[newImageLoadedId].width / 2) + "px",
                "top": ImagePosition[newImageLoadedId].y + (ImagePosition[newImageLoadedId].height / 2) + "px"
              });;
              $("<div id=\'dark" + newImageLoadedId + "\' class=\'dark_content\'>" +
                $('#demoTemplate').html() +
                "<a href=\"javascript:void(0)\" onclick=\"document.getElementById(\'dark" + newImageLoadedId + "\').style.display=\'none\'\">Close</a>" + "</div>").appendTo(".pip" + newImageLoadedId).css({
                "left": $('.edit' + newImageLoadedId).width() + 2 + "px",
                "top": "0px"
              });
            }
            //  end
          };
          counter++;
        }
      }
      json(jsonData);
    }); // end of function
    
    // Image 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.getImagePosition = function() {
          return {
            x: settings.x,
            y: settings.y,
            scale: settings.scale
          };
        };
    
        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;
            // console.log(image.src);
            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 = $('#renderImage'); // new Image()
          img = new Image();
          img.setAttribute('crossOrigin', 'anonymous');
          img.src = settings.imageUrl;
          // console.log(image.src);
          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");
          settings.y = startY;
          settings.x = startX;
          // console.log(settings.y);
          // console.log(settings.x);
          prevX = prevY = 0;
          settings.imageUrl = imageUrl;
          // console.log(settings.imageUrl);
          initImage = true;
          container.updateStyle().then(renderInnerImage);
          // sirpepole  Add this
          return settings.id;
        };
    
        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
        });
        // Edit image
        div.addClass('masked-img' + settings.id);
        // end
        return container;
      };
    }(jQuery));
    
    // zoom
    
    var angle = 0;
    var scale = 1;
    
    var scales = [];
    
    function zoom_in(data) {
      var getParent = data.parentElement.parentElement.parentElement;
      var getId = getParent.id.substring(getParent.id.length - 1);
    
      console.log(getId);
    
      if (scales[getId] === undefined) {
        scales[getId] = 1.25;
      } else {
        scales[getId] += 0.25;
      }
    
      $('.masked-img' + getId + ' #renderImage').css({
        transform: 'scale(' + scales[getId] + ')'
      });
    }
    
    function zoom_out(data) {
      var getParent = data.parentElement.parentElement.parentElement;
      var getId = getParent.id.substring(getParent.id.length - 1);
      console.log(getId);
    
      if (scales[getId] === undefined) {
        scales[getId] = 0.75;
      } else {
        scales[getId] -= 0.25;
      }
    
      $('.masked-img' + getId + ' #renderImage').css({
        transform: 'scale(' + scales[getId] + ')'
      });
    }
    .container {
      background: silver;
      position: relative;
    }
    
    .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: 0;
      position: absolute;
    }
    
    .edit {
      display: block;
      background: #444;
      border: 1px solid black;
      color: white;
      text-align: center;
      cursor: pointer;
      position: absolute;
      z-index: 3;
    }
    
    .edit:hover {
      background: white;
      color: black;
      position: absolute;
      z-index: 3;
    }
    
    .dark_content {
      display: none;
      position: relative;
      top: 25%;
      left: 25%;
      width: 250px;
      height: 250px;
      padding: 16px;
      border: 16px solid orange;
      background-color: white;
      z-index: 1002;
      overflow: auto;
    }
    <input id="fileup" name="fileup" type="file" style="display:none">
    
    <div id="container" class="container">
    </div>
    
    <template id='demoTemplate'>
    			<span>
    				<div class="btn-group">
    					<button type="button" class="js-zoom-in" onclick="zoom_in(this)">Zoom In</button>
    					<button type="button" class="js-zoom-out" onclick="zoom_out(this)">Zoom Out</button>
    				</div>
    				<img id="image" src ="" style ="display:none">
    			</span>
    		</template>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js">
    </script>