javascript - last image clicked bring to front - javascript

I'm trying to make a really simple website in javascript.
I want the website to have a stack of images that you can drag.
So far I managed to do it. But now I want to always bring to front the last clicked image.
How can I do this ? Thanks !
Here is my code :
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva#8.4.0/konva.min.js"></script>
<meta charset="utf-8" />
<title></title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
var img1 = new Konva.Image({
x: 20,
y: 20,
width: 400,
height: 566,
draggable: true,
});
layer.add(img1);
var img2 = new Konva.Image({
x: 100,
y: 20,
width: 400,
height: 566,
draggable: true,
});
layer.add(img2);
var imageObj1 = new Image();
imageObj1.onload = function () {
img1.image(imageObj1);
};
imageObj1.src = 'imgs/img1.jpg';
var imageObj2 = new Image();
imageObj2.onload = function () {
img2.image(imageObj2);
};
imageObj2.src = 'imgs/img2.jpg';
</script>
</body>
</html>
I've seen this code on another post, but I don't know how to make it work ..
$("img.myclass").click(function() {
$("img.myclass").not(this).css("z-index", 0);
$(this).css("z-index", 100);
});

The "code on another post" is written in jQuery wich is an javascript library and need to be added to your page as you do with konva.
Konva is a tool that draw an canvas.
You can change zIndex (property that will change order of your stack) when you click on image.
For example:
img1.zIndex(0);
img2.zIndex(1); // will set img2 higher
If image didn't have zIndex() function you can try to wrap it to a group.
const group1 = new Konva.Group();
group1.add(img1);
group1.zIndex(0);
Edit:
Increase zIndex on click.
let zIndex = 0;
img.onclick = function () {
zIndex += 1;
img.zIndex(zIndex);
}

Related

Fabric.js remnants of old canvas remain

