How to animate SVG group as a whole? - javascript

I am using Snap.svg to make animation. I have two elements (one circle and one ellipse) to draw currently. The elements will translate together and the ellipse also rotate and change shape. I can use Element.animate() function in Snap.svg to animate each element, but it is very tricky to keep them transform harmonically. So how can I animate the elements as a whole? The html snippet is put on https://gist.github.com/dongli/8124267.
PS: The attributes cx and cy of centroid are not updated after each animation frame. I expect them will be changed.
Thanks for any help!

Snap provides a group function (also a sets, but group is a proper svg element, so I think typically preferred). So you have one group element and and perform one transfer can do something like this...
var s = Snap(400,400);
var r = s.rect(100,100,100,100,20,20).attr({ stroke: '#123456', 'strokeWidth': 20, fill: 'red'});
var c = s.circle(50,50,50).attr({ stroke: '#123456', 'strokeWidth': 20, fill: 'blue', });
var g = s.group(r,c);
g.animate({ transform: 'r360,150,150' }, 1000, mina.bounce );
Working jsfiddle here. http://jsfiddle.net/n8Een/2/ . If you're still having problems, pop your code on a jsfiddle.

Related

Snap SVG scale and centre loaded SVG file

I have trouble with scaling and centring SVG loaded from file.
It's #svg container to which I load SVG files with Snapsvg:
It's loaded SVG:
And when I'm scaling it:
How browser inspecting it:
My JS:
var s = Snap("#svg");
var g = s.group();
var tux = Snap.load("svg/roulette.svg", function ( loadedFragment ) {
g.append( loadedFragment );
var firstScene = new Snap.Matrix();
firstScene.scale(1.5);
g.animate({ transform: firstScene }, 0);
});
How can I scale my roulette and centre it in svg#svg element?
Working example:
http://plnkr.co/edit/DE1dds8n3ULOLQRATnLY?p=preview
It may depend if you need to scale it and calculate where it should go, or if a responsive type solution could work. I would explore this first, and go for a calculated option otherwise.
As you haven't shown a running example, its hard to be sure though. I would post up a jsbin with the file getting loaded so others could play if the following doesn't work.
In the meantime, you could try something like this inside the load function...it may not work though depending on what Layer_1 and other svg parents though, and setting the viewBox to match the inner SVG.
s.select('#wheel'); // or whatever ID it has, or give it one
.attr({ width: '100%', height: '100%', viewBox: "0 0 600 600" });
As an aside, you don't need to worry about Matrices, just use the Snap transform strings, so you don't need that code...eg
g.animate({ transform: 's1.5,1.5' }, 1000)
is all you would need to animate scale by 1.5

Clickable outline around Snap SVG path

Currently I create a small modeling tool using Snap SVG.
var p = Snap.path(pathString).attr({fill:'none', stroke:'black', strokeWidth:1});
creates connections between other elements.
Every element, including the paths are clickable. Since it is hard to click exactly the path I tried to create an invisible outline around the path using Snap.filter.shadow so that a click event could be triggered for the path:
var filter = this.paper.filter(Snap.filter.shadow(2,2,1));
filter.click(function() {
do_as_if_i_clicked_the_path();
});
p.attr('filter', filter);
But adding a click event to the filter doesn't work.
Is there any way to create an invisible outline that is connected to the path so I can add events on it?
One possibility that works quite well, is to clone the object you want to have an event on, and either scale it up, or make the strokeWidth larger, and then have a very low opacity (you can increase this just for testing and then reduce it to almost invisible).
If you add it to a group like this example, you can just put a drag on the group, so no need to remove it or anything (unless you need the clone not to be there sometimes, which makes sense).
jsfiddle
var s = Snap("#svg");
var myLine = s.path('M0,0L100,100').attr({ stroke: 'black', strokeWidth: 1 });
var myClone = myLine.clone().attr({ strokeWidth: 50, opacity: 0.01 });
var myGroup = s.g( myLine, myClone );
myGroup.drag();

