Once user click on Mask image, we are allowing user to upload custom image, this is working fine if there is single mask image : https://codepen.io/kidsdial/pen/jJBVON
Requirement :
but if there are multiple mask images, then also user should be able to upload custom images on all mask images [ something like https://codepen.io/kidsdial/pen/rRmYPr ] , but right now its working only for single image....
2 images codepen : https://codepen.io/kidsdial/pen/xBLrjY
8 images codepen : https://codepen.io/kidsdial/pen/ywMVOY
var mask;
let jsonData = {
"path" : " newyear collage\/",
"info" : {
"author" : "",
"keywords" : "",
"file" : "newyear collage",
"date" : "sRGB",
"title" : "",
"description" : "Normal",
"generator" : "Export Kit v1.2.8"
},
"name" : "newyear collage",
"layers" : [
{
"x" : 0,
"height" : 612,
"layers" : [
{
"x" : 0,
"color" : "0xFFFFFF",
"height" : 612,
"y" : 0,
"width" : 612,
"shapeType" : "rectangle",
"type" : "shape",
"name" : "bg_rectangle"
},
{
"x" : 160,
"height" : 296,
"layers" : [
{
"x" : 0,
"height" : 296,
"src" : "ax0HVTs.png",
"y" : 0,
"width" : 429,
"type" : "image",
"name" : "mask_image_1"
},
{
"radius" : "26 \/ 27",
"color" : "0xACACAC",
"x" : 188,
"y" : 122,
"height" : 53,
"width" : 53,
"shapeType" : "ellipse",
"type" : "shape",
"name" : "useradd_ellipse1"
}
],
"y" : 291,
"width" : 429,
"type" : "group",
"name" : "user_image_1"
},
{
"x" : 25,
"height" : 324,
"layers" : [
{
"x" : 0,
"height" : 324,
"src" : "hEM2kEP.png",
"y" : 0,
"width" : 471,
"type" : "image",
"name" : "mask_image_2"
},
{
"radius" : "26 \/ 27",
"color" : "0xACACAC",
"x" : 209,
"y" : 136,
"height" : 53,
"width" : 53,
"shapeType" : "ellipse",
"type" : "shape",
"name" : "useradd_ellipse_2"
}
],
"y" : 22,
"width" : 471,
"type" : "group",
"name" : "user_image_2"
}
],
"y" : 0,
"width" : 612,
"type" : "group",
"name" : "newyearcollage08"
}
]
};
$(document).ready(function() {
$('.container').click(function(e) {
setTimeout(() => {
$('#fileup').click();
}, 20)
});
function getAllSrc(layers) {
let arr = [];
layers.forEach(layer => {
if (layer.src) {
arr.push({
src: layer.src,
x: layer.x,
y: layer.y
});
} else if (layer.layers) {
let newArr = getAllSrc(layer.layers);
if (newArr.length > 0) {
newArr.forEach(({
src,
x,
y
}) => {
arr.push({
src,
x: (layer.x + x),
y: (layer.y + y)
});
});
}
}
});
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;
for (let {
src,
x,
y
} of arr) {
$(".container").css('width', width + "px").css('height', height + "px").addClass('temp');
var mask = $(".container").mask({
maskImageUrl: 'https://i.imgur.com/' + src,
onMaskImageCreate: function(img) {
img.css({
"position": "absolute",
"left": x + "px",
"top": y + "px"
});
}
});
fileup.onchange = function() {
mask.loadImage(URL.createObjectURL(fileup.files[0]));
};
}
}
json(jsonData);
}); // end of document ready
// jq plugin for mask
(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.onDragStart = function(evt) {
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.getImagePosition = function() {
return {
x: settings.x,
y: settings.y,
scale: settings.scale
};
};
container.onDragOver = function(evt) {
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;
container.updateStyle();
}
};
container.updateStyle = function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
context.clearRect(0, 0, canvas.width, canvas.height);
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
});
};
img = new Image();
img.src = settings.imageUrl;
img.setAttribute('crossOrigin', 'anonymous');
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;
};
}, 0);
};
// change the draggable image
container.loadImage = function(imageUrl) {
if (img)
img.remove();
// reset the code.
settings.y = startY;
settings.x = startX;
prevX = prevY = 0;
settings.imageUrl = imageUrl;
initImage = true;
container.updateStyle();
};
// change the masked Image
container.loadMaskImage = function(imageUrl, from) {
if (div)
div.remove();
canvas = document.createElement("canvas");
context = canvas.getContext('2d');
canvas.setAttribute("draggable", "true");
canvas.setAttribute("id", settings.id);
settings.maskImageUrl = imageUrl;
div = $("<div/>", {
"class": "masked-img"
}).append(canvas);
// div.find("canvas").on('touchstart mousedown', function(event)
div.find("canvas").on('dragstart', function(event) {
if (event.handled === false) return;
event.handled = true;
container.onDragStart(event);
});
div.find("canvas").on('touchend mouseup', function(event) {
if (event.handled === false) return;
event.handled = true;
container.selected(event);
});
div.find("canvas").bind("dragover", container.onDragOver);
container.append(div);
if (settings.onMaskImageCreate)
settings.onMaskImageCreate(div);
container.loadImage(settings.imageUrl);
};
container.loadMaskImage(settings.maskImageUrl);
JQmasks.push({
item: container,
id: settings.id
})
return container;
};
}(jQuery));
.temp {
background: black;
}
.container {
background: black;
position: relative;
}
.masked-img {
overflow: hidden;
margin-top: 30px;
position: relative;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<input id="fileup" name="fileup" type="file" style="display:none" >
<div class="container">
</div>
Edit
I tried as Static way : https://codepen.io/kidsdial/pen/xBLrjY , here for 2 images, i included 2 <input type="file" >
, for 3 images i may need to add 3 input files , is it possible to fix the number of or is there any other dynamic hack, so that it will work for any number of mask images ?
Here is a working solution.I just update the way how you are getting data . I saved all masks in an array . Than look for the array associated to the target click
Since you're using canvas elements for the actual drawings, we can take advantage of that:
clientX
andclientY
are the coordinates of the click relative to the client area (the area of the page currently visible in the browser). Using these withdocument.elementsFromPoint()
gives us all the elements that were under the click location.Then we scan through all the found elements, checking if they have the property
getContext
..some()
lets us iterate through the array of elements while also letting us terminate early if we choose. We test forgetContext
to see if it's a canvas element and, if it's not, we can skip it.If it is a canvas, we can move on to the next step.
getContext('2d')
grabs the canvas' drawing context from which we can pull raw pixel data viagetImageData()
.pageX
andpageY
from the event are the click coordinates relative to the whole page.getBoundingClientRect()
gets us the element's location relative to the page. Combining those gives us the coordinate of the click relative to the element. We use that ingetImageData()
to grab the canvas pixel data at the click location in that canvas.Pixel data is interleaved red, blue, green, and alpha (opacity) values, ranging from 0 to 255 for each. Since we grabbed a single pixel, it will have 4 values, the last one being alpha. We can then use that to determine whether or not we trigger the file dialog.
If we happen to find an opaque pixel at the click location in a canvas, we can trigger the file dialog and then skip the rest of the elements by returning
true
from the.some()
callback.