In my code, I have an SVG of the USA. The USA SVG has paths for every US State. Once one clicks on a specific US State, the ID of that path matches with another SVG (an enlarged State SVG) that is in a separate folder called "counties". The USA SVG fades out, and the enlarged State SVG fades in. How, then, can I interact with this newly shown enlarged state SVG?
Here is my code that grabs the enlarged State SVG after clicking on a specific path on the USA SVG:
<object style="width:auto; height:auto;" id="countyLevel" data="" type="image/svg+xml"></object>
and the Jquery:
$(document).ready(function(){
$('path').on("click",function(e){
var state = "counties/"+$(this).attr('id') + ".svg";
showState(state);
});
});
function showState(stateFile) {
$('.usa').fadeOut();
$('.returncountry').fadeIn();
$('.table2').fadeIn();
$("#countyLevel").attr("data", stateFile);
$("#countyLevel").css('display', 'block');
}
The paths on the USA SVG are interactive, but I don't know how to make the new enlarged State SVG paths interactive once it fades in.
You can access the DOM of a document in an <object> tag with .contentDocument as soon as one is loaded. From there you can select nodes like you would in any other document:
$(document).ready(function(){
$('path').on("click",function(e){
var state = "counties/"+$(this).attr('id') + ".svg";
showState(state);
});
// queue up the load event once
$("#countyLevel").on("load", function () {
// choose adequate selectors and events
$(this.contentDocument).find('path').on("click", showAlert);
});
});
// showState as above
// showAlert defines your interaction
Related
I need some way to trigger event and listen to it in FabricJs.
The idea is that using jQuery selector I get elements and when someone clicks on any of those elements, fabricjs should change Canvas background.
$('#background-selector .background-img img').on("click", (el) => {
// so here I need to trigger event and also pass el.data to the event
// el.data contains image name that should be used to change canvas background
});
I am new to fabricjs and javascript (without jquery) so need some guidance on how this should work to each other.
I'm new to fabric.js too... Because of your question, so thanks ;)
It took a while to find how to trigger an event from outside the canvas. I tried about everything using the JavaScript new Event and .createEvent()... But that just failed because the "custom event" can only apply on a DOM element and the objects inside a fabric canvas aren't.
So.. Exploring the fabric.js documentation, I found they handle some events fired from an action inside the canvas, but it was not what I needed... While useful to know how to listen to an event.
I finally found the observable namespace. It include a fire() method which is exactly about triggering events.
Then, I blocked a while on how to make the changes "appear" in the canvas, from the canvas object event handlers. It seems that property changes on the canvas itself, using a method like .setBackgroundImage(), needs a callback to re-render and changes on canvas objects need the object to be re-added to the canvas.
So here is a simple demo where there is two DOM buttons:
one to pass an image url
another to change the rect color
// create a wrapper around native canvas element (with id="c")
var canvas = new fabric.Canvas('c')
var render_callback = canvas.renderAll.bind(canvas)
// create a rectangle object
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20
});
// "add" rectangle onto canvas
canvas.add(rect);
// ========================================
// Canvas object event handlers
rect.on('btn1_click', function(e) {
console.log('custom event #1', e.data);
// Change the fill color and re-add the object
rect.set('fill', e.data)
canvas.add(rect);
});
canvas.on('btn2_click', function(e) {
console.log('custom event #2', e.data);
// Set a background image,
// the callback (2nd argument) is needed to re-render
canvas.setBackgroundImage(e.data, render_callback)
});
// ========================================
// DOM event handlers for the buttons
$("#change_color").on("click", function(){
console.log($(this).data("color"))
// Fire the canvas event and pass the image url
rect.fire("btn1_click", {data: $(this).data("color")})
})
$("#change_image").on("click", function(){
console.log($(this).data("img"))
// Fire the canvas event and pass the image url
canvas.fire("btn2_click", {data: $(this).data("img")})
})
#c{
border: 1px solid lightgrey;
}
button{
margin: 2em 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.0/fabric.min.js"></script>
<canvas id="c" width=220 height=220></canvas>
<button id="change_color" data-color="blue">Click me to change the rect color</button>
<button id="change_image" data-img="https://via.placeholder.com/220x220.png">Click me to add an image</button>
So I am using SnapSVG to manipulate (animate) my SVG File that i created with Inkscape. The animating of the different elements like a rectangle or circle is working completely fine. The problem that i have is when i try to add a new element like a rectangle with SnapSVG code it does not work and the whole screen is blank (everything disappears). Why do i have this problem? Is it even possible to add a new element with SnapSVG to a existing SVG File?
Down below i showed you some code on how i manipulate the SVG and how it gets displayed in a DIV on my Page. And I am also showing you how I am trying to add a new Element with SnapSVG
I tried almost everything. I can put the code for the new Element outside of the code for the already existing SVG but then it always appears outside of the SVG file.
<head>
<meta charset="utf-8">
<title>Inkscape Animated Icon Snap</title>
<!--We need to add the Snap.svg script to our document-->
<script src="snap.svg.js"></script>
<script>
//Run script right away
window.onload = function () {
//We'll be appending the icon to this DIV later
var s = Snap("#svgDiv");
//Have Snap load the SVG file
Snap.load("icon.svg", function(f) {
s.append(f);
//Assign the white rectangle
whiteRect = f.select("#whiteRect");
//Assign the whole icon group
icon = f.select("#icon");
//When the icon is hovered over, have the white rectangle move up slightly with elastic properties
icon.hover(function() {
whiteRect.animate({y:18}, 500, mina.elastic);
},
//And return to original position when not hovered over
function() {
whiteRect.animate({y:23.984177}, 500, mina.elastic);
}
);
var bigCircle = s.circle(0, 0, 20).attr({fill: 'green' });
icon.append(bigCircle);
//Finally append the icon to iconDiv in the body
});
};
</script>
</head>
<body>
<!--Here's the DIV that will hold the animated SVG icon-->
<svg id="svgDiv"></svg>
</body>
So basically what I want as a result is just another rectangle added to my SVG File. What i get is a blank Page.
2 main things spring to mind (hard to be sure without the html/svg as well).
Firstly, you Snap must be called on an SVG element, not an HTML element, so when I see this line...
var s = Snap("#iconDiv");
it's almost certainly wrong. It cannot be a div, or any HTML element. Snap works on SVG elements,
var s = Snap("#anSVGselement");
is what's needed.
Secondly, your line
s.append(f);
Wants to be straight after the Snap.load()
Snap.load("icon.svg", function(f) {
// stuff here probably won't work, there are some minimal methods like select, but not like hover etc
s.append(f);
//do stuff now it's appended
var icon = s.select("#icon");
var bigCircle = s.circle(10, 970, 20);
icon.append(bigCircle);
});
The problem is, f is just a fragment at this point, it has no place in the DOM yet, so methods like 'hover' etc, aren't available until you have added with append into the DOM. Once it's appended these methods become available. There are a few methods available like select, before it's appended (so you 'may' be able to select an element from the fragment and then append that, rather than appending everything I think).
Example
Also note, the icon.svg file has some transforms on it, so you will need to adjust the circle to have the correct cx/cy like in my example, or add a transform to match (or remove the transforms from the original svg and have correct cx/cy)
I have a problem when trying achieve hover effect on mapped image. I have an image with mapped areas and on all of them I want to show a different image when hover.
You can see my work so far here:
http://mantasmilka.com/map/pries-smurta.html
The problem is when I hover over area it show the image, but when I move the cursor (not leaving the area) it starts flickering. It takes area space pixel by pixel.
I've tried working with Javascript and jQuery solutions:
Javascript:
mouseenter="document.getElementById('Vilnius').style.display = 'block';" mouseleave="document.getElementById('Vilnius').style.display = 'none';"
jQuery:
$('.hide').hide();
setTimeout(function(){
$("#area-kaunas").mouseenter(function(){
$('#Kaunas').show();
});
$("#area-kaunas").mouseleave(function(){
$('#Kaunas').hide();
});
}, 500);
Why not just use hover() inside of jQuery? I'm also unsure why you bind the events after a 500 millisecond timeout?
$('.hide').hide();
$("#area-kaunas").hover(function() {
$('#Kaunas').show();
}, function() {
$('#Kaunas').hide();
});
There is a css property called "pointer-event" which gives the value "none" to the img tags that overlap in the mapped image and works as you need it. This is the documentation https://developer.mozilla.org/en/docs/Web/CSS/pointer-events
The problem will always be the compatibility of browsers.
I'm building a poker table maker that works as follows:
When the page loads, I load the components of a poker table one-by-one.
Final users select what kind of felt, armrest, body or cup holders want on their poker tables through a select box.
When a change is detected in the select box, the src attribute of the Image object changes as well. For example: If a user wants a black felt, I'm loading the image: felts/black.png; then if he decides that it looks better in blue, the page loads the image felts/blue.png
My problem is the following:
As soon as they change one element of the poker table, the new image is automatically loaded on top of the rest .
For example:
If I change the felt color, the felt's image will go on top of the armrest's image.
I'd like to know how to force the felt image to maintain its z-index sort of thing.
Here's my code:
var tableBody = new Image();
tableBody.onload = function() {
context.drawImage(tableBody, 0, 0);
};
tableBody.src = "http://www.mcptables.com/images/buildyourtable/body/autumn-oak.png";
/* and it does the same thing for the other parts of the poker table... tableArmrest, tableFelt
tableCupholders, etc... */
$('#armrest').change(
function() {
switch( $(this).val() ) {
case "black":
tableArmrest.src = "http://www.mcptables.com/images/buildyourtable/armrest/black.png";
break;
case "brown":
tableArmrest.src = "http://www.mcptables.com/images/buildyourtable/armrest/wood.png";
}
}
)
You can see it in action on the following page:
http://mcptables.com/buildyourtable.html
And here's a link to my JS code:
http://mcptables.com/js/pokertablebuilder.js
Any help would be greatly appreciated!
If I want to change the
Unfortunately there is no way to maintain the z-index of already drawn objects on a canvas. All you can do is delete everything and redraw appropriately.
There are canvas frameworks that take care of this and give the developer the "impression" of separate objects on the canvas. See processing.js, raphael, kineticjs etc
I'm making a web application that uses Kinetic.js for some fancy
graphical functions, and I want to show a tooltip with some information about each element when the users hovers over it.
I've already figured out how to invoke a function on hover with Kinetic.js:
myKineticObject.on("mousemove", function() {
// Change content of tooltip depending on myKineticObject
// Set position of tooltip to cursor position
// Show tooltip
});
myKineticObject.on("mouseout", function() {
// Hide tooltip
}
I've decided to use the seemingly nice Opentip to show the tooltip.
The only problem is that Kinetic.js doesn't create any usable DOM elements to use as a target for opentip.
This is (roughly) what my HTML looks like:
<html>
<head><!-- Styles --></head>
<body>
<div id="container">
<canvas class = "kineticjs-buffer-layer">
<canvas class = "kineticjs-path-layer">
<canvas class = "myLayer1">
<canvas class = "myLayer2">
<!-- ... more layers -->
</div>
<!-- Scripts -->
</body>
</html>
Important to know is that these canvas elements all have the same width and height and stack on eachother. So they're unusable as targets.
So instead of using a DOM element to use as the target for my tooltip, I need to
manually show/hide and position the tooltip.
I've figured out how to do the showing and hiding like this:
var tooltip = new Opentip(
"div#container", //target element
"DummyContent", // will be replaced
"My Title", // title
{
showOn: null, // I'll manually manage the showOn effect
});
I now do the following:
myKineticObject.on("mousemove", function() {
tooltip.show();
});
myKineticObject.on("mouseout", function() {
tooltip.hide();
}
The only problem is that it just shows up at the top left of the page, and I can't find anything in the docs on how to position this thing manually.
Suggestions or ideas welcome. I'm also open to using a different tooltip library, if necessary.
Thanks!
It appears (with no knowledge of Opentip, besides a quick look at the docs) that you can set 'fixed' to true in the config. Since the target is null, the docs suggest fixed=true should make the tooltip appear at the mouse position, but not follow it once the mouse is moved around.
How does that sound?
I managed to solve the problem like this, for those of you who are looking for a solution too:
group.on("mousemove", function(event) {
tooltip.content = //change content
tooltip.show();
$("#opentip-1").offset({ left: event.offsetX, top: event.offsetY });
});