Zoom the user uploaded image onclick button

2019-06-15 08:33发布

问题:

Here is the basic code for Zoom in & Zoom out: https://codepen.io/kidsdial/pen/ROBpgM,

Now I tried to integrate that code in my Project.

Background:

  1. User clicks on Mask & upload their own image on the mask.

  2. Once image uploaded, Edit Text is displaying on the image.

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

  4. Zoom in & Zoom out button displaying.

Issue:

When we click on that button, zoom is not happening for user uploaded image.

Video link

Codepen : https://codepen.io/kidsdial/pen/PgxegO

Fiddle : https://jsfiddle.net/kidsdial1/nhswfjr3/

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 newImageLoadedId = mask2.loadImage(URL.createObjectURL(fileup.files[0]));
                document.getElementById('fileup').value = "";

                //  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;
                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");
            settings.y = startY;
            settings.x = startX;
            prevX = prevY = 0;
            settings.imageUrl = 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 

//document.getElementById("img").src = json(data);

var angle = 0;
var scale = 1;
var $img = $('#image');

$img.on('transform', function() {
    $img.css('transform', `rotate(${angle}deg) scale(${scale})`);
});

$('.js-zoom-in').on('click', function() {
    scale += 0.25;
    if (scale == 2.25) {
        scale = 2;
    };
    $img.trigger('transform');
});

$('.js-zoom-out').on('click', function() {
    scale -= 0.25;
    if (scale == 0) {
        scale = 0.25;
    }
    $img.trigger('transform');
});
.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">Zoom In</button>
         <button type="button" class="js-zoom-out">Zoom Out</button>         
      </div>
      <img id="image" src ="" style ="display:none">             
   </span>
</template>

Note: I will give 150 bounty points if someone gives me a solution.

回答1:

Have a try with this code. @PranavNutalapati helped me figure this out.

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');
let mask;

$(function () {

    // Upload image on the click mask image

    containerElement.click(function (e) {
        var res = e.target;
        target = res.id;
        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: 'http://i.imgur.com/' + src,
                // end

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

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

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

                // Edit image - IGNORE this code

                if ($(".masked-img" + newImageLoadedId).length === 1) {
                    const span = $("<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"
                    });
                    span.attr('data-id', newImageLoadedId)
                    $("<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++;
        }
        return mask;
    }
    mask = json(jsonData);
}); // end of function

// Image code

(function ($) {
    window.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) {},
            rotate: 0,
        }, options);

        // Create the image properties
        settings.maskImage = new Image
        settings.image = new Image

        // set the cross-origin attributes
        settings.maskImage.setAttribute('crossOrigin', 'anonymous');
        settings.image.setAttribute('crossOrigin', 'anonymous');

        settings.maskImage.onload = function () {
            // once the mask is loaded, load the image
            container.loadImage(settings.imageUrl)
        }

        settings.image.onload = function () {
            // once the image is loaded, render to canvas
            container.drawMask()
            container.drawImage()
        }

        var container = $(this);

        let prevX = 0,
            prevY = 0,
            draggable = false,
            img,
            canvas,
            context,
            image,
            timeout,
            initImage = false,
            startX = settings.x,
            startY = settings.y,
            scale = settings.scale,
            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.zoom = function (delta) {
            settings.scale += delta;
            settings.scale = Math.min(2, Math.max(0, settings.scale))
            context.clearRect(0, 0, canvas.width, canvas.height);
            container.drawMask()
            container.drawImage();
        }

        container.rotate = function (rotation) {
            settings.rotate += rotation * Math.PI / 180;
            console.log('Rotation', settings.rotate);
            context.clearRect(0, 0, canvas.width, canvas.height);
            container.drawMask()
            container.drawImage();
        }

        container.drawMask = function () {
            canvas.width = settings.maskImage.width;
            canvas.height = settings.maskImage.height;
            context.save();
            context.beginPath();
            context.globalCompositeOperation = "source-over";
            // draw the masked image after scaling
            context.drawImage(settings.maskImage, 0, 0, settings.maskImage.width, settings.maskImage
                .height);
            context.restore()
        };

        container.drawImage = function () {
            const img = settings.image

            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.save();
            context.translate(settings.x + img.width / 2, settings.y + img.height / 2);
            context.rotate(settings.rotate);
            context.scale(settings.scale, settings.scale);
            context.translate(-(settings.x + img.width / 2), -(settings.y + img.height / 2));
            let width = img.width, height = img.height;
            context.drawImage(img, settings.x, settings.y, width, height);
            context.restore();
            initImage = false;
        }

        // change the draggable image

        container.loadImage = function (imageUrl) {
            console.log("selected image, loading");
            settings.y = startY;
            settings.x = startX;
            prevX = prevY = 0;

            initImage = true;

            settings.image.src = imageUrl; // CHANGED

            // sirpepole Add this
            return settings.id;
        };

        container.onDragStart = function(evt) {
            console.log('Draw started');
            if (evt.target.getContext) {
                var pixel = evt.target.getContext('2d').getImageData(evt.offsetX, evt.offsetY, 1, 1).data;

                $(canvas).attr("active", "true");
                container.selected(evt);
                prevX = evt.clientX;
                prevY = evt.clientY;
                var img = new Image();
                evt.originalEvent.dataTransfer.setDragImage(img, 10, 10);
                evt.originalEvent.dataTransfer.setData('text/plain', 'anything');

            }
        };

        container.onDragOver = function(evt) {
            console.log('Drag over');

            if (evt.target.getContext) {
                var pixel = evt.target.getContext('2d').getImageData(evt.offsetX, evt.offsetY, 1, 1).data;
                if (pixel[3] === 255) {
                    if (draggable && $(canvas).attr("active") === "true") {
                        var x = settings.x + evt.clientX - prevX;
                        var y = settings.y + evt.clientY - prevY;
                        if (x == settings.x && y == settings.y)
                            return; // position has not changed
                        settings.x += evt.clientX - prevX;
                        settings.y += evt.clientY - prevY;
                        prevX = evt.clientX;
                        prevY = evt.clientY;
                        clearTimeout(timeout);
                        timeout = setTimeout(function() {
                            container.drawMask();
                            container.drawImage();
                        }, 1);
                    }
                } else {
                    evt.stopPropagation();
                    return false;
                }
            }
        };

        container.loadMaskImage = function (imageUrl, from) {
            console.log('loading mask image from', imageUrl, from)
            canvas = document.createElement("canvas");
            context = canvas.getContext('2d');
            canvas.setAttribute("draggable", "true");
            canvas.setAttribute("id", settings.id);
            // settings.maskImageUrl = imageUrl;
            settings.maskImage.src = imageUrl // CHANGED

            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);
            // Moved this to the settings.maskImage.onload
        };
        container.loadMaskImage(settings.maskImageUrl);
        JQmasks.push({
            item: container,
            id: settings.id
        })
        // Edit image
        div.addClass('masked-img' + settings.id);
        div.attr('data-id', settings.id);
        // ends
        return container;
    };
}(jQuery));

