Svg text element vertical flip - javascript

I have problem with svg text element. I want to turn it over y axis.
For this, I using scale(-1,1) function.
var t = getMatrix(element);
t.scale(-1,1);
element.transform(t);
but element seems to be the same. Transformation does not work.
If I try to turn over horizontal axis, by scale(1,-1) function. It works
Please, What I doing wrong?

If you want to create transforms on an element in svg using the transform object, it is a five(5) step process:
1.) Attach the transform to your element.
var attachTransObject=myElement.transform
2.) Make a transform list for that attached transform.
var transObjList=attachTransObject.baseVal
3.) Start a Transform request object.
var requestTransformObj=mySVG.createSVGTransform()
4.) Perform/request the desired transform.
requestTransformObj.setScale(-1,1)
5.) Append the request to the transform list for the element.
transObjList.appendItem(requestTransformObj)
This may seem a bit obtuse, but once you understand these five steps, it is quite seamless.
For example, to flip a text element around the y axis, at its x,y point would be as follows:
var x=parseFloat(myText.getAttribute("x"))
var y=parseFloat(myText.getAttribute("y"))
var attachTransObject=myText.transform
var transObjList=attachTransObject.baseVal
var requestTransformObj=mySVG.createSVGTransform()
requestTransformObj.setTranslate(x,y)
transObjList.appendItem(requestTransformObj)
var requestTransformObj=mySVG.createSVGTransform()
requestTransformObj.setScale(-1,1)
transObjList.appendItem(requestTransformObj)
var requestTransformObj=mySVG.createSVGTransform()
requestTransformObj.setTranslate(-x,-y)
transObjList.appendItem(requestTransformObj)
This would build a transformation on the text element similar to:
transform="translate(153.785 94.71) scale(-1 1) translate(-153.785 -94.71)"

Thanks for help. I forgot to say that I use the library Snap svg.
It was bug there.
If someone will have the same problem, there is solution:
https://github.com/adobe-webplatform/Snap.svg/issues/153

Related

SVG: reverse children rotation on parent transform

I'm fiddling around with D3.js in this plunker.
It mostly does what I want, but I noticed a small inconsistency.
The idea is that the 2nd slider rotates the SVG elements in the container. As the elements within are actually text elements, I would like to have them displayed unrotated, and as such, I applied a transform: rotate to them with the symmetric value from the slider.
Although, I noticed that by doing that, the text elements don't rotate around their center, but rather around their top-left corner (I think). This is mostly visible when you observe a point near the edge and see how in transposes the edge on rotation.
I tried already setting both text-anchor and alignment-baseline to middle on these elements, hoping it would offset the text path, but apparently it doesn't change the point around which they pivot when rotated.
Additionally, should I try to set the rotate with pivot point coordinates, it boggles the effect entirely, by applying some translate to the elements, which I can't figure out.
Not sure if getBBox() could help me either in this subject, but I've considered maybe offsetting the points by half their width/height. This seems a bit convoluted though, and I was hoping for an easier/more elegant/cleaner fix.
Any thoughts?
Well, this is awkward. Right after I posted the question, I found an answer in D3.js docs: polygon.centroid().
Basically, I used the return value of this function as the transform: rotate pivot point coordinates and the offset is taken care of. Example (line 99 of the above plunker, rotate() function):
(...)
var textElement = d3.select(this);
return justTranslate+"rotate("+ -value+ textElement.centroid() +")";
(...)
Hope this helps anyone with the same issue.
EDIT: for some reason, Chrome's console says:
Uncaught TypeError: textElement.centroid is not a function
But the behavior is what I was looking for. Have no idea why.
EDIT2: ended up changing the above answer to a getBBox() approach, because the slider movement was bonked because of the previous edit error.
Changed it to this:
text.attr("transform", function(d){
var textElement = d3.select(this);
var current = textElement.attr("transform");
var alreadyRotated = current.indexOf('rotate');
var justTranslate = current.substring(0, alreadyRotated != -1 ? alreadyRotated : current.length);
var bbox = textElement.node().getBBox();
var point = [bbox.x + 0.5*bbox.width, bbox.y + 0.5*bbox.height];
return justTranslate+"rotate("+ -value +" "+ point +")";
});

Masked element in Snap SVG doesn't come into view when translated

