Can't add elements with SnapSVG to my Page - javascript

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)

Related

How to interact with another SVG after clicking a SVG

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

How to keep z-index in canvas when updating images via select box

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

html2canvas offscreen

In using html2canvas, I have a stack of DOM objects (relative positioned div's that contain various things), that I wish to create individual thumbnails for. So if there are ten divs, I will create ten thumbnails.
Some of these objects will be offscreen --each of these divs are in a single, encompassing div called "mainDiv". I iterate through the divs within mainDiv and execute the html2canvas on each of them individually.
For those that are onscreen, this works fine. Those that are offscreen do not -- they come back blank. I created a workaround that scrolls the objects to the top of the mainDiv, however this is a kludge and visually unappealing.
Is it possible to specify a DOM object that is not visible? Ideally, I'd like to be able to specify a containing div and have html2canvas ignore the parent visibility, so I can screen capture hidden objects, but barring that, I'd like to be able to screen capture objects that are simply scrolled off screen.
Any thoughts, ideas? Thanks!
---- Here is some example code. Basically, if you had a bunch of divs within a div, iterate through them. I actually do this recursively so that only one gets processed at a time, with the callback calling the recursive function, so it looks something like this:
function recurser(anIndex, callback) {
if (anIndex == -1) {
callback();
return;
}
$(myDivs[anIndex]).html2canvas({
onrendered : function(canvas) {
var img = canvas.toDataURL();
// store the image in an array, do stuff with it, etc.
recurser(--anIndex, callback);
}
})
}
Once the recursive calls are complete, it executes the callback function, which is a function that will do stuff with the images.
Again, all this works fine as long as the objects are visible within the scrolling div that contains all of the divs in #mainDiv. Once any part of the divs are scrolled off, however, they render black. In fact, if half of two divs are scrolled off (the top half of one, the bottom half of the next), they both render completely black.
I know this is an old question but it seemed kind of interesting. Based on my testing it seemed like only position: absolute and position:fixed elements that were outside of the viewport caused this issue. I figured out a solution to the problem.
What I'm doing is making a clone of the element. Setting the styling of the clone to left = 0, top = window.innerHeight (so that it won't be inside the window) and position = 'relative'. Then I append the clone to the body, use html2canvas on the clone, and then remove the clone from the body. This solution works for me in IE, Firefox, and Chrome.
I have created a JSBin example here. Here is the key javascript code:
function hiddenClone(element){
// Create clone of element
var clone = element.cloneNode(true);
// Position element relatively within the
// body but still out of the viewport
var style = clone.style;
style.position = 'relative';
style.top = window.innerHeight + 'px';
style.left = 0;
// Append clone to body and return the clone
document.body.appendChild(clone);
return clone;
}
var offScreen = document.querySelector('.off-screen');
// Clone off-screen element
var clone = hiddenClone(offScreen);
// Use clone with htm2canvas and delete clone
html2canvas(clone, {
onrendered: function(canvas) {
document.body.appendChild(canvas);
document.body.removeChild(clone);
}
});
set 'overflow' as visible for all of its parent elements. After rendered remove it
CSS
.overflowVisible{
overflow:visible !important;
}
JS
$("#container").parents().each(function(ind,elem){
$(elem).addClass("overflowVisible");
});
html2canvas($("#container"), {
onrendered: function(canvas) {
var img =canvas.toDataURL("image/png");
$('body').append(img);
$("#container").parents().each(function(ind,elem){
$(elem).removeClass("overflowVisible");
});
}
});
You can use the latest version of html2canvas. This resolves all images pix-elation issues and rendering object issue. I used the version 0.5.0-beta4 by Erik koopmans refer this and called the html2canvas as below:
$('html,body').scrollTop(0); // Take your <div> / html at top position before calling html2canvas
html2canvas( $("#my_div"), {
useCORS: true,
dpi: 200,
onrendered:function(canvas) {
var str = canvas.toDataURL("image/png");
var contentType = 'image/png';
console.log(str);
b64Data =str;
$('#uploadedImage').val(b64Data); // set image captured by html2canvas
imgFrameSave(); // Call a function to save your image at server side.
}
})
Ah now have you tried Div display: none; and Div display: absolute; (or how you'd like your div to appear - maybe z-index)
arrow1.onclick = (event){
arrow1.style.display = none;
arrow2.style.display = absolute;
}
arrow2.onclick = (event){
arrow2.style.display = none;
arrow1.style.display = absolute;
}
Just use:
{
scrollX: -window.scrollX,
scrollY: -window.scrollY
}
Based on: https://github.com/niklasvh/html2canvas/issues/1878#issuecomment-511678281

Manually position an opentip tooltip in combination with Kinetic.js

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 });
});

Relation between div position and DOCTYPE's effects

I have a div element that I'm trying, basically, to move wherever the user clicks on a canvas element.
I have a CSS style for the div setting the position to absolute, with an initial position (top,left).
I have javascript that captures the user's click event, and sets the div element's left and top to the location of the click, and set the text of the div.
My problem is that this worked fine before I set a DOCTYPE on the html file. Now the div stays in the same place, while displaying the new text, and I'm assuming the position issue is something to do with how I'm using CSS.
What's the right way to set the position of a div element? The html goes more or less like this:
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
#myDiv{
position:absolute;
top:100px;
left:835px;
}
</style>
</head>
<body><canvas id='canv'></canvas>
<div id='myDiv'>-</div>
</body>
</html>
Here's what the javascript looks like, which locates the div for me:
var theDiv = document.getElementById('myDiv');
theDiv.style.left = selShp.pinx; // selShp.pinx is the position of a shape object I've created, which is how I position the shape on the canvas
theDiv.style.top = selShp.piny; // piny is just the y position
Before setting a DOCTYPE, this worked beautifully on Chrome, Firefox, Safari mobile, and Opera. With it set, I can render to the canvas in IE9, but then the positioning of the div in all the browser stops working - just stays in the initial position.
I figured out my problem. My javascript for setting the new position went like this:
var theDiv = getElementByID(...)
theDiv.style.left = selShp.pinx; // selShp is the selected shape object, and pinx is x location (numeric) on canvas
theDiv.style.top = selShp.piny; // piny is y location on canvas (numeric)
This worked fine before I was using the doctype, because apparently the browser was fine with me just giving a number, but I had to change the code to this, and it works:
var theDiv = getElementByID(...)
theDiv.style.left = selShp.pinx.toString() + 'px';
theDiv.style.top = selShp.piny.toString() + 'px';
Stupid, rookie mistake, I guess. My understanding of the solution is, standard HTML requires you to set the left and top as strings, with units specified.
The real problem begins with not using a doctype. A doctype is required of all modern web pages to keep the browser out of 'quirks mode'. In that case, the box model is different than it should be using current web standards. You should read about quirks on Wikipedia or Google for it.

Categories