// Zoom

//document.getElementById("img").src = json(data);

function zoom_in(button) {
    const id = $(button).parents('.pip').attr('data-id')
    JQmasks[id].item.zoom(0.1);
}

function zoom_out(button) {
    const id = $(button).parents('.pip').attr('data-id')
    JQmasks[id].item.zoom(-0.1);
}

// Rotate

function rotate_right(button) {
    const id = $(button).parents('.pip').attr('data-id')
    JQmasks[id].item.rotate(20);
}

function rotate_left(button) {
    const id = $(button).parents('.pip').attr('data-id')
    JQmasks[id].item.rotate(-20);
}
.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>
            <button type="button" class="js-rotate-right" onclick="rotate_right(this)">Rotate Right</button>
            <button type="button" class="js-rotate-left" onclick="rotate_left(this)">Rotate Left</button>
        </div>
        <img id="image" src="" style="display:none">
    </span>
</template>

Explanation

I have added a data attribute id to differentiate both the image's buttons.

Then, added a function to handle the button clicks (zoom_in and zoom_out namely).

function zoom_in(button) {
    const id = $(button).parents('.pip').attr('data-id') // to retrieve the id
    JQmasks[id].item.zoom(0.1); // to zoom the correct image
}

function zoom_out(button) {
    const id = $(button).parents('.pip').attr('data-id')
    JQmasks[id].item.zoom(-0.1);
}

Now, we need to handle the zoom functionality. For that, we need to create a new function zoom within the container.

container.zoom = function (delta) {
    settings.scale += delta; // increase / decrease the scale
    settings.scale = Math.min(2, Math.max(0, settings.scale)) // limiting the range of the scale from 0 to 2
    context.clearRect(0, 0, canvas.width, canvas.height); // clearing the whole canvas
    container.drawMask(); // drawing the bow
    container.drawImage(); // drawing the scaled image
}

We are only updating the scale property, so we need to make use of it while drawing it. So within the drawImage() function

container.drawImage = function () {
    const img = settings.image

    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.save();
    context.translate(settings.x + img.width / 2, settings.y + img.height / 2); // translating the canvas to center of the image
    context.scale(settings.scale, settings.scale); // scaling the canvas
    context.translate(-(settings.x + img.width / 2), -(settings.y + img.height / 2)); // translating the canvas back to it's original position
    let width = img.width, height = img.height;
    context.drawImage(img, settings.x, settings.y, width, height);
    context.restore();
    initImage = false;
}