I have a group of elements that are masked by a rect in SnapSVG and I want to translate the elements, bringing new ones into view (and hiding ones that are currently in view). The code is really simple - here's a codepen: http://codepen.io/austinclemens/pen/ZbpVmX
As you can see from the pen, box1, which starts outside the mask element (clip) should cross through it when animated, but it never appears. Moreover, box2, which should move out of the clipping area, remains visible.
This example seems to do a similar thing and has no problems: http://svg.dabbles.info/snaptut-masks2
Here's the code from codepen:
var t = Snap('#target')
var clip=t.rect(200,200,200,200).attr({fill:'#fff'})
var box1=t.rect(300,100,50,50).attr({fill:'#000'})
var box2=t.rect(300,300,50,50).attr({fill:'#000'})
var boxgroup=t.group(box1,box2)
boxgroup.attr({mask:clip})
boxgroup.animate({transform:'t100,300'},2000)
I notice that the svg.dabbles examples translates the clip region by 0,0 at one point, but adding something like that doesn't seem to get me anywhere.
Ok, I figured this out thanks in part to this really great article about SVG transforms: http://sarasoueidan.com/blog/svg-transformations/
The upshot is that when I translate the box group, it takes the mask with it. This is a little confusing to me still - I guess the mask attribute is causing this somehow? Anyways, the solution is to apply an opposite translation to the mask to keep it in place. Check the pen to see it in action but basically I just had to add:
clip.animate({transform:'t-100,-300'},2000)
The tricky part of this is that you now need to synchronize the movement of the mask and the movement of the box group.
edit - I now demonstrate how synchronization can be achieved using snap's set.animate method on the codepen.

Adding c3.js padding without cutting off graph

This is in response to the following question, How to remove padding in c3.js?, where the answer that was provided solves this issue, but also raises another issue -- the buttons on the graph are cut off at the end --
How would I get there to be no padding and the buttons not to be cut off, for example, it should look like:
The dots are getting clipped off because of the clip-path set on the chart layer. You just have to remove it. You can use D3 for this, like so
d3.select(chart.element).select("." + c3.chart.internal.fn.CLASS.chart).attr("clip-path", null);
where chart is your C3 chart object
Fiddle - http://jsfiddle.net/zds67nh1/
However you most probably want the dots to appear above the axis layer. For that you need to detach and attach the chart layer (in SVG, the z-index is determined by the order - the last of the siblings come on top. So you have to basically move it to the end of the siblings list), like so
var chartLayer = d3.select(chart.element).select("." + c3.chart.internal.fn.CLASS.chart);
var chartLayerParentNode = chartLayer.node().parentNode;
var chartLayerNode = chartLayer.remove();
chartLayerParentNode.appendChild(chartLayerNode.node());
chartLayer.attr("clip-path", null);
Fidle - http://jsfiddle.net/7e1eL22f/

Tracking mouse position of a jqplot vertical line chart

I want to create a jqPlot line chart which has the ability to change orientation between vertical and horizontal orientation. I was able to achieve this using CSS rules, by rotating the div element containing the chart.
My work up to now: http://jsfiddle.net/GayashanNA/A4V4y/14/
But the problem is I also want to track the mouse-pointer and mouse clicks on points on chart after the orientation is flipped because i want to annotate those points. I am unable to do this when the chart is in vertical orientation. Can anyone suggest a method to do this? Or am i approaching the problem in a wrong way?
(Note: I am able to do this in horizontal orientation, you can observe it if you try to click on a point on the above chart.)
Thanks and help is much appreciated.
I've never used jqPlot, but I guess your problem is trying to use css rotate(), since the cursor plugin is using the mouse position to determine where to draw the lines, and element's size doesn't change when transformed by rotate(), it still have the same width and height values.
If you take a look at the code, you will see:
if (c.showVerticalLine) {
c.shapeRenderer.draw(ctx, [[gridpos.x, 0], [gridpos.x, ctx.canvas.height]]);
}
if (c.showHorizontalLine) {
c.shapeRenderer.draw(ctx, [[0, gridpos.y], [ctx.canvas.width, gridpos.y]]);
}
So it seems like the library is always drawing the lines based on mouse position over the original element, which of course, won't match the position after being transformed by rotate(), and XY coordinates are going to be transformed to YX after rotate().
I would try to change the size of your original element, though I don't know if the library lets you specify in which sides are the labels going to be drawn.
I finally found a solution for the problem. But i had to change jqPlot library to achieve this. To help anyone else who run in to the same problem, i'll put my solution here.
First i had to insert the following code in to the jqPlot class of the jquery.jqplot.js file, which is the core library.
function jqPlot() {
//add the following code segment
var verticallyOriented = false;
this.setVertical = function(state){
verticallyOriented = state;
}
//don't change other code that isn't mentioned here
//now you have to change the logic in the getEventPosition function
//to make sure the new orientation is detected
function getEventPosition(ev) {
//change the line starting with var gridPos = ...
//to the following code segment
//depending on the orientation the event position calculating algorithm is changed
if(verticallyOriented){
var gridPos = {x:ev.pageY - go.top , y:plot.eventCanvas._elem.height() - ev.pageX + go.left};
} else {
var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
}
//no change to other code is needed
}
}
You can view a working example here: http://jsfiddle.net/GayashanNA/yZwxu/
Gist for the changed library file: https://gist.github.com/3755694
Please correct me if i have done something wrong.
Thanks.