KinetikJS Keep bounds while dragging an object

Heyhey,
while learning JS and using KineticJS I am stuck with a little problem.
As Iam not able to get my own code into a fiddle right now lets take this example:
http://jsfiddle.net/m1erickson/n5xMs/
If we make the white rect also draggable:
var white = new Kinetic.Rect({
x: 20,
y: 20,
width: 300,
height: 300,
fill: 'white',
stroke: 'black',
strokeWidth: 2,
draggable: true
});
the dragBoundFunc won´t work correctly anymore if the white rect is moved around. While debugging this with coda2, I found that when the white rect is dragged around, that the x and y attrs do not change.
Why is that so? How do I manage to make the dragBoundFunc work for moving parent-shapes?
thanks in advance for your help.
First of all:
I found that when the white rect is dragged around, that the x and y attrs do not change.
No, they don't, because they're defined statically. If you wanted changing values, you'd want to get the updated values within the dragBoundFunc. It might be as simple as calculating them the same way they are now, but inside that function.
How do I manage to make the dragBoundFunc work for moving parent-shapes?
In your example the white rectangle isn't a parent of anything, it's just a shape drawn on the same stage as the orange rectangle. What you want to do is make the white shape background and the orange rectangle both children of the same node, and drag that. The most direct way to do this here would be to make the layer itself draggable, e.g.:
var layer = new Kinetic.Layer({draggable: true});
But how you do this is arbitrary. The white and orange boxes might also be children of another Kinetic.Container, for example.

set background color in chart

I am using a theme, but i want to set background color around the chart to white or transparent or something.
I am trying this code:
var myTheme = dojox.charting.themes.PlotKit.orange;
myTheme.fill= "white";
chart10.setTheme(myTheme);
chart10.addPlot("default", {
type: "Pie",
labelOffset: -30,
radius: 150,
startAngle: 45,
font: "normal normal 10pt Tahoma",
fontColor: "black",
labelWiring: "grey",
labelStyle: "columns",
htmlLabels: true,
plotarea: { fill: "#000" }
});
But this code didn't works, none alteration is visible.
How can i set the color background ?
I believe you have to set both the chart.fill and plotarea.fill properties.
myTheme.chart.fill= "white";
myTheme.plotarea.fill = "white";
If you do console.debug(myTheme) you can inspect all the properties of the theme object. I usually have to experiment a little before I find the right ones.
I know this is old, but I had a situation where I was creating a pie chart and needed to change the background color behind the pie chart. I tried this solution, but it didn't work because I wasn't assigning a theme. I am creating it this way:
var pieChart = new Chart("myDIV");
pieChart.addPlot("default", {type: "Pie"});
pieChart.addSeries("Series A", pieString);
My pieString is where I am combining my variables together to form the pie. Basically, I concatenate a series of statements like this:
{y:200, tooltip: "layer", color:"red", text: "myText"}
I join those strings together and assign it to my pieString variable.
When I set up my chart, I got a standard white background behind it, which doesn't work well for my colored background since I wanted it to blend. There are two rectangles that make up the background. You can change the larger and furthest back one through pieChart.fill = "something", but the inner one didn't change.
The way I solved this for me is like this.
function ClearChartBackground() {
var sumDivRects = document.getElementById("chartDIV").getElementsByTagName("svg")[0].getElementsByTagName("rect"); //so I am getting the rectangles that are part of my chart div
var firstRect = sumDivRects[0];
var secondRect = sumDivRects[1];
firstRect.attributes.item(1).value = "0";
secondRect.attributes.item(1).value = "0";
//this drills down to the 'fill-opacity' attribute that can then be changed to make it transparent
}
As you can see in my photo, the original background was white, which doesn't work well on my gray background. After running my function, it is changed to transparent.
I am posting this because it took me a pretty long time to find out how to change this since I am having Dojo render it for me. I came across this post, but I didn't get much help from it since I wasn't doing a theme.
dojox.charting.themes.Claro.chart.fill = "#F1F1F0"
dojox.charting.themes.Claro.plotarea.fill = "#F1F1F0"
dojox.charting.themes.Claro.chart.stroke = "#F1F1F0"
// Set the theme
chart.setTheme(dojox.charting.themes.Claro);
Using Jquery, you can do something like this:
$("#chartNode svg rect:eq(0)").attr("fill", "0");
$("#chartNode svg rect:eq(0)").attr("fill-opacity", "0");
So, basically you are accessing the element and changing the attribute manually. It is not very efficient way to do with, but it will do the trick.

