3

User click on Mask & upload the image....

Its displaying same image two times, also Edit Text is displaying on image....

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

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

Issue :

Once we click on those buttons, its zooming the outside image, but not the inside image....

Requirement :

So I want to replace the Inside image by outside image & Delete the Inside image , so that zoom feature will work for uploaded image....

enter image description here

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

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>
  • I would suggest to use clip path instead canvas, if you use canvas you would need to render zoom image on canvas and them mask it. – jcubic Apr 30 '19 at 08:18
  • And side note: don't mix event attributes with native document.getElementById and jQuery. It looks horrible. – jcubic Apr 30 '19 at 08:19
  • @jcubic Thanks for suggestion, I can't use clip-path because i have thousands of images , so its not possible to do clip-path for each image.... I have images like [this](http://139.59.24.243/ecom1/site/test/nisharg8images) & [this](http://139.59.24.243/ecom1/site/test/nisharg9images) –  Apr 30 '19 at 09:04
  • NOTE: You have some issues with mask plugin you use absolute `$('.mask canvas');` it will not work if you have more then one mask on the page. I think that you probably will be able to get more answers if you narrow down your problem, you just put all your code into question. Create Minimal Working Example. Maybe just mask plugin and how to zoom the image inside mask on canvas. – jcubic Apr 30 '19 at 16:13

1 Answers1

3

You can replace your whole canvas code with CSS, so you will be able to use transform: scale(2) with image inside mask:

var img = document.querySelector('img');
var zoom = 1;
document.querySelector('#zoom_in').addEventListener('click', function(e) {
  zoom += 0.2;
  img.style.transform = 'scale(' + zoom +')';
});
document.querySelector('#zoom_out').addEventListener('click', function(e) {
  zoom -= 0.2;
  img.style.transform = 'scale(' + zoom +')';
});
.clip {
  -webkit-mask-image: url(https://i.imgur.com/ax0HVTs.png);
  -webkit-mask-repeat: no-repeat;
  mask-image: url(https://i.imgur.com/ax0HVTs.png);
  mask-repeat: no-repeat;
  mask: url(https://i.imgur.com/ax0HVTs.png);
}
<div class="clip">
   <img src="http://placekitten.com/500/300"/>
</div>
<button id="zoom_in">+</button>
<button id="zoom_out">-</button>

Tested only in Chrome, if you want more browsers support you would probably need to investigate what are prefixes and how Cross-Browser code look like (if you can find it you can ask question about "cross browser CSS mask" it one is outdated Cross-browser clipping masks old and there're no good answers).

This article show more info about clip-path and mask https://css-tricks.com/clipping-masking-css/

This solution will not work in every browser (here is support https://caniuse.com/#feat=css-masks) but it may be of some use.

If you would like to make it with canvas it more complex and I don't think this is the right place to ask, you can probably hire someone to update the mask plugin for you to add support for zoom (because it need to be done in zoom plugin, maybe something like scale image on canvas and then apply the mask, Sorry didn't check how the mask plugin exactly work).

EDIT: this should create css file for files in directory:

const {promisify} = require('util');
const fs = require('fs');
const readFileAsync = promisify(fs.readFile);
const statAsync = promisify(fs.stat);
const writeFileAsync = promisify(fs.writeFile);

readFileAsync('./masks').then(async (data) =>
    var css = [];
    for (let file of data) {
        const stat = await statAsync(file);
        if (stat.isFile()) {
            css.push(`.mask_${i} {
               -webkit-mask-image: url(${file});
               -webkit-mask-repeat: no-repeat;
               mask-image: url(${file});
               mask-repeat: no-repeat;
               mask: url(${file});
           }`);
         }
    }
    await writeFileAsync('masks.css', css.join('\n'));
    console.log(`css file written ${css.length} rules`);
});
jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Also if you have lot of images and need to generate mask in JS just create mask css inside JS. – jcubic Apr 30 '19 at 16:05
  • Thanks , but I have thousands of images like [this](http://139.59.24.243/ecom1/site/test/nisharg9images) , it's difficult to use clip path manually for all thousands of images.... Please suggest me any dynamic solution.... –  May 01 '19 at 15:17
  • @vickeycolors just write css in js. – jcubic May 02 '19 at 16:39
  • Thanks, but You included images path statically inside .clip , but I have thousands of images like that , do I need to include image path for every image? If so, it's really not possible.... –  May 03 '19 at 04:55
  • 1
    @vickeycolors you have your mask in you JS code so you need to pick the file by hand in the code, why instead of mask plugin you create jQuery code that add mask with JavaScript. it would be the same as your code. Also you can generate css code from files. You can write simple node.js code that will iterate over all your files and create CSS mask for you. You don't need to write it all in code by hand. – jcubic May 03 '19 at 08:06
  • Thanks for the suggestion, but I don't know about node.js, if possible when you get free time, can you please help me with code. –  May 03 '19 at 15:23
  • @vickeycolors node.js is just JavaScript I need to do the same you would I need to search google for basic staff like read directory etc. to write the code. If you want to be programmer you need to learn how to search and write code in language that you don't know, and it seems you know JS and you don't then you need to learn before you start writing code. – jcubic May 03 '19 at 16:36
  • @vickeycolors here is the node.js code (it's just JavaScript, it's like using JS library) . that create css file for you with `.mask_`. you can put name for the class in filename like 'foo-bar__mask.png` and class would be `foo-bar`, this is left as exercise for the reader. – jcubic May 03 '19 at 16:44
  • @vickeycolors jcubic gave you a very good answer. If you need further help on other tasks you can post a new question on Stackoverflow, but I believe your requirement was very well satisfied. Unluckily if you were assigned some work, you will need to complete it outside of Stackoverflow. – Fabrizio Bertoglio May 07 '19 at 05:35
  • @vickeycolors he is suggesting you to use nodejs for a reason. You can insist on not using nodejs with javascript for 2-3 more years, but you will end up only being outdated and sooner or later have to learn nodejs ... – Fabrizio Bertoglio May 07 '19 at 05:48