I'm having some problems with my javascript canvas application, where I try to change the background color of an html element outside the canvas area when mouse the mouse cursor is over a canvas shape. I'm using konva library.
What I'm trying to do in code is to target the html side text class and colorize its background when mouse cursor is over canvas element shape building. Moving to different buildings it highlights different text elements to show what areas are in that building. When cursors leaves the canvas building area and is not on any other building it highlights no text elements. Its like an interactive map.
So what I'm asking is, how can I highlight text element on html side when hovering over canvas shape then stop highlighting when leaving the canvas shape while using konva library?
var stage = new Konva.Stage({
container: 'container',
width: 600,
height: 800
});
var layer = new Konva.Layer();
//just few buildings for example
var shapes = [];
shapes.push({
points: [117,188,218,188,218,218,137,218,137,225,117,225],
name: "Building-A",
link: "a-building-link",
});
shapes.push({
points: [230,185,255,185,255,310,250,310,250,318,237,318,237,310,230,310],
name: "Building-B",
link: "b-building-link",
});
shapes.push({
points: [261,134,367,134,367,152,261,152],
name: "Building-C",
link: "c-building-link",
});
for (var i = 0; i < shapes.length; i++) {
var s = shapes[i];
//var links = document.getElementsByClassName(s.link);
var poly = new Konva.Line({
points: s.points,
fill: 'rgba(255,0,0,0.2)',
stroke: 'black',
strokeWidth: 3,
closed : true,
name: s.link,
opacity: 0
});
poly.on('mouseover', function () {
this.opacity(1);
layer.draw();
//Some things I tried to get this working
//for (var i = 0; i < links.length; i++) {
//var item = links[i];
//item.style.backgroundColor = "#ffcc00";
//}
/////////////////
//var item = this.name;
//item.style.backgroundColor = "#ffcc00";
////////////////
//document.getElementsByClassName(this.name).style.backgroundColor = "#ffcc00";
///////////////
//highlight_target = this.name;
///////////////
//document.getElementsByClassName(${this.name}).style.backgroundColor = "#ffcc00";
///////////////
//document.getElementsByClassName(s.name).style.backgroundColor = "#ffcc00";
});
poly.on('mouseout', function () {
this.opacity(0);
layer.draw();
//Some things I tried to get this working
//for (var i = 0; i < links.length; i++) {
//var item = links[i];
//item.style.backgroundColor = "";
//}
/////////////
//var item = this.name;
//item.style.backgroundColor = "";
/////////////
//document.getElementsByClassName(this.name).style.backgroundColor = "";
/////////////
//highlight_target = "";
/////////////
//document.getElementsByClassName(${this.name }).style.backgroundColor = "";
/////////////
//document.getElementsByClassName(s.name).style.backgroundColor = "";
});
layer.add(poly);
}
stage.add(layer);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div id="container"></div>
<div class="textlink a-building-link b-building-link">
<span>Place 1</span>
<span>A, B</span>
<span>1</span>
</div>
<div class="textlink c-building-link">
<span>Place 4 and 5</span>
<span>C</span>
<span>3</span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<script type="text/javascript" src="test.js"></script>
</body>
</html>
Thanks for any help.
You were very close. To get the name of the shape you need to use .name(). After that it looks like some of your code would have worked.
I moved the building shapes layer up the page and put the text at the top in the snippet so that it is more clear what is going on for other future readers. Additionally, since Konvajs relies on jquery being present, you can use the handy jquery selector mechanism to access your test elements. This is not critical to the solution.
var stage = new Konva.Stage({
container: 'container',
width: 600,
height: 400
});
var layer = new Konva.Layer();
//just few buildings for example
var shapes = [];
shapes.push({
points: [117,188,218,188,218,218,137,218,137,225,117,225],
name: "Building-A",
link: "a-building-link",
});
shapes.push({
points: [230,185,255,185,255,310,250,310,250,318,237,318,237,310,230,310],
name: "Building-B",
link: "b-building-link",
});
shapes.push({
points: [261,134,367,134,367,152,261,152],
name: "Building-C",
link: "c-building-link",
});
for (var i = 0; i < shapes.length; i++) {
var s = shapes[i];
//var links = document.getElementsByClassName(s.link);
var poly = new Konva.Line({
points: s.points,
fill: 'rgba(255,0,0,0.2)',
stroke: 'black',
strokeWidth: 3,
closed : true,
name: s.link,
opacity: 0.5
});
poly.on('mouseover', function () {
this.opacity(1);
layer.draw();
// use name() to ge the name. I then use the name as a jquery selector.
$('.' +this.name()).css({ backgroundColor: "#ffcc00"});
});
poly.on('mouseout', function () {
this.opacity(0.5);
layer.draw();
$('.' +this.name()).css({ backgroundColor: "transparent"});
});
layer.add(poly);
layer.move({x:0, y:-40}) // make layer nearer the top of page for SO.
stage.draw();
}
stage.add(layer);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div class="textlink a-building-link b-building-link">
<span>Place 1</span>
<span>A, B</span>
<span>1</span>
</div>
<div class="textlink c-building-link">
<span>Place 4 and 5</span>
<span>C</span>
<span>3</span>
</div>
<div id="container"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<script type="text/javascript" src="test.js"></script>
</body>
</html>
Related
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);
}
I'm using paper js to draw an animated set of rectangles. Since there will be several HTML pages in this project, I tried adding the menu HTML programmatically using javascript. However, once the menu script loads, paper js stops redrawing.
I used a timer to delay the loading of the menu script to ascertain if it was any other issue. The animation definitely plays normally right before the menu script loads.
Any other element added programmatically works fine. It's the only paper that stops redrawing.
HTML
<head>
<script src="./scripts/paper-full.js"></script>
<script type="text/javascript" src="./scripts/menu.js"></script>
<script
type="text/paperscript"
src="./scripts/paper.js"
canvas="myCanvas"
></script>
<!-- <link rel="stylesheet" href="./styles/style.css" /> -->
<link
href="https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap"
rel="stylesheet"
/>
</head>
<body id="body">
<div id="main">
<label id="stats"></label>
<canvas id="myCanvas" resize></canvas>
</div>
</body>
MENU
var label = '<label id="stats"></label>';
var divO = '<div id="menu" class="menu">';
var divC = "</div>";
var html =
'Home ProjectsBlog About';
setTimeout(() => {
document.body.innerHTML += label + divO + html + divC;
}, 500);
PAPER
var length = 35;
var path = new Path({
strokeColor: '#E4141B',
strokeWidth: 20,
strokeCap: 'round'
});
var start = view.center / [10, 1];
for (var i = 0; i < points; i++)
path.add(start + new Point(i * length, 0));
function onMouseMove(event) {
path.firstSegment.point = event.point;
for (var i = 0; i < points - 1; i++) {
var segment = path.segments[i];
var nextSegment = segment.next;
var vector = segment.point - nextSegment.point;
vector.length = length;
nextSegment.point = segment.point - vector;
}
path.smooth({ type: 'continuous' });
}
function onMouseDown(event) {
path.fullySelected = true;
path.strokeColor = '#e08285';
}
function onMouseUp(event) {
path.fullySelected = false;
path.strokeColor = '#e4141b';
}
Your problem comes from the fact that you are resetting the document.body.innerHTML and this somehow breaks Paper.js coupling with the canvas.
What I would suggest is using a dedicated element to insert your dynamic content into the DOM, rather that injecting it into the <body> directly.
Here is an updated code demonstrating the solution. Have a close look at the lines:
<div id="menu-container"></div>
document.querySelector('#menu-container').innerHTML = label + divO + html + divC;
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/paper"></script>
<link
href="https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap"
rel="stylesheet"
/>
</head>
<body id="body">
<div id="main">
<label id="stats"></label>
<canvas id="myCanvas" resize></canvas>
</div>
<div id="menu-container"></div>
<script type="text/javascript">
var label = '<label id="stats"></label>';
var divO = '<div id="menu" class="menu">';
var divC = "</div>";
var html =
'Home ProjectsBlog About';
document.querySelector('#menu-container').innerHTML = label + divO + html + divC;
</script>
<script type="text/paperscript" canvas="myCanvas">
var length = 35;
var path = new Path({
strokeColor: '#E4141B',
strokeWidth: 20,
strokeCap: 'round'
});
var points = 20
var start = view.center / [10, 1];
for (var i = 0; i < points; i++)
path.add(start + new Point(i * length, 0));
function onMouseMove(event) {
path.firstSegment.point = event.point;
for (var i = 0; i < points - 1; i++) {
var segment = path.segments[i];
var nextSegment = segment.next;
var vector = segment.point - nextSegment.point;
vector.length = length;
nextSegment.point = segment.point - vector;
}
path.smooth({ type: 'continuous' });
}
function onMouseDown(event) {
path.fullySelected = true;
path.strokeColor = '#e08285';
}
function onMouseUp(event) {
path.fullySelected = false;
path.strokeColor = '#e4141b';
}
</script>
</body>
</html>
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>
I am using sigma.js and have a custom node renderer that draws a node as a rectangle. Inside the rectangles are more shapes (basically just more rectangles and some text). I am also using the dragNodes plugin.
Right now, when a user wants to click on a node or drag it around the canvas, the user must go to the top left coordinate. However, the rectangle (the outermost one encompassing all the shapes inside it) represents the whole node. How do I modify my code to specify the dimensions of a node?
Also, although not shown in the code, if a user clicks inside a node rectangle (on one of the inner rectangles), I also want that to be detected and have a callback. Is this possible?
An example demonstrating my problem is shown here:
<!DOCTYPE html>
<html>
<head>
<script data-require="jquery#*" data-semver="2.2.0" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/sigma.min.js"></script>
<script src="http://rawgit.com/jacomyal/sigma.js/master/plugins/sigma.plugins.dragNodes/sigma.plugins.dragNodes.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
$(document).ready(function() {
console.log('ready');
var g = { nodes: [], edges: [] };
g.nodes.push({
id: 'n1',
label: 'node1',
type: 'rtype',
x: Math.random(),
y: Math.random(),
size: 1,
color: '#666'
});
g.nodes.push({
id: 'n2',
label: 'node2',
type: 'rtype',
x: Math.random(),
y: Math.random(),
size: 1,
color: '#666'
});
sigma.renderers.def = sigma.renderers.canvas;
sigma.canvas.labels.rtype = function(node, context, settings) { };
sigma.canvas.nodes.rtype = function(node, context, settings) {
var prefix = settings('prefix') || '';
var size = node[prefix + 'size'];
var x = node[prefix + 'x'],
y = node[prefix + 'y'];
context.strokeStyle = 'rgb(0,0,0)';
context.strokeRect(x, y, 200, 200);
};
sigma.canvas.drawLabels = true;
var s = new sigma({
graph: g,
container: 'graph-container'
});
var dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
});
</script>
</head>
<body>
<div id="container">
<style>
#graph-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
}
</style>
<div id="graph-container"></div>
</div>
</body>
</html>
I have the three functions running on my interactive pet (change background, dimmer lights, mouse follow). I'm trying to add an image to the background of the body, but every time I try it gets pushed to the top and the dirty/clean tank function doesn't work. Could someone tell me how to change the color sections of the first function below to switch between images instead of color codes? I think that would solve the immediate problem. :/
CSS Code:
//change background color: dirty/clean tank
setTimeout(function dirty() { //sets background color to murky green
document.body.style.backgroundColor = ****"#CCCC99";** //would like this to be an image**
var btn = document.body.appendChild(document.createElement('button'));
btn.appendChild(document.createTextNode("Clean the tank!"));
document.body.style.marginTop = "-1000px";
btn.onclick = function clean() {
btn.parentNode.removeChild(btn);
document.body.style.backgroundColor = **"#CCFFFF";//would like this to be an image**
setTimeout(dirty,27000); //how long it takes to make the tank dirty
};
},500); //first loading of clear blue timeout
//dim the lights
var dimmed = 0;
function toggleLights()
{
var dimmer = document.getElementById("dimmer");
if(dimmed == 0) dimmed = 1;
else dimmed = 0;
if(dimmed == 1)
{
dimmer.style.opacity = 0.8;
dimmer.style.visibility = "visible";
}
if(dimmed == 0)
{
dimmer.style.opacity = 0.0;
dimmer.style.visibility = "hidden";
}
}
//fish moves on mouse hover
$(document).ready(function() {
$("#swim").mousemove(function (event) {
var fish = $("#fish1");
var position = fish.position();
var mousey = event.pageX;
if (position.left > mousey) {
$("#fish1").html("<img src='images/happy.png' />");
} else {
$("#fish1").html("<img src='images/happyback.png'/>");
}
$("#fish1").stop().animate({
left: event.pageX,
top: event.pageY
}, 300);
});
});
HTML Code:
<HTML>
<HEAD>
<TITLE>Untitled</TITLE>
<link href="style.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"
</script>
<script type="text/javascript" src="pet.js"></script>
<script type="text/javascript" src="core.js"></script>
</HEAD>
<div id="table">
<div id="dimmer" onClick="toggleLights()"></div>
<a href="javascript:toggleLights();"><img src="images/table-lamp.png" height="380px"
alt="table lamp"></a>
</div>
<div id="swim">
<div id="fish1"><img src="images/happy.png"/></div>
</div>
</BODY>
</HTML>
Add a starting <body> tag in your HTML, and change element classes with javascript, instead of style properties. That way you have more control over the styling through your CSS file.
So you would do something like:
document.body.className = 'state1';
And in your CSS
.state1{
background-image: url(the/image.jpg);
}
and so on...