How to animate a menu with JQuery.hover plus Raphael.animate? - svg

See this fiddle:
http://jsfiddle.net/UfP3C/3/
Each list item contains an SVG element.
My goal is when the user hovers the list items, the Raphael animation occurs (for its child svg element).
I'm having the following issue:
JQuery.hover works fine when moving the mouse slowly. But, when you
quickly mouse across (and off) both list items (horizontally), the svg elements often get stuck in the "mouseenter" animate position.
I'm trying to figure out how to get the animation to work as expected: When quickly mousing across both list items, the end result is that the svg elements are in the "mouseleave" position.
The above fiddle demonstrates the issue (in FireFox 5 and Chrome 13).

I would recommend using Raphael's native mouseover and mouseout
They rapidly respond to these events
You will also be able to tightly bind the animation with the keyword "this"

See this fiddle for the solution:
http://jsfiddle.net/UfP3C/4/
To prevent the svg animation from freezing in the mouseenter animate postion, set the animate in and out time to be the same value:
list_item.hover(function() {
my_rectangle.animate({
y: 0
}, 400, 'bounce');
},function(){
my_rectangle.animate({
y: 200
}, 400, 'bounce');
});
In the above code, the animate time is 400. Setting both animate times to the same value solves the issue.
See the above fiddle for the full code.
Note that using the Raphael hover function instead of the JQuery hover function (with different animate times) still shows the same issue and has the same solution.

Related

SVGPanZoom discards original viewBox