Raphael.js - Using fill attribute for a circle, makes the element in the circle disappear. How to avoid that?

Please see the code below. I am trying to draw a circle around a path (an icon made by Raphael.js founder, Dimitry) and then fill the circle with a color. This, however, paints on the top of the path. If I could first draw the filled circle and then draw the path, this would be solved. But I need to reference the path because I need to find its center, in order to draw the circle. Can anyone please suggest how to do this? My code is below.
Thanks.
<script>
var myVar = {
s: 1,
pw: 850,
ph: 450
}
</script>
<script>
var paper = new Raphael('figSellerBuyer', myVar.pw * myVar.s, myVar.ph * myVar.s);
var market = paper.path(paths.marketBoundary);
market.attr({fill: "rgb(75,245,75)", stroke: "None"});
var humanIcon = paper.path("M21.021,16.349c-0.611-1.104-1.359-1.998-2.109-2.623c-0.875,0.641-1.941,1.031-3.103,1.031c-1.164,0-2.231-0.391-3.105-1.031c-0.75,0.625-1.498,1.519-2.111,2.623c-1.422,2.563-1.578,5.192-0.35,5.874c0.55,0.307,1.127,0.078,1.723-0.496c-0.105,0.582-0.166,1.213-0.166,1.873c0,2.932,1.139,5.307,2.543,5.307c0.846,0,1.265-0.865,1.466-2.189c0.201,1.324,0.62,2.189,1.463,2.189c1.406,0,2.545-2.375,2.545-5.307c0-0.66-0.061-1.291-0.168-1.873c0.598,0.574,1.174,0.803,1.725,0.496C22.602,21.541,22.443,18.912,21.021,16.349zM15.808,13.757c2.362,0,4.278-1.916,4.278-4.279s-1.916-4.279-4.278-4.279c-2.363,0-4.28,1.916-4.28,4.279S13.445,13.757,15.808,13.757z")
humanIcon.attr({fill: "rgb(75,75,75)"}).scale(2.5,2.5);
humanIcon.translate(40,40);
var bbox = humanIcon.getBBox();
var xcenter = Math.round(bbox.x + bbox.width/2.0);
var ycenter = Math.round(bbox.y + bbox.height/2.0);
var circle = paper.circle(xcenter, ycenter, 40);
circle.attr({fill:"white"});
</script>
After doing a lot of search on Google, I found the answer here on Stackoverflow. At the time, I did not save the link to the answer and I don't remember it. If anyone does find it, please edit this answer and post it. However, I did record the solution and here it is:
One can use the insertBefore() and insertAfter() functions in Raphael. In the example code given in the question, one can do this by changing the last line to:
circle.attr({fill:"white"}).insertBefore(humanIcon);
Thanks to those who responded.
Try changing the order in which the two are drawn. That or look over the Raphael docs to see if there is a "Z-index" attribute that can be used to permanently modify the position of the path on the stack of render-able layers.
Edit: I didn't read your explanation well enough. If you could declare your object without drawing it perhaps you could grab the BBox, and then draw it later.
Edit, Edit: SVG has a "defs" tag to create objects without drawing them, so it stands to reason that Raphael can handle it too.
Try using Raphael's Element.toBack() and Element.toFront().
In your case, it sounds like you want to draw the path, then draw the circle, then call either circle.toBack() or path.toFront().

Categories

Resources