Update 1 - Added functionality for the user to rotate the image

Explanation

Starting with handling the button click.

function rotate_right(button) {
    const id = $(button).parents('.pip').attr('data-id') // getting the id of the image
    JQmasks[id].item.rotate(20); // rotating it by 20deg
}

function rotate_left(button) {
    const id = $(button).parents('.pip').attr('data-id')
    JQmasks[id].item.rotate(-20);
}

Now, inside the container, a new function rotate() has been added to handle the rotation

container.rotate = function (rotation) {
    settings.rotate += rotation * Math.PI / 180; // converting degree to radians and adding it to the previous rotation angle
    console.log('Rotation', settings.rotate);
    context.clearRect(0, 0, canvas.width, canvas.height); // clearing the canvas
    container.drawMask(); // re-drawing the bow
    container.drawImage(); // re-drawing the image
}

Here we are only updating the rotation property. So, while drawing it we need to use that. For that inside the drawImage() function

context.save();
context.translate(settings.x + img.width / 2, settings.y + img.height / 2); // translating the canvas to the center of the image
context.rotate(settings.rotate); // rotating the image
context.scale(settings.scale, settings.scale); // scaling the image
context.translate(-(settings.x + img.width / 2), -(settings.y + img.height / 2)); // translating the image back to it's original position
let width = img.width, height = img.height;
context.drawImage(img, settings.x, settings.y, width, height);
context.restore();


回答2:

You have some scope issues, and some other stuff. I tried to comment as I went. I also fixed issues with missing and extra semi-colons.

I created a global object myApp. to hold the stuff instead of a large number of global variables.

Usability: the "+" asks for something to upload but it is not clear (to me) what right off, users may note that also - but that is opinion not fact. I do not see those "edit" or anything like that so your zoom perhaps is wrapped in that lost code? I have no idea there.

Now the QUESTION: zoom on the image - your "upload" is bleeding through, you have duplicate id on the canvas. I do not know enough about what you are attempting to do to get this better.

var myApp = myApp || {};
myApp.target = {};
myApp.imageUrl = "https://i.imgur.com/RzEm1WK.png";
myApp.jsonData = {
  "layers": [{
    "x": 0,
    "height": 612,
    "layers": [{
        "x": 160,
        "layers": [{
          "x": 0,
          "src": "ax0HVTs.png",
          "y": 0,
          "height": 296,
          "width": 429,
          "name": "L2b-1"
        }],
        "y": 291,
        "name": "user_image_1"
      },
      {
        "x": 25,
        "layers": [{
          "x": 0,
          "src": "hEM2kEP.png",
          "height": 324,
          "width": 471,
          "y": 0,
          "name": "L2C-1"
        }],
        "y": 22,
        "name": "L2"
      }
    ],
    "y": 0,
    "width": 612,
    "name": "L1"
  }]
};


// Below code will fetch mask images 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,
        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 = [];
  for (let {
      src,
      x,
      y,
      name
    } of arr) {
    let csize = {
      'width': width + "px",
      'height': height + "px"
    };
    $(".container")
      .css(csize)
      .addClass('temp');
    //Get Height and width of mask image [ edit button ]
    var ImagePosition = arr;
    //code end
    var mask = $(".container").mask({
      imageUrl: myApp.imageUrl,
      // where is src defined?
      maskImageUrl: 'https://i.imgur.com/' + src,
      onMaskImageCreate: function(img) {
        // Mask image positions
        img.css({
          "position": "absolute",
          "left": x + "px",
          "top": y + "px"
        });
        // code end
      },
      id: counter
    });
    table.push(mask);
    fileup.onchange = function() {
      let mask2 = table[myApp.target];
      const newImageLoadedId = mask2.loadImage(URL.createObjectURL(fileup.files[0]));
      document.getElementById('fileup').value = "";
      //  Edit image
      let mskimg = $(".masked-img" + newImageLoadedId);
      if (mskimg.length === 1) {
        let mySpan = $("<span class='pip'></span>");
        mySpan.addClass("pip" + newImageLoadedId);
        let darkId = "dark" + newImageLoadedId;
        let myA = $("<a onclick='document.getElementById('" + darkId + "').style.display=\"block\";'></a>");
        let linkSpan = $("<span class='edit'>Edit</span>")
          .addClass("edit" + newImageLoadedId);
        myA.append(linkSpan);
        mySpan.append(myA)
          .insertAfter(mskimg)
          .css({
            "left": ImagePosition[newImageLoadedId].x + (ImagePosition[newImageLoadedId].width / 2) + "px",
            "top": ImagePosition[newImageLoadedId].y + (ImagePosition[newImageLoadedId].height / 2) + "px"
          });
        $("<div id='" + darkId + "' class='dark_content'></div>").append($('#demoTemplate').html() +
            "<a href='javascript:void(0)' onclick=\"document.getElementById('" + darkId + "').style.display='none'\">Close</a>")
          .appendTo(".pip" + newImageLoadedId)
          .css({
            "left": $('.edit' + newImageLoadedId).width() + 2 + "px",
            "top": "0px"
          });
      }
      // Edit code end here....        
    };
    counter++;
  }
}