I want to display the content of a canvas as the background of another canvas and draw a bunch of rectangles on there. I need to dynamically change:
the canvas from which to load the background image for my finalcanvas
which rectangles to draw
It's easiest if I start this process from scratch. I have imitated starting from scratch with a simple button. However, after redrawing my canvas, previous information from fabric.js remains present after dragging an item of a canvas a bit. This means that the old canvas was not cleared properly. I tried playing around with .clear() and .depose(), but to no avail.
In case the description is vague, here an image:
And an small reproducible example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
<canvas id="finalcanvas"></canvas>
<canvas id="backgroundcanvas" width="200" height="100"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js" type="text/javascript"></script>
<script>
function load_plane_onto_active_canvas() {
var c = document.getElementById('backgroundcanvas');
var ctx = c.getContext("2d");
var bg = c.toDataURL("image/png");
var canvas = new fabric.Canvas('finalcanvas', {
width: 333,
height: 333
});
canvas.setBackgroundImage(bg, canvas.renderAll.bind(canvas));
canvas.on("mouse:over", function(e) {
console.log(e.target)
});
// add 100 rectangles
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
rect = new fabric.Rect({
width: 10,
height: 10,
left: j * 15,
top: i * 15,
fill: 'green',
})
canvas.add(rect);
}
}
}
window.onload = function() {
// fill the background canvas with red
(function() {
var canvas = document.getElementById("backgroundcanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
}())
load_plane_onto_active_canvas()
}
</script>
<button onclick="load_plane_onto_active_canvas()">click me</button>
</body>
</html>
I hope someone can help me!
Notice that you're creating a new instance of fabric.Canvas in your load_plane_onto_active_canvas() function. So when you're clicking the button, the existing canvases stay intact, and you're actually calling clear() on your freshly created canvas. The DOM becomes messed up at this point, with several nested canvases inside of each other - you can take a peek at the DOM inspector to see that.
What you can do instead is create the canvas instance just once, then work with a reference to it later in your other functions.
const finalCanvas = new fabric.Canvas("finalcanvas", {
width: 333,
height: 333
});
// finalCanvas now exists in the global (window) scope
function load_plane_onto_active_canvas() {
var c = document.getElementById("backgroundcanvas");
var ctx = c.getContext("2d");
var bg = c.toDataURL("image/png");
finalCanvas.clear();
finalCanvas.setBackgroundImage(bg, finalCanvas.renderAll.bind(finalCanvas));
finalCanvas.on("mouse:over", function (e) {
// console.log(e.target);
});
// add 100 rectangles
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
rect = new fabric.Rect({
width: 10,
height: 10,
left: j * 15,
top: i * 15,
fill: "green"
});
finalCanvas.add(rect);
}
}
}
window.onload = function () {
// fill the background canvas with red
(function () {
var canvas = document.getElementById("backgroundcanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
})();
load_plane_onto_active_canvas();
};
document.querySelector("#b1").onclick = () => load_plane_onto_active_canvas();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.js"></script>
<canvas id="finalcanvas"></canvas>
<canvas id="backgroundcanvas" width="200" height="100"></canvas>
<button id="b1">start from scratch</button>

What are getClientRect(); method and layer.children.each do in JavaScript?

How does the getClientRect(); method work in this code? Is that mean to get the sides of the rectangle?
Also, what is layer.children.each in the code below? Is that mean selecting each child of the node? Can anyone explain to me how these methods work? I checked the document but still not getting how they work.
Thank you so much for your help! I was able to solve this problem.
var stage = new Konva.Stage({
width: 400,
height: 200,
container: 'container'
});
var layer = new Konva.Layer();
stage.add(layer);
layer.on('dragmove', function(e) {
var target = e.target;
var targetRect = e.target.getClientRect();
layer.children.each(function(obj) {
if (obj === target) {
return;
}
if (haveIntersection(obj.getClientRect(), targetRect)) {
alert("Intersection")
}
});
});
function haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width/2 ||
r2.x + r2.width/2 < r1.x ||
r2.y > r1.y + r1.height/2 ||
r2.y + r2.height/2 < r1.y
);
}
// This will draw the image on the canvas.
function drawImage(source, konvaImage) {
layer.add(konvaImage);
var image = new Image();
image.src = source;
image.onload = function() {
konvaImage.image(image);
layer.draw();
}
}
//1yen
var ichiYenImg = new Konva.Image({
x: 20,
y: 20,
width: 100,
height: 100,
draggable: true
});
var sourceImg1 = "https://illustrain.com/img/work/2016/illustrain09-okane5.png";
drawImage(sourceImg1, ichiYenImg);
var goYenImg = new Konva.Image({
x: 120,
y: 20,
width: 100,
height: 100,
draggable: true
});
var sourceImg2 = "https://illustrain.com/img/work/2016/illustrain09-okane7.png";
drawImage(sourceImg2, goYenImg);
//piggy bank 1yen
var ichiYenpiggyImg = new Konva.Image({
x: 300,
y: 100,
width: 100,
height: 100,
draggable: false
});
var sourceImg7 = "https://user-images.githubusercontent.com/31402838/63416628-a322b080-c3b4-11e9-96e8-e709ace70ec1.png";
drawImage(sourceImg7, ichiYenpiggyImg);
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/konva#4.0.5/konva.min.js"></script>
</head>
<body>
<div id="stage-parent">
<div id="container"></div>
</div>
</body>
</html>
A layer may have several children elements. So layer.children is just an array with of such objects.
layer.children.each(func) is a function similar to Array.prototype.forEach()
. It allows to to executre a function for every element in children array.
node.getClientRect() is a function that calcualte absolute boudning box of any Konva.Node. Bounding box is just an object like this:
{
x: 10,
y: 10,
width: 60,
height: 60
}
It allows you to detect the position and the size of any object, even if it is scaled, rotate, etc. Usually, that function can be used to defined position f edges of the shapes.

Dragging a Konva layer from empty areas

I'd like to drag a Konva layer even if empty. I see that I can drag the layer only where objects are present (the code shows that you can drag where the rectangle is but nowhere else).
How can I drag the layer dragging empty parts?
Moreover, I see that also the mouseenter and mousleave aren't respected if you are out of the objects of the layer.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<script src="js/modulePattern.js"></script>
<script src="https://unpkg.com/konva#2.4.2/konva.min.js"></script>
<div id="container"></div>
<script>
function demo() {
var stage = new Konva.Stage({
container: 'container',
width: 100,
height: 100
});
var layer = new Konva.Layer({draggable: true});
stage.add(layer);
layer.on("dragmove", function() {
console.log(this.x(), this.y())
});
layer.on("mouseenter", function() {
stage.container().style.cursor = 'pointer';
});
layer.on("mouseleave", function() {
stage.container().style.cursor = 'default';
});
var rect = new Konva.Rect({
width: 50,
height: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 5
});
layer.add(rect);
stage.draw();
}
demo();
</script>
</body>
</html>
Konva does not detect events on empty space of layers. And it is unclear how can it do it if you have several layers on the stage.
If you still need to listen on empty layers and make a layer draggable at any space you can:
Make stage draggable instead and listen events on it (click will work on empty space too).
Add a transparent rectangle as a background, so it will be used to listen to events:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<script src="js/modulePattern.js"></script>
<script src="https://unpkg.com/konva#2.4.2/konva.min.js"></script>
<div id="container"></div>
<script>
function demo() {
var stage = new Konva.Stage({
container: 'container',
width: 300,
height: 300
});
var layer = new Konva.Layer({draggable: true});
stage.add(layer);
layer.on("mouseenter", function() {
stage.container().style.cursor = 'pointer';
});
layer.on("mouseleave", function() {
stage.container().style.cursor = 'default';
});
var rect = new Konva.Rect({
width: 50,
height: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 5
});
var back = new Konva.Rect({
fill: 'red',
width: stage.width(),
height: stage.height()
})
layer.add(back);
// move rectangle so it always stay on the screen, no matter where layer it
layer.on('dragend', function() {
back.setAbsolutePosition({x: 0, y: 0});
layer.draw();
});
layer.add(rect);
stage.draw();
}
demo();
</script>
</body>
</html>

