I am trying to render text in the middle of an arc with Snap.svg.
This is possible with SVG like this:
<defs>
<path id="path1" d="M30 40 C 50 10, 70 10, 120 40 S 150 0, 200 40"/>
</defs>
<text text-anchor="middle">
<textPath xlink:href="#path1" startOffset="50%">
Text on a curved path
</textPath>
</text>
What I get is this (please ignore the specific coordinates):
<path d="M 352.5 14.1 A 338.4 338.4 0 0 1 645.5 183.3" id="path1"></path>
<text x="0" y="0" style="text-anchor: middle;">
<textPath xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path1">
Lorem Ipsum
</textPath>
</text>
when I do this in Snap.svg:
var labelArc = paper
.path('M 352.5 14.1 A 338.4 338.4 0 0 1 645.5 183.3')
.attr('startOffset': '50%'});
paper
.text(0, 0, 'Lorem Ipsum')
.attr({
'text-anchor' : 'middle',
'textpath' : labelArc
});
The problem is that the startOffset attribute is not passed to the textpath.
Setting this attribute via CSS did not work either.
Am I doing something wrong or does it require some fancy workaround?
Ok, I figured out a way to do it...
Simply assign the text to a variable and set startOffset like so:
var label = paper
.text(0, 0, 'Lorem Ipsum')
.attr({
'text-anchor' : 'middle',
'textpath' : labelArc
});
label.textPath.attr({ startOffset: '50%' });
There might be a more elegant way of doing it, but this is at least working.
Related
Is there a way to animate a value of dy attribute of SVG's tspan using JavaScript, ideally using Web Animations API?
The value of dy is known to be animatable using <animate /> element (as seen here):
<svg>
<text x="30" y="30">FairBridge
<animate attributeName="dy" from="0 0 0" to="0 -20 20" dur="1s" repeatCount="indefinite"/>
</text>
</svg>
I am wondering if it's possible to convert that animation to JavaScript.
For context, I've been using KeyframeEffect for all my previous animations, and would prefer to use it to animate the attribute value too in order to keep the animations code consistent.
In case there's absolutely no way to use KeyframeEffect, how would one go about animating a generic value of a tag attribute?
Only the presentation attributes can be animated using Web Animations API, because they are also CSS properties, XML attributes cannot.
In this example I can animate fill and rotate, but not x and dy. When I give the x property the wrong value (like '100' or 100) I get an error in the FireFox console -- strange when the right value has no effect. dy is just ignored.
var text1 = document.getElementById('text1');
var textKeyframes = new KeyframeEffect(
text1, [{
fill: 'red',
rotate: '0deg',
x: '0px',
dy: '0px'
},
{
fill: 'green',
rotate: '90deg',
x: '100px',
dy: '100px'
}
], {
duration: 2000,
fill: 'forwards'
}
);
var textAnimation = new Animation(textKeyframes, document.timeline);
textAnimation.play();
<svg viewBox="0 0 800 200">
<g transform="translate(100 100)">
<text id="text1" text-anchor="middle" dominant-baseline="middle"
font-size="30">FairBridge </text>
</g>
</svg>
I'm back to square one a bit because some new code I had was giving me issues. I've got this currently:
<svg version="1.2" viewBox="0 0 600 400" width="600" height="400" xmlns="http://www.w3.org/2000/svg" >
<text id="t1" x="50%" y="50%" text-anchor="middle" style="fill: white;">TESTING MY UGLY TEXT</text>
<script type="application/ecmascript">
var width=350, height=80;
var textNode = document.getElementById("t1");
var bb = textNode.getBBox();
var widthTransform = width / bb.width;
var heightTransform = height / bb.height;
var value = widthTransform < heightTransform ? widthTransform : heightTransform;
textNode.setAttribute("transform", "matrix("+value+", 0, 0, "+value+", 0,0)");
</script>
</svg>
It came from here: https://stackoverflow.com/a/22580176/1738522
I'm having a hard time putting it in the centre of my SVG since this didn't seem to do the trick: x="50%" y="50%" text-anchor="middle"
Any anyone please tell me how I might be able to do this?
EDIT: After a bit of research it seems:
the scale attribute also affects the coordinate system of the current item, so if you want the element to be in the same position, will need to divide both x and y positions by the scalar to get the same relative position - however I don't know how I should be attempting this.
I have a SVG Pattern which I use as fill in many other SVGs.
Of course, when I change attributes in the pattern afterwards, it is changed in every SVG using this pattern.
Is there a possibility to modify the pattern attributes in a specific instance/use only?
I would like to avoid creating the pattern in 100 different versions before.
EDIT with sample:
This is a sample pattern. I use this icon as fill for a circle on a country on a world map. The circle inside the pattern is used to set the background color.
<svg width="0" height="0">
<defs>
<pattern id="infantry_svg" patternUnits="objectBoundingBox" width="100% " height="100%">
<circle r="10" fill="transparent"></circle>
<path d="M 19.328,3.097 19.025,2.633 18.35,3.045 17.638,2.148 17.555,3.563 15.188,5.011 C 14.818,4.596 14.555,4.484 14.555,4.484 L 10.343,7.079 10.039,6.959 9.871,6.7 13.072,4.919 C 12.811,4.496 12.29,3.65 12.29,3.65 L 9.295,5.893 9.173,5.968 9.028,5.732 8.342,6.155 8.488,6.393 8.276,6.523 7.838,5.811 7.152,6.234 7.59,6.946 7.293,7.129 7.146,6.891 6.46,7.314 6.606,7.551 6.584,7.566 3.881,8.993 4.556,10.089 7.139,8.339 C 7.26,8.536 7.353,8.687 7.353,8.687 l -1.748,1.074 0.293,0.456 -0.373,1.185 -1.518,1.412 c 0,0 -2.772,1.469 -4.007,2.152 0.308,0.691 0.964,1.57 1.443,2.214 0,0 4.855,-4.879 6.706,-4.765 l 1.837,-1.136 c 0,0 -0.752,-0.704 -0.5,-0.864 1.583,1.752 3.062,1.704 3.062,1.704 0.442,-0.568 0.528,-1.221 0.78,-1.834 -1.195,-0.286 -1.226,-0.103 -2.25,-1.111 l 4.673,-2.88 C 15.821,6.048 15.48,5.467 15.48,5.467 l 3.848,-2.37 z m -9.83,8.165 -1.203,0.742 c 0,0 -0.281,-0.437 -0.435,-0.688 l 0.265,-0.16 c 0.168,0.165 0.502,0.417 0.834,0.213 l -0.124,-0.2 C 8.662,11.275 8.461,11.146 8.333,11.03 l 0.743,-0.453 0.422,0.685 z"
fill="#030104" transform="translate(1,1.1) scale(0.29)" stroke="white" stroke-width="0.4" />
</pattern>
</defs>
</svg>
Now when I set this pattern inside a circle on a country, it looks like this:
The circle on the country is generated like this with Snap.svg:
var circle = xs.circle(x, y, 4).attr({ id: "blabla", fill: "url(#infantry_svg)", stroke: "black", strokeWidth: 1 }).appendTo(xs.select('g'));
Directly after this, I set the background color of the pattern used for this circle:
$("#infantry_svg circle").attr({fill: troopOwner_color]});
Now every circle using "infantry_svg" pattern, has "troopOwner_color" as background color of the circle.
But I would like to change only this single instance of the pattern usage.
Whilst you can't do what you want with the specific requirement of the question, ie using a pattern. I'm not sure why you need to use a pattern.
Why not just use a 'use' element for the defs statement, and have a path & circle in there. Then you can just clone it and change the fill...
<defs>
<g id="infantry_svg" >
<circle cx="30" cy="30" r="40" stroke="red" stroke-width="5"/>
<path>path stuff</path>
</defs>
var g = s.g().use( Snap.select('#infantry_svg') );
var g1 = s.g( g ).attr({ fill: 'yellow' });
var g2 = g.clone().attr({ fill: 'blue', transform: 't200,0' })
var g2 = g.clone().attr({ fill: 'green', transform: 't100,0' })
jsfiddle
Just include the bits that don't change in the defs element, and create the bits that will be unique.
I'm new to javascript and SVG and I have no graphical programming background and this is my first project using all of those. So I started to make a custom element just like Mike Goodwin answer proposed and I ended with this code after editing it:
joint.shapes.tools.tooledElement = joint.shapes.basic.Generic.extend({
toolMarkup: [
'<g class="element-tools">',
'<g class="element-tool-remove"><circle fill="red" r="11" stroke="black" stroke-width="1"/>',
'<path transform="scale(.7) translate(-16, -16)" stroke="black" stroke-width="1" d="M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z"/>',
'<title>Remove this element from the model</title>',
'</g>',
'<g class="element-tool-link"><circle fill="green" r="11" cx="160" cy="40" stroke="black" stroke-width="1"/>',
'<path transform="scale(.7) translate(-16, -16)"/>',
'<title>creates a new link</title>',
'</g>',
'</g>'
].join(''),
defaults: joint.util.deepSupplement({
attrs: {
text: { 'font-weight': 400, 'font-size': 'small', fill: 'black', 'text-anchor': 'middle', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle' }
}
}, joint.shapes.basic.Generic.prototype.defaults)
});
Which works properly. Now I would like to draw some line on the green circle and to make the red circle into a red square. To achieve this I looked at this tutorial on paths to draw and this tutorial on basic shapes. But if I try to make a line on the green circle like this:
'<path transform="scale(.7) translate(-16, -16)" stroke="black" stroke-width="1" d="x y L 10 10 " />'
it won't draw anything. They do say " If your cursor already was somewhere on the page, no line is drawn to connect the two places." and that's why I omitted the "M" from path.
So here comes the first question: How can I draw the line on the center of the green circle without starting from the previous last point defined on any other path?
To make the red square I tried the exactly example from the second tutorial changing the fill (as a test):
//first line to test
<rect x="10" y="10" width="30" height="30" stroke="black" fill="red" stroke-width="5"/>
or
//second line to test
<rect x="60" y="10" rx="10" ry="10" width="30" height="30" stroke="black" fill="red" stroke-width="5"/>
The result of the first line would be the element at which the tools are being used to be draw again above itself like this:
And the second line would end up on nothing being show.
So here are the next questions:
Why did the results from the first line got like that?
and
How can I change the red circle into any other shape?
UPDATE:
About the line draw:
'<path transform="scale(.7) translate(-16, -16)" stroke="black" stroke-width="1" d="M150 150 H 5 V 5 H 5 z" />'
If I use this code for example, this is the result:
'<path transform="scale(.7) translate(-16, -16)" stroke="black" stroke-width="1" d="M150 150 H 5 V 5 H 5 z" />'
If I use this other code then the result will be:
The tutorial led me to believe that M is defining the start point but changing the translate(-16, -16) to something else made the correct start point be possible. So its the translate attribute combined with M that set the starting point.
The first question as been answered on update. As for the second question (concerning the red square conflicting with the element shape inside a rect) this was my work around:
Use the path to draw the tools elements instead of using basic shapes, that way it won't conflict with the element shape.
Example:
'<g class="element-tool-link"><circle fill="green" r="11" cx="160" cy="40" stroke="black" stroke-width="1"/>',
'<path transform="scale(.7) translate(-16, -16)"/>',
'<title>creates a new link</title>',
'</g>'
would turn into:
'<g class="element-tool-link">',
'<path d="M33 0 a 11 11 0 1 0 0.0001 0z " stroke="black" fill="lightblue" stroke-width="1"/>',
'<path transform="scale(.7) " stroke="black" stroke-width="1" d="M58,16 H 37 "/>',
'<title>creates a new link</title>',
'</g>'
where '<path d="M33 0 a 11 11 0 1 0 0.0001 0z " stroke="black" fill="lightblue" stroke-width="1"/>' defines a circle shape.
How can I add a text node inside javascript svg object . I am using this object , but I don't know how to add text into svg .
var circlemarker = {
path: 'M-5,0a5,5 0 1,0 10,0a5,5 0 1,0 -10,0 z',
fillColor:'yellow', //'#f39c13',
fillOpacity: 0.8,
scale: 3,
strokeColor: '#f7692c',
strokeWeight: 2,
data: 'My text' // this is not working
};
First of all, read more about SVG here
TL;DR SVG is just set of elements that represent a geometric figure.
Simple SVG looks like
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
<text x="30" y="40">aaa</text>
</svg>
Notice that <text></text> is an element, so it cannot be used inside <circle></circle> but underneath
So to add text, create new text element like this
var textmarker = {
x: 10,
y: 40,
fontSize: 10,
fontFamily: 'Verdana',
value: 'some text' //this might be tricky, as per docs, text element filled by innerHTML property, try data, text, innerHTML, etc
}