I am using SVGPanZoom to manage the zooming of an SVG image in my hybrid Android (for all intents and purposes the same behavior as in Chrome) app. While zooming works well I have found a strange issue. My original inline SVG element goes like this
<svg id='puzzle' viewBox='0 0 1600 770' preserveAspectRatio='none'
width='100vw' height='85.5vh' fill-rule='evenodd' clip-rule='evenodd'
stroke-linejoin='round' stroke-miterlimit='1.414'
xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://
www.w3.org/1999/xlink'>
Initially this SVG element is empty and gets populated programmatically from JavaScript at run time after which I initiate SVGPanZoom as follows
var panZoom = svgPanZoom('#puzzle',
{panEnabled:false,controlIconsEnabled:false,
zoomEnabled:true,dblClickZoomEnabled:true,onZoom:postZoom});
panZoom.refreshRate = 10;
panZoom.zoomScaleSensitivity = 0.02;
The problem I have run into is this - I want my SVG image to fill the available area, 100vw x 85.5vhcompletely to do which I instruct it via the preserveAspectRatio="none"attribute above along with the viewBox="0 0 1600 770" attribute. I have found that this works - so long as I don't use SVGPanZoom. As soon as I initiate panZoom thezoomBox`attribute gets stripped out and I end up with an image that does not quite behave in terms of its default stretching/filling behavior.
SVGPanZoom is widely used so I assume that this behavior is down to me not quite setting it up properly. Dipping into the code I have found SVGPanZoom creates a cacheViewBoxand then proceeds to remove the original zoomBox attribute.
Which is fine if after that zooming works and the original behavior of the application does not change which is not what I find. What am I doing wrong here?
I've also run into this issue recently. From my research, this is just how the library works. I chose to live with this limitation for now but I found a couple other libraries that may work the way you intend (I haven't tried them yet):
jquery.panzoom is a jquery library that provides this functionality and also has some nice features. I know many people try to avoid jquery but it's pretty small and may do what you want. It handles SVG but I don't know what it does with the viewBox attribute.
react-svg-pan-zoom is a react component which may be useful if you are working in react.
I've also tried the PanZoom library but this also suffers the same viewBox limitation.
A note for anyone running into this thread. In the end I abandoned SVGPanZoom and decided to eschew the route of using any pan/zoom library at all. At the same time I decided to completely stop using the SVG viewBox and handle all zooming/panning entirely on my own through SVG transforms. The core steps involved
Wrap the entire SVG contents in a group to make it easier to manage the transform. I use the id attribute gOuter for this group
Set an initial scale for the SVG to occupy the desired client rectangle. In my case I had an original viewBox of 0 0 1600 770 intended to occupy 100% of screen width and 85% of screen height. So my scaling was scaleX = 1600/window.innerWidth and scaleY = 770/)0.85*window.innerHeight).
Apply this initial transform to the wrapping outer group, gOuter.setAttribute('transform','0 0 scaleX,scaleY)
Now in order to zoom to a an object whose virtual top left hand coordinates in the original viewBox were Ox,Oy you would use the transform
gOuter.setAttribute('transform',
scale(scaleX,scaleY) translate(-Ox,-Oy) scale(2*scaleX,2*scaleY) translate(Ox,Oy))
to zoom in by a factor of x 2. The important things to understand here
In SVG transformations are applied right to left.
Here we are translating the zoom point to the top l.h.s. scaling and then translating it back to its original location.
The problem is that we also need to allow for the original level of zoom through the initial scaling so we tag that on as one last transform
This leaves you in complete control of the zooming process and as a fringe benefit the operation becomes considerably more smooth than when using a pan/zoom library.

Pixi.js Container.width does not return width

I'm having a bit of a problem: I'm trying to access the width of a container in which I've added a sprite to, but it seems to return as 1. However, when I inspect the object in the console, it gives me the proper width.
I wrote up a code pen showing the issue, but it goes something like this:
var container = new PIXI.Container();
app.stage.addChild(container);
var sprite = PIXI.Sprite.fromImage('https://i2.wp.com/techshard.com/wp-
content/uploads/2017/05/pay-1036469_1920.jpg?ssl=1&w=200');
container.addChild(sprite);
console.log(container.height);
console.log(container);
The first console log returns 1, while if I go into the object in the second log it gives me 141.
I'm trying to center the container like in the demo. The demo container returns the proper width, unless you try and do it for only one "bunny" (replacing bunny texture with internet image, also the for loop is commented out).
Any suggestions on a proper approach for this?
Cheers
There's a few things to address here.
Firstly, what the problem in your codepen is:
You're creating a texture from an image that has yet to be loaded.
Until the image loads pixi will not be able to give you the dimensions of it, and the container reports a width and height of 1 when you immediately query them. If you put the same console.log statement in a timeout then it will report the dimensions after the image has loaded and thus the dimensions will be accurate.
Logging out the object itself seems to work because when you examine the contents of it they've been updated to the correct values because the image has loaded by that point.
If the texture is already in the cache at the point that you create a new sprite using it then you won't have to wait before you can access its true dimensions.
Secondly, why the bunny example on pixi's site doesn't have the same problem:
Actually, it does. You just don't notice it.
The magic is in the bunny.anchor.set(0.5);. It lines 25 sprites with width and height of 1 out in a grid. By spacing them out, their container now has width and height of 160.
This container is now centered immediately based on its current dimensions, and then when the sprite textures finish loading and the sprites are updated with their new dimensions. Due to their anchor being set to 0.5, however, this means they remain centered despite their container now being larger.
You can play around with using a larger image than the bunny to exaggerate things and changing the anchor value, along with using the rerun code button rather than just refreshing the page. If you rerun the code the image being used for the texture remains cached by pixi so you get different results.
Lastly, how you would resolve this issue:
Loading your assets before creating sprites with them.
(Or at least waiting before they're loaded before querying their dimensions to position things)
You can find an example of the resource loader that pixi has here: http://pixijs.io/examples/?v=next-interaction#/basics/spritesheet.js

velocity.js reset svg animation

I'm using velocity.js (1.2.1) to create a fairly complex multi-step svg animation. I want users to be able to reset the animation at the end and run it again - by reset I mean if several elements have moved and had opacity changed they will all go back to their original starting positions and opacity, this can be instant, no need to play the animations in reverse. I want to create reusable code that can apply to multiple different svg animations.
There doesn't seem to be a way to rewind UI pack sequences. Other solutions I could think of:
Record the original attributes in a lookup object for each element then loop through and reset each element - I suspect this could be slow.
Clone the SVG, remove the original at the end and append the clone - this results in duplicate ID references in IE and the SVG turns black as a result
I tried the following solutions to cloning:
$(document).data("initialSVG", $("#svg1").clone(true));
// animate... then reset
$(document).data("initialSVG").replaceAll("#svg1");
Also:
var clonedSVG = $("#svg1").clone();
// animate... then reset
$("#svg1").remove();
$(".container").append(clonedSVG);
Both resulted in black svgs in IE.
Does anyone have an improvement on these approaches or an alternative suggestion using velocity.js please?

ImageOverlay and Rectangle Z index issue

I have a function that adds an imageOverlay and a semitransparent Rectangle on top of that image (so as to tint the image, and draw a keyline around it).
activeUserImage = new L.imageOverlay(imageUrl, imageBounds).addTo(map);
activeUserTile = new L.rectangle(imageBounds, {stroke: true, color: "#ffffff", opacity:1, weight: 1, fillColor: "#003572", fillOpacity: 0.7, clickable:true}).addTo(map);
this works great, but then I want to remove the image and rectangle with:
map.removeLayer(activeUserImage);
map.removeLayer(activeUserTile);
This seems to work well...
However when I try and add a second Image & Rectangle (using the same function) the rectangle SVG is being rendered underneath the image, so I don't see the colored overlay.
This seems to be because the element is being left behind from the first creation, and then when the image is being added a second time it appears in front of the SVG.
Q:
Is this a bug? Should the SVG element not be cleared too?
Can I adjust z-index of the image or SVG on creation?
should i be containing to rectangle in a different layer to the images? How?
Many Thanks
OK, so the Leaflet bringToFront() method didn't work, but instead I have used a bit of JQuery to force the same approach.
svgObj = $('.leaflet-overlay-pane svg');
svgObj.css('z-index', 9999);
This works, but still feels like a hack... however if (?) there is a bug in LEaflet, then maybe this will have to do???
Any better ideas?
The bringToFront() function alows you to bring layer to the top.
Search it in the docs.

Tipsy tooltip positioning on d3 in firefox, IE

I'm drawing d3 line graphs and tipsy tooltips attached to the circles.
The tooltips work fine in Chrome/Safari but in Firefox and IE when you hover over a point, while the respective tooltip does appear, it shows up outside the graph/SVG element in the top left hand corner of the screen (html element) instead of next to the point.
This is how I'm attaching the tooltip:
jQuery('g circle').tipsy({
gravity: 'w',
html: true,
title: function() {
return this.textContent;
}
})
Any advice on what I'm doing wrong would be much appreciated.
This patch adds proper SVG support to Tipsy.
tipsy uses offsetWidth and offsetHeight on elements. It assumes that such things work on SVG elements, unfortunately that assumption is incorrect outside of Chrome/Safari.
The CSSOM specification says that offsetWidth/offsetHeight are html element properties. It seems that Chrome/Safari have put these on their SVG elements but there's no specification that says that that should be the case.
You'll either need to fix tipsy to be cross-browser or get the author to do it. Using getTransformToElement and/or getBBox is probably what's needed.

Resources