Raphael svg invert y-axis coordinates

I am using the Raphael library from http://raphaeljs.com/ and work on a chart library. For this library it is useful when the Y-axis are inverted. Now 0,0 is at the top left but I want it to be at the bottom left.
There is a possibility to apply a scale matrix to an element but I want the coordinates to be inverted for whatever I draw. Any clues?
The only way I could figure out to do this was to apply a negative scaling to the svg element using CSS (see this fiddle). (I used jQuery to add the styles).
This is not without problems, though. For example, text is going to be mirrored, unless you do something to un-mirror it (like applying the invert() method I added to elements using Raphael.el):
Raphael.el.invert = function() {
this.transform('s1,-1');
};
Also, if you are going to be interacting with the elements using your mouse, you will have to tweak things. Note that the black circle uses a pretty standard mouseMove function, but it doesn't work - it moves in the wrong direction in y. So you have to do something like I did with the other circles:
function cMove(dx, dy, x,y) {
this.attr('cx', x);
this.attr('cy', paperHeight - y);
};
In short, this is not at all elegant, and no other things I tried were really any better. I know this isn't what you want to hear, but I would recommend getting used to the coordinate system as it is, unless you just plan on displaying static charts.
One small issue with Mike C's resolution is that you have to know if the text is going to be inverted in the end when you create the text. If you want to ensure right-side-up text at the end (after applying other transformations) I found it works well to alter the text element's .transform() to flip the scale of text to right side up at the end.
function InvertText(ObjSet){
// This function resets the inversion of text such that it is always right side up
// ObjSet is a raphael paper.set() object
for (var i=0; i<ObjSet.items.length; i++){
var ThisObj = ObjSet.items[i];
if (ThisObj.type == 'text'){
var tArr = ThisObj.transform();
// Find the scaling factor
for (var j=0; j<tArr.length; j++){
if (tArr[j][0] == 's'){
tArr[0][2] = 1;
break;
}
}
ThisObj.transform(tArr);
}
}
}
You can use like this:
var ObjSet = paper.set().push(
paper.text(0,10,'FirstText'),
paper.path('M,0,0,v,100,h,20,v,-100,h,-20'),
paper.circle(0,0,5)
);
//Flip everything on the y-axis
ObjSet.transform('s,1,-1, T,100,100');
// Make the text right-side-up
InvertText(ObjSet);
Here's how to do it just with RaphaelJS transforms, no CSS transforms.
var SCALE = 2;
var paper = Raphael(0, 0, 400, 700);
// box notched at bottom-center and right-center
var p = paper.path("M0,0 L100,0 L100,40 L90,50 L100,60 L100,100 L60,100 L50,90 L40,100 L0,100 Z");
var bounds = p.getBBox();
p.attr({
stroke: 'none',
fill: [90, '#578A6E', '#34573E'].join("-")
})
.transform("t"+ (-bounds.width/2) +","+ (-bounds.height/2) +
"s"+ SCALE +","+ (-SCALE) +
"t"+ (bounds.width/2) +","+ (-bounds.height/2));
Raphael applies scale transforms from the center of the element's bounding box, rather than its origin. To invert the y-axis, offset before scaling, then offset again after.

Categories

Resources