Multidimensional array of images

I am having troubles with my multidimensional array. There are no errors, but the images are not appearing. I don't know why. please help
<!doctype html>
<html>
<head><script src="kinetic.js"></script></head>
<body>
<div id="canvas" align="center">
<span id="timer"></span>
<div id="container"></div>
<script>
var stage = new Kinetic.Stage({
container: 'container',
width: 850,
height: 650
});
var layer = new Kinetic.Layer();
var image = [];
var img = [];
var images = [
["1.png","2.png","3.png","4.png"]
];
this is the part where I am having problems:
for (var a = 0; a < 4; a++) {
img[a] = new Image();
img.src = images[0][a];
img.onload = function(){
image[a] = new Kinetic.Image({
x: Math.floor(Math.random()*400),
y: Math.floor(Math.random()*400),
image: img[a],
width: 500,
height: 500
});
layer.add(image[a]);
image[a].draw();
}
}
</script>
</body>
</html>

Moving an image in KineticJS using touch events

I want to make a Map, the user should Scroll it using touch events, zoom in and out using touch gestures.
Here am using the touchstart to set the dragging flag to true, then i use touchmove to calculate the delta in the coordinates and move the layer (map layer) accordingly, then finally i use touchend to set the dragging flag to false.
the problem is it's not working, been banging my head for few hours so far can't get it to work.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<link rel="stylesheet" href="core/jquery.mobile-1.4.2.css" />
<script type="text/javascript" src="core/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="core/jquery.mobile-1.4.2.min.js"></script>
<script type="text/javascript" src="javascript/map.js"></script>
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="main" data-role="page">
<div data-role="header">
<h1>Header of #main</h1>
</div>
<div id="content" data-role="content">
<script type="text/javascript" src="core/kinetic-v5.0.1.min.js"> </script>
<script defer="defer">
var stage = new Kinetic.Stage({
container: 'content',
width: 300,
height: 200
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var yoda = new Kinetic.Image({
x: 0,
y: 0,
image: imageObj,
width: 106,
height: 118
});
layer.add(yoda);
stage.add(layer);
};
imageObj.src = 'image/map-04.png'
var dragging = false,
lastX = 0,
lastY = 0;
imageObj.on('touchstart', function() {
var touchPos = stage.getPointerPosition();
dragging = true;
lastX = touchPos.x;
lastY= touchPos.y;
});
imageObj.on('touchmove', function() {
var touchPos = stage.getPointerPosition();
if(dragging){
var dx = touchPos.x - lastX;
var dy = touchPos.y - lastY;
layer.translate(dx,dy);
lastX = touchPos.x;
lastY = touchPos.y;
}
});
imageObj.on('touchend', function() {
dragging = false;
});
stage.add(layer);
</script>
</div>
<div data-role="footer">
<h4>Footer of #main Page</h4>
</div>
</div>
</body>
</html>
Thanks for your help in advance.
your imgObject is not a kineticjs image, yoda is a kineticjs image, here is a fix with some changes:
http://jsbin.com/miqoxese/1/edit
<script type="text/javascript" src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.js"></script>
<script defer="defer">
var stage = new Kinetic.Stage({
container: 'content',
width: 300,
height: 200
});
var layer = new Kinetic.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var yoda = new Kinetic.Image({
x: 0,
y: 0,
image: imageObj,
width: 106,
height: 118,
draggable: true
});
yoda.on('dragstart', function(){
console.log("dragging"); // see console for result
});
layer.add(yoda);
stage.add(layer);
};
imageObj.src = 'http://www.clker.com/cliparts/8/9/9/d/11949855741697952186small_house_01.svg.med.png';
// stage.add(layer); // no need to add again
</script>
Now you need to attach your touchstart touchend etc events to yoda, not to imgObject which is part of the DOM, not the canvas.

Categories