$(function() {
  // below code will upload image onclick mask image
  $('.container').click(function(e) {
    var res = e.target;
    myApp.target = res.id;
    console.log("target:", myApp.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);
      }
    }
  });

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

// jq plugin 

(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;
        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);
      // sirpepole  Add this
      return settings.id;
    };

    // change the masked Image
    container.loadMaskImage = function(imageUrl, from) {
      canvas = document.createElement("canvas");
      context = canvas.getContext('2d');
      canvas.setAttribute("draggable", "true");
      // invalid to have same id
      let cvl = $('canvas').length;
      console.log("cvl:",cvl,"sid:",settings.id);
      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);
    // code end
    return container;
  };
}(jQuery));

//Zoom in , Zoom out , Rotate code
// assume change data to myApp.jsonData
// in the template so, it does not find it? wrong ID? terrible id
//document.getElementById("img").src = json(data);
//document.getElementById("template-image").src = json(myApp.jsonData);

// probably should namespace these
myApp.angle = 0;
myApp.scale = 1;
myApp.$img = $('.template-image').eq(0);
// moved from above, use the revised id (class)
myApp.$img.prop("src", json(myApp.jsonData));
myApp.$img.on('transform', function() {
  $(this).css('transform', `rotate(${myApp.angle}deg) scale(${myApp.scale})`);
});

$('.js-rotate-right').on('click', function() {
  myApp.angle += 15;
  myApp.$img.trigger('transform');
});

$('.js-rotate-left').on('click', function() {
  myApp.angle -= 15;
  myApp.$img.trigger('transform');
});

$('.js-zoom-in').on('click', function() {
  myApp.scale += 0.25;
  if (myApp.scale == 2.25) {
    myApp.scale = 2;
  }
  myApp.$img.trigger('transform');
});

$('.js-zoom-out').on('click', function() {
  myApp.scale -= 0.25;
  if (myApp.scale == 0) {
    myApp.scale = 0.25;
  }
  myApp.$img.trigger('transform');
});
.container {
  background: gold;
  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: 350px;
  height: 350px;
  padding: 16px;
  border: 16px solid orange;
  background-color: white;
  z-index: 1002;
  overflow: auto;
}
.js-zoom-out,.js-zoom-in{border: solid lime 1px;}
<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>

<template id='demoTemplate'>
   <span>      
         <span class="btn-group">
            <button type="button" class="js-zoom-in">Zoom In</button>
            <button type="button" class="js-zoom-out">Zoom Out</button>
            <button type="button" class="js-rotate-right">Rotate Right</button>
            <button type="button" class="js-rotate-left">Rotate Left</button>
         </span>		 
		 <img class="template-image" src ="" style ="display:none">                     </span>
</template>



回答3:

I solved your problem but i cant get it, how can i add this image into your mask image, if you know how do you, add this image into mask image tell me in comments, i surely help you.

Seen your code from two days and I finally know, you cant add any CSS property into your uploaded image because your JS only change src value and your element created by new Image() function and you don't add any graphical interface of your img tag so we cant preview it, for solving that i added some features.

EXPLANATION

First of all i get your mask image id and append img tag and change the src file repeatedly when file change

const imgView = URL.createObjectURL(fileup.files[0]);

now you have preview of your image and you can apply all CSS property on it, i added zooming-in and zooming-out feature.

$('.masked-img' + getId + ' #renderImage').css({
    transform: 'scale('+ scale +')'
});

i also added functionality that append image only one time and for second time it change only src 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);
}

YOUR FIDDLE: https://jsfiddle.net/y7Lq9d6v/



回答4:

Document.getElementById("img").src = json(data);

var angle = 0;
var scale = 1;
var $img = $('#image');

json or JSON data or jsonData PLease check it twice



回答5:

var imagesize = $('img').width();

$('.zoomout').on('click', function() {
  imagesize = imagesize - 5;
  $('img').width(imagesize);
});

$('.zoomin').on('click', function() {
  imagesize = imagesize + 5;
  $('img').width(imagesize);
});
div {
  height: 150px;
  width: 150px;
  display: block;
  overflow: hidden;
  outline: 2px solid #777777;
  padding: 20px;
}

img {
  width: 150px;
}

button {
  font-size: 10px;
  margin: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div><img src="img.png" /></div>
<button class="zoomout">Zoom Out</button > <button class="zoomin">Zoom In</button >