I am looking to animate a path element inside an SVG using the Snap SVG library.
I saw Snap has a few methods related to paths. Some include: Snap.path.getBBox(), Snap.path.getSubpath(), and Snap.path.map()
I was trying to wrap my head around how to animate the path so that it starts as not showing, and over time reveals more and more of the path.
My question is similar to this (but this question is not specific to Snap, although I assume the principal is similar): How can I animate a progressive drawing of svg path?
And here is a working demo so it is clear what I am looking to accomplish (end result): http://jsfiddle.net/lollero/THGuz/
I made a JSFiddle to play around with my particular path element: http://jsfiddle.net/xhdjbj1r/1/
Any help or guidance is appreciated.
I have a solution that shows some buggy behavior (blinking extra lines) in Chrome and Opera
var yourElement = s.select('path#test');
var pathLength = yourElement.getTotalLength();
yourElement.attr({
'stroke-dasharray': '' + pathLength + ' 0'
});
Snap.animate(0, pathLength, function(t){
yourElement.attr({'stroke-dasharray': '' + t + ' ' + (pathLength - t)});
}, 10000);
The idea is pretty simple - animate stroke's dasharray from zero to full length. I hope this could be a good step for a clean solution
Related
I'm using the Swiper API. When there's any d3 Chart embedded, the brush receives senseless mouse coordinates, in any case not relative to the container where the click occurs. (That's in fact at least the surrounded svg.)
I'm trying to find a solution but I don't know how I can force d3.brushX to use mouse coordinates which are really relative.
I don't know whether this is a bug or not, it has probably not really to do with the brush itself, rather how the browser pass mouse clicks top-down the DIV's until the SVG will be reached.
Here's the Fiddle.
(just for the annoying code rule:)
// Add brushing
var brush = d3.brushX()
The second slide contains an embedded d3 line chart example, taken from here.
The fiddle works only in Chrome 75+.
Not in Firefox 68+ nor in Edge 44+.
Running the chart example standalone, it works in all available browsers. So I designate this post for Swiper and D3 hopefully to get a hint for an solution.
According to the problem here I found out, that I can change the behavior in the point.js routines an an workaround.
If a D3 chart makes use of an brush **and ** the SVG of the chart element is embedded by a surrounding DIV with an explicite width, mouse clicks will not be interpreted correctly in Firefox or Edge. In Chrome it works perfectly.
I changed the code like this to achieve that it works in FF and Edge, but lose functionality with Chrome:
function reverseTraversal(node,targetTagName) {
var p = node;
while(p.tagName != targetTagName) p = p.parentNode;
return p;
}
function point(node, event) {
var svg = node.ownerSVGElement || node;
if (svg.createSVGPoint) {
var point = svg.createSVGPoint();
var p = reverseTraversal(node,"DIV");
var rect = p.getBoundingClientRect();
point.x = event.clientX + rect.width, point.y = event.clientY;
point = point.matrixTransform(node.getScreenCTM().inverse());
return [point.x, point.y];
}
var rect = node.getBoundingClientRect();
return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
}
As you can see, I have to traverse backwards until the closest DIV will be reached, get the bounding and add its width to the clientX coordinate.
Without adding the fixed width, the brush is unusable in the particular case.
To get working with all the browsers, maybe a switch is necessary.
It's not a perfect solution, just a workaround for d3.brushX behavior.
I implemented something based on http://bl.ocks.org/hlvoorhees/5986172#index.html. After the latest browserupdates I realized that the textnodes in my Application as well as the ones in the example are now rendered as black boxes (one week ago they looked just fine).
The label creating code is (from the example above)
var tickLabels = ticks.selectAll("billboard shape text")
.data(function(d) { return [d]; });
var newTickLabels = tickLabels.enter()
.append("billboard")
.attr("axisOfRotation", "0 0 0")
.append("shape")
.call(makeSolid)
newTickLabels.append("text")
.attr("string", scale.tickFormat(10))
.attr("solid", "true")
.append("fontstyle")
.attr("size", tickFontSize)
.attr("family", "SANS")
.attr("justify", "END MIDDLE" );
tickLabels // enter + update
.attr("string", scale.tickFormat(10))
tickLabels.exit().remove();
The X3DOM-Logdiv doesn't contain any errors or warnings. So I don't know if X3DOM is breaking things or D3, but as it's actually a X3DOM-Node I would locate the problem there.. Neither X3Dom nor D3 updated their libraries lately - however I tried out older versions, but still broken text.
Tested on
Chromium Version 45.0.2454.101 Ubuntu
Firefox 42.0
some current safari (can't tell exactly, sorry)
I had a similar issue while i was trying to get smooth text rendering using big font sizes and a transform to scale them down again. I worked arround the black rectangles by using a small font size and the quality attribute and no transform arround it.
For you it could look like like this:
.append("fontstyle")
.attr("size", 0.5)
.attr("quality","10")
.attr("family", "SANS")
.attr("justify", "END MIDDLE" );
The quality attribute is used for oversampling, so you get nice text rendering even if your text nodes are rather small: http://doc.x3dom.org/author/Text/FontStyle.html
I'm not sure if this is a x3dom bug or if this is intended behaviour for text nodes, which are too small.
I am playing with kineticjs with a draggable and zoomable stage and I'd like to know if there is a way to get the stage.getAbsoluteMousePosition() as we can have the absolutePosition of a Node.
Here is the jsfiddle showing a use case, note the position of the tooltip when you zoom in/out.
The interesting part is here:
circle.on('mouseover mousemove',function(){
var mousePos = stage.getMousePosition();
tooltip.setPosition(mousePos.x-stage.getAbsolutePosition().x,mousePos.y-stage.getAbsolutePosition().y);
tooltip.setVisible(true);
tooltip.moveToTop();
layer.draw();
});
I am having trouble to make it work and I believe with the getAbsoluteMousePosition would fix it.
Best,
Edit: This question is outdated.
Outdated Answer
Ok, fixed it myself even thought I don't use the absoluteposition I wanted to.
Here is the jsfiddle, the proper way of doing it was like this:
circle.on('mouseover mousemove', function () {
var mousePos = stage.getMousePosition();
tooltip.setPosition(mousePos.x/ui.scale-stage.getAbsolutePosition().x/ui.scale+stage.getOffset().x,
mousePos.y/ui.scale-stage.getAbsolutePosition().y/ui.scale+stage.getOffset().y);
tooltip.setVisible(true);
tooltip.moveToTop();
layer.draw();
});
If I understand correctly, if you want to get the absolute position of your mouse pointer to the screen this is how you could do it:
circle.on('mouseover mousemove', function (e) {
var mousePos = stage.getMousePosition();
var absoluteX = e.screenX;
var absoluteY = e.screenY;
tooltip.setPosition(mousePos.x - absoluteX, mousePos.y - absoluteY);
tooltip.setVisible(true);
tooltip.moveToTop();
layer.draw();
});
This doesn't solve your answer though as the (x,y) coordinate of the mouse should be relative to the canvas, not your screen. I'm not exactly sure why tooltip.setPosition(mousePos.x, mousePos.y); wouldn't work, the zoom must be messing up the mousePos coordinates relatively somehow.
Anyways, if you only need the tooltip to hover above the correct node, and not follow the mouse position, than this should work for you:
tooltip.setPosition(this.getX(), this.getY());
And if you need, you could offset it by a certain amount for example half the height (radius).
tooltip.setPosition(this.getX(), this.getY()-this.getHeight()/2);
http://jsfiddle.net/projeqht/nDpYr/
I want to create an SVG animation, yet render it to Canvas. I know about CanVG, but I'm not certain it does what I need: Displaying SVG elements animated using javascript within a Canvas div, rather than a 'native' SVG window. When I look at CanVG example code, it seems to point to an SVG file, rather than utilizing SVG code that is part of the same file, & being manipulated in realtime by javascript. Any help?
I faced this issue me too some time ago and tried every possibilities mentioned above, unfortunately without results. After some research I found a method to inject SVG into Canvas, with a little pain and extra work it's possible. So the solution is to define svg elements then push it to an array then render that SVG as an image.
Here is a short code snippet:
SVG part:
var data = "data:image/svg+xml," +
"<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400'>" +
"<foreignObject width='100%' height='100%'>" +
"<div xmlns='http://www.w3.org/1999/xhtml' style='font: bold 160px Myriad Pro, Arial, Helvetica, Arial, sans-serif'>" +
"<span style='color:rgb(32,124,202); opacity: 0.8; -webkit-text-stroke-color: white; -webkit-text-stroke-width: 2px'>" + letters[pos] + "</span>" +
"</div>" +
"</foreignObject>" +
"</svg>";
characters.push(data);
and JS part:
var letters = [...] // a pool of characters
var images = new Array(letters.length);
for (var i=0; i< letters.length; i++) {
var textPosition = WIDTH/2 - (letters.length * CHAR_DISTANCE)/2;
var txt = new Text(letters[i], textPosition + (i*CHAR_DISTANCE), HEIGHT/2,
(Math.random() * 3) + 5 , (-Math.random() * 10) + 5);
images[i] = new Image();
images[i].src = characters[i];
textArray.push(txt);
}
You may check the whole source code here: https://esimov.com/experiments/javascript/404/
There are several libraries (like CanVG) that merely turn SVG into Canvas drawing commands. They won't help you much with animation.
For animation you're going to have to do it on your own. You could attempt to use CanVG to draw the SVG onto the canvas as it is being animated, using a timer to update the canvas, but this is supremely messy and cannot be done unless the browser also supports SVG (and if it does, why would you want to do it?)
If you want to manipulate the SVG in real time you're going to have to stick with SVG proper. Otherwise you need to roll all your own state and interactivity on the canvas. No shortcuts there[1], sorry.
[1] Well, there are a few libraries for canvas object interaction that allow you to use SVG objects as their objects, such as fabric.js. But depending on what you want this may be nowhere near enough.
I've written a home-brew view_port class for a 2D strategy game. The panning (with arrow keys) and zooming (with mouse wheel) work fine, but I'd like the view to also home towards wherever the cursor is placed, as in Google Maps or Supreme Commander
I'll spare you the specifics of how the zoom is implemented and even what language I'm using: this is all irrelevant. What's important is the zoom function, which modifies the rectangle structure (x,y,w,h) that represents the view. So far the code looks like this:
void zoom(float delta, float mouse_x, float mouse_y)
{
zoom += delta;
view.w = window.w/zoom;
view.h = window.h/zoom;
// view.x = ???
// view.y = ???
}
Before somebody suggests it, the following will not work:
view.x = mouse_x - view.w/2;
view.y = mouse_y - view.h/2;
This picture illustrates why, as I attempt to zoom towards the smiley face:
As you can see when the object underneath the mouse is placed in the centre of the screen it stops being under the mouse, so we stop zooming towards it!
If you've got a head for maths (you'll need one) any help on this would be most appreciated!
I managed to figure out the solution, thanks to a lot of head-scratching a lot of little picture: I'll post the algorithm here in case anybody else needs it.
Vect2f mouse_true(mouse_position.x/zoom + view.x, mouse_position.y/zoom + view.y);
Vect2f mouse_relative(window_size.x/mouse_pos.x, window_size.y/mouse_pos.y);
view.x = mouse_true.x - view.w/mouse_relative.x;
view.y = mouse_true.y - view.h/mouse_relative.y;
This ensures that objects placed under the mouse stay under the mouse. You can check out the code over on github, and I also made a showcase demo for youtube.
In my concept there is a camera and a screen.
The camera is the moving part. The screen is the scalable part.
I made an example script including a live demo.
The problem is reduced to only one dimension in order to keep it simple.
https://www.khanacademy.org/cs/cam-positioning/4772921545326592
var a = (mouse.x + camera.x) / zoom;
// now increase the zoom e.g.: like that:
zoom = zoom + 1;
var newPosition = a * zoom - mouse.x;
camera.setX(newPosition);
screen.setWidth(originalWidth * zoom);
For a 2D example you can simply add the same code for the height and y positions.