Hello the answer to this question with this fiddle is perfect for what I need to do - drag something along a path.
However they used Rapael - I'd like to update things and use snap.svg instead.
Replacing the reference from Raphael to Snap in this answer doesn't work - does anyone have any ideas why?
This is as far as I've got in this codepen - I want to have a 10 point shape - the outer points of which can move a long a path to the centre.
var s = Snap("#svg");
var paths = [], points = [], circles = [], lines = [], l = 0, searchDl = 1;
var dist = function (pt1, pt2) {
var dx = pt1.x - pt2.x;
var dy = pt1.y - pt2.y;
return Math.sqrt(dx * dx + dy * dy);
};
var gradSearch = function (l0, pt, path) {
var totLen = path.getTotalLength();
l0 = l0 + totLen;
var l1 = l0,
dist0 = dist(path.getPointAtLength(l0 % totLen), pt),
dist1,
searchDir;
console.log(dist0);
if (dist(path.getPointAtLength((l0 - searchDl) % totLen), pt) >
dist(path.getPointAtLength((l0 + searchDl) % totLen), pt)) {
searchDir = searchDl;
} else {
searchDir = -searchDl;
}
l1 += searchDir;
dist1 = dist(path.getPointAtLength(l1 % totLen), pt);
console.log(dist1);
while (dist1 < dist0) {
dist0 = dist1;
l1 += searchDir;
dist1 = dist(path.getPointAtLength(l1 % totLen), pt);
}
l1 -= searchDir;
console.log(l1 % totLen);
return (l1 % totLen);
};
var startCircleToPoly = function () {
// storing original coordinates
this.ox = this.attr("cx");
this.oy = this.attr("cy");
this.attr({opacity: 1});
};
var moveCircleToPoly = function (dx, dy) {
var tmpPt = {
x : this.ox + dx,
y : this.oy + dy
};
var path = paths[this.data('int')];
// move will be called with dx and dy
l = gradSearch(l, tmpPt, path);
//console.log(l);
pt = path.getPointAtLength(l);
this.attr({cx: pt.x, cy: pt.y});
};
var endCircleToPoly = function () {
// restoring state
this.attr({opacity: 1});
};
for(var i = 1; i <= 10; i++){
//polygon points
points.push( s.select('#line' + i).attr('x2') + ' ' + s.select('#line' + i).attr('y2') );
paths.push( s.path('M' + s.select('#line' + i).attr('x2') + ' ' + s.select('#line' + i).attr('y2') + ' L' + s.select('#line' + i).attr('x1') + ' ' + s.select('#line' + i).attr('y1')).attr({stroke: "blue"}) );
lines.push(s.select('#line' + i).attr({'stroke' : ''}));
//circles
circles.push( s.circle(
s.select('#line'+i).attr('x2'),
s.select('#line'+i).attr('y2'),5).attr({
fill: "red",
id: "circle"+i,
style: {"cursor" : "pointer"}
}).data({int: i-1}).drag( moveCircleToPoly, startCircleToPoly, endCircleToPoly )
);
}
//add poly
/*var poly = s.polygon(points);
poly.attr({
id:"poly",
fill:"#555555"
});
*/
<svg id="svg" version="1.1" preserveAspectRatio="xMinYMin meet" class="svg-content" viewBox="-10.109 -107.67 400 400">
<line id="line1" fill="none" x1="82.196" y1="-17.513" x2="107.595" y2="-95.686"/>
<line id="line2" fill="none" x1="82.196" y1="-17.513" x2="148.689" y2="-65.827"/>
<line id="line3" fill="none" x1="82.196" y1="-17.513" x2="164.391" y2="-17.513"/>
<line id="line4" fill="none" x1="82.196" y1="-17.513" x2="148.689" y2="30.801"/>
<line id="line5" fill="none" x1="82.196" y1="-17.513" x2="107.595" y2="60.66"/>
<line id="line6" fill="none" x1="82.196" y1="-17.513" x2="56.796" y2="60.66"/>
<line id="line7" fill="none" x1="82.196" y1="-17.513" x2="15.699" y2="30.801"/>
<line id="line8" fill="none" x1="82.196" y1="-17.513" x2="0" y2="-17.513"/>
<line id="line9" fill="none" x1="82.196" y1="-17.513" x2="15.699" y2="-65.827"/>
<line id="line10" fill="none" x1="82.196" y1="-17.513" x2="56.796" y2="-95.686"/>
</svg>
Thanks!
I think your main problem is that some of the floats are being treated like strings in addition rather than numeric ids. I would also use the data() method in the odd place you weren't before.
So I would do things like the following to force numeracy, or you could use parseInt etc
var tmpPt = {
x : +this.data("ox") + +dx,
y : +this.data("oy") + +dy
};
codepen
Added note: You may want to have a think about matrices and transformations as well as your drag is happening on elements that have a different screen transformation, so when you drag them, they move slightly quicker, so you may want to compensate for that.
Related
I have a svg path with a textPath connecting 2 divs from center like this:
item1.style.top="20px";
item1.style.left="20px";
item2.style.top="40px";
item2.style.left="160px";
var x1=parseFloat(item1.style.left)+ item1.offsetWidth / 2;
var y1=parseFloat(item1.style.top) + item1.offsetHeight / 2;
var x2=parseFloat(item2.style.left) + item2.offsetWidth / 2;
var y2=parseFloat(item2.style.top) + item2.offsetHeight / 2;
path1.setAttribute("d",`M ${x1} ${y1} L ${x2} ${y2}`)
*{
margin:0;
}
div{
height:2rem;
width:2rem;
position:absolute;
background-color:black;
box-sizing:border-box;
}
<div id="item1"></div>
<div id="item2" style="width:10rem; height:3rem"></div>
<svg id="svg1" style="overflow:visible">
<path id="path1" fill="none" stroke="red" stroke-width="3" />
<text font-size="24" dy="-10" text-anchor="middle">
<textPath href="#path1" fill="green" startOffset="50%">T</textPath>
</text>
</svg>
But as you can see the Text "T" isn't technically centered because of the height & width
so is there a way to shift the text (without changing the path d) into visual center?
like this:
Note
The height, width & position of the divs will change so a more flexible & versatile approach would be better
You could find the intersection points between the rectangles' borders and your textPath.
The final path start and end coordinates would be the calculated intersection points.
item1.style.top = "20px";
item1.style.left = "20px";
item2.style.top = "40px";
item2.style.left = "160px";
let svg = document.querySelector('svg');
let cx1 = parseFloat(item1.style.left) + item1.offsetWidth / 2;
let cy1 = parseFloat(item1.style.top) + item1.offsetHeight / 2;
let cx2 = parseFloat(item2.style.left) + item2.offsetWidth / 2;
let cy2 = parseFloat(item2.style.top) + item2.offsetHeight / 2;
// text path coordinates
let lTextP = [cx1, cy1, cx2, cy2];
renderLine(svg, lTextP)
// rect1: left, right, top, bottom
let x1 = parseFloat(item1.style.left);
let rx1 = x1 + item1.offsetWidth;
let y1 = parseFloat(item1.style.top);
let by1 = y1 + item1.offsetHeight;
// rect2: left, right, top, bottom
let x2 = parseFloat(item2.style.left);
let rx2 = x1 + item2.offsetWidth;
let y2 = parseFloat(item2.style.top);
let by2 = y2 + item2.offsetHeight;
// 1st rect: right border
let l1 = [rx1, y1, rx1, by1];
renderLine(svg, l1)
// 2nd rect: left border
let l2 = [x2, y2, x2, by2];
renderLine(svg, l2)
// find intersections between textpath and rect borders
let intersection1 = getVectorIntersection(l1, lTextP, true);
renderPoint(svg, intersection1, 'orange', '1%');
let intersection2 = getVectorIntersection(l2, lTextP, true);
renderPoint(svg, intersection2, 'orange', '1%');
// shorten text path according to intersections
[cx1, cy1] = intersection1;
[cx2, cy2] = intersection2;
path1.setAttribute("d", `M ${cx1} ${cy1} L ${cx2} ${cy2}`);
/**
* helper: get intersection coordinates
* based on
* source: https://dirask.com/posts/JavaScript-calculate-intersection-point-of-two-lines-for-given-4-points-VjvnAj
*/
function getVectorIntersection(l1, l2, exact = false, decimals = 3) {
let intersection = [];
let dx1 = l1[0] - l1[2];
let dy1 = l1[1] - l1[3];
let dx2 = l2[0] - l2[2];
let dy2 = l2[1] - l2[3];
// down part of intersection point formula
let d = dx1 * dy2 - dy1 * dx2;
if (d === 0) {
console.log('parallel')
return false;
} else {
// upper part of intersection point formula
let u1 = l1[0] * l1[3] - l1[1] * l1[2];
let u4 = l2[0] * l2[3] - l2[1] * l2[2];
// only exact intersections
let isIntersecting = u4 > d;
if (exact && !isIntersecting) {
return false;
}
// intersection point formula
let px = +((u1 * dx2 - dx1 * u4) / d).toFixed(decimals);
let py = +((u1 * dy2 - dy1 * u4) / d).toFixed(decimals);
intersection = [px, py];
}
return intersection;
}
// debug helper: render coordinates as markers
function renderPoint(svg, coords, fill = "red", r = "0.5%") {
if (coords.length) {
let marker =
'<circle cx="' +
coords[0] +
'" cy="' +
coords[1] +
'" r="' +
r +
'" fill="' +
fill +
'" ><title>' +
coords.join(", ") +
"</title></circle>";
svg.insertAdjacentHTML("beforeend", marker);
}
}
// debug helper: render lines
function renderLine(svg, coords, color = "purple", strokeWidth = 1) {
let [x1n, y1n, x2n, y2n] = coords;
let newLine =
'<line x1="' +
x1n +
'" y1="' +
y1n +
'" x2="' +
x2n +
'" y2="' +
y2n +
'" stroke-width="' + strokeWidth + '" stroke="' + color + '" />';
svg.insertAdjacentHTML("beforeend", newLine);
}
*{
margin:0;
}
.item{
height:2rem;
width:2rem;
position:absolute;
z-index:-1;
background-color:black;
box-sizing:border-box;
}
<div class="item" id="item1"></div>
<div class="item" id="item2" style="width:10rem; height:3rem"></div>
<svg id="svg1" style="overflow:visible">
<text font-size="24" dy="-10" text-anchor="middle">
<textPath href="#path1" fill="green" startOffset="50%">T</textPath>
</text>
<path id="path1" fill="none" stroke="red" stroke-width="3" stroke-linecap="square" />
</svg>
An easier alternative might be to draw the text path between the vertical centers like so:
var x1=parseFloat(item1.style.left)+ item1.offsetWidth ;
var y1=parseFloat(item1.style.top) + item1.offsetHeight / 2;
var x2=parseFloat(item2.style.left);
var y2=parseFloat(item2.style.top) + item2.offsetHeight / 2;
item1.style.top="20px";
item1.style.left="20px";
item2.style.top="40px";
item2.style.left="160px";
var x1=parseFloat(item1.style.left)+ item1.offsetWidth ;
var y1=parseFloat(item1.style.top) + item1.offsetHeight / 2;
var x2=parseFloat(item2.style.left);
var y2=parseFloat(item2.style.top) + item2.offsetHeight / 2;
path1.setAttribute("d",`M ${x1} ${y1} L ${x2} ${y2}`)
*{
margin:0;
}
.item{
height:2rem;
width:2rem;
position:absolute;
z-index:-1;
background-color:black;
box-sizing:border-box;
}
<div class="item" id="item1"></div>
<div class="item" id="item2" style="width:10rem; height:3rem"></div>
<svg id="svg1" style="overflow:visible">
<text font-size="24" dy="-10" text-anchor="middle">
<textPath href="#path1" fill="green" startOffset="50%">T</textPath>
</text>
<path id="path1" fill="none" stroke="red" stroke-width="3" stroke-linecap="square" />
</svg>
The solution provided by #herrstrietzel works but I found another solution
I used this solution and calculated the distance required to center and then used dx attribute to simply shift the text by that much
I'm attempting to create a crude database diagram generator using D3, but I can't figure out how to get connectors between fields. I can get straight lines going from two points, but I wanted it to be rounded and like a path I guess.
I've tried to put together an example of just that specific issue, linking two text fields:
https://codesandbox.io/s/gifted-bardeen-5hbw2?fontsize=14&hidenavigation=1&theme=dark
Here's an example from dbdiagram.io of what I'm referring to:
I've been reading up on the d attribute and the various commands, but nothing seems even close. I suspect the forceSimulation method, especially the forceCenter function might be messing up the relative positioning when I use the lower-cased commands. But not 100% on that.
You can compute a connector path between 2 points by connectorPath routine:
const source = {x: 200, y: 120};
const target = {x: 50, y: 20};
const MAX_RADIUS = 15;
const connectorPath = (from, to) => {
if (from.y === to.y || from.x === to.x)
return `M ${from.x},${from.y} L ${to.x},${to.y}`;
const middle = (from.x + to.x) / 2;
const xFlag = from.x < to.x ? 1 : -1;
const yFlag = from.y < to.y ? 1 : -1;
const dX = Math.abs(from.x - to.x);
const dY = Math.abs(from.y - to.y);
const radius = Math.min(dX / 2, dY / 2, MAX_RADIUS);
return `M ${from.x},${from.y} H ${middle - radius * xFlag} Q ${middle},${from.y} ${middle},${from.y + radius * yFlag} V ${to.y - radius * yFlag} Q ${middle},${to.y} ${middle + radius * xFlag},${to.y} H ${to.x}`;
};
d3.select('#source').attr('cx', source.x).attr('cy', source.y);
d3.select('#target').attr('cx', target.x).attr('cy', target.y);
d3.select('#connector').attr('d', connectorPath(source, target));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="300" height="200">
<path id="connector" stroke="blue" fill="none" />
<circle id="source" fill="red" r="5"/>
<circle id="target" fill="green" r="5"/>
</svg>
I'm trying to rotate object upon guideline path I created in illustrator.
I did found a way to rotate the object upon the guideline, but It's running on time and not scroll as I wish it to be.
I have this javascript code to determine the path of the object and this animationMotion to rotate the object automatically on time (instead of scroll).
How can I calculate the angle of the object on the path same as animationMotion but base it on scroll instead of time?
Some working codepen without animationMotoion.
function positionTheElement() {
var html = document.documentElement;
var body = document.body;
var scrollPercentage = (html.scrollTop + body.scrollTop - html.clientHeight) / (body.scrollHeight + 600 - html.clientHeight);
var path = document.getElementById("tracker");
var pathLen = path.getTotalLength();
var pt = path.getPointAtLength(scrollPercentage * pathLen );
var element = document.getElementById("wipe");
element.setAttribute("transform", "translate("+ pt.x + "," + pt.y + ")");
};
window.addEventListener("scroll", positionTheElement);
positionTheElement();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg>
<g>
<path
id="tracker"
d="M1343.5,32.5
L1341,558C1339.8,583.3 1349.8,600.1 1313,611C1298,619 1247,631 1144,630C1041,629 277,630 277,630C277,630 136,625 136,658C130,687 119,719 126,747C133,775 120,1931 120,1931C120,1931 128,2029.8000000000002 208,2034.4C288,2039 1697.8,2034.4 1697.8,2034.4C1697.8,2034.4 1788,2016 1794,2124C1800,2232 1780,2700 1780,2700C1780,2700 1796.8000000000002,2794 1689.9,2793C1583,2792 938,2787 938,2787L249,2782C249,2782 151.39999999999998,2769.8 154.7,2863.9C158,2958 155,3552 155,3552C155,3552 159,3645.3999999999996 243.5,3632.7C328,3620 1484,3634 1484,3634L1491.8,3999" fill="transparent" stroke-width="0"/>
<!--Wipe-->
<g id="wipe">
<path class="st11" d="M33.3,1c0,0-6.9-14.1-24.7-3.6S-18.5-5.8-23-0.2s-10.3-3.3-10.3-3.3s-3.4,26.2-0.5,24.8
c0.4-0.2,0.6-0.1,0.7,0.3c1.1,2.5-2.4,16.7-2.4,16.7s-3,22.1,0.8,20.7c3.8-1.5-1.4,23.2-1.4,23.2s-7.4,23.6,5.6,2.2
s56.9-26.1,57-15.8s1.1,20.7,3.4-11.3c0.5-6.8,6.3-46.6,0.5-31.9S31.3,4.2,33.3,1z"/>
<line class="st12" x1="-20.3" y1="17.9" x2="-33.3" y2="21.2"/>
<path class="st13" d="M28.1,28.2c0,0-15.7-19.9-23.1-9.8"/>
<path class="st14" d="M26.4,67.4c0,0-0.3-3-9.8-4.2c-9.5-1.2-2.4,1.6-2.4,1.6l13,13.8c0,0-0.3-0.8-0.6-5
C26.5,69.5,26.4,67.4,26.4,67.4L26.4,67.4"/>
<path class="st13" d="M29.1,19l3.5,3.2c-0.7-1.5-3,5.4-4,6.6C27.6,29.9,29.1,19,29.1,19z"/>
<path class="st15" d="M-20.3,17.9c0,0-9.6,12.3-12.7,7.4c0.1-2,0.1-4-0.2-4C-33.5,21.3-20.3,17.9-20.3,17.9z"/>
<path class="st12" d="M-34,58.9c0,0,25.5-19.6,25.8-7.7"/>
<path class="st15" d="M-34.2,58.8l26-7.7C-8.2,51.2-7.1,38.8-34.2,58.8z"/>
</g>
</g>
<!-- IF you run this, the object will auto rotate but on time.
<animateMotion
xlink:href="#wipe"
dur="3s"
begin="0s"
fill="freeze"
repeatCount="indefinite"
rotate="auto"
>
<mpath xlink:href="#tracker" />
</animateMotion>
-->
</svg>
EDIT:
I have tried to add this to get the degrees, the console works but the actual rotate fails to exist.
var firstPoint = path.getPointAtLength(scrollPercentage * pathLen);
var secondPoint = path.getPointAtLength(scrollPercentage * (pathLen + 3));
//Get degree
var deg = Math.atan2(firstPoint.y - secondPoint.y, firstPoint.x - secondPoint.x) * (180 / Math.PI);
console.log(deg);
var element = document.getElementById("wipe");
element.setAttribute("transform", "translate("+ pt.x + "," + pt.y + ") rotate(" + pt.deg + ")");
The degrees works but the transform rotation does not apply.
Error: attribute transform: Expected number, "…7578125)
rotate(undefined)".
You're already calling
var pt = path.getPointAtLength(scrollPercentage * pathLen );
If you call that function again with a small delta in either direction you'll be able to determine the direction of the path. A little trigonometry would then get the rotate you need.
I am making parallax by moving an object on a path and it is working fine with getPointAtlength() but I also need to rotate this object with the path.
I need something like getPointAtLength() but for angles that I get the angle of the point. Rapheal seems to have a method to it but it isn't friendly to svg elements that is created in html or I don't know how to deal with it. Any ideas?
var l = document.getElementById('path');
var element=$('#svg_26')
$(window).scroll(function(){
var pathOffset=parseInt($('#l1').css('stroke-dashoffset'));
var p = l.getPointAtLength(-1*pathOffset);
translation = 'translate('+p.x+'px,'+p.y+'px)'
$(element).css('transform',translation);
})
Using a library for this kind of task would be overkill. Its actually quite simple to write your own function to calculate the angle. All you have to do is use pointAtLength two time with a little offset:
var p1 = path.getPointAtLength(l)
var p2 = path.getPointAtLength(l + 3)
and then calculate the angle of the resulting line and the x-axis using Math.atan2
var deg = Math.atan2(p1.y - p2.y, p1.x - p2.x) * (180 / Math.PI);
here is a little example using the above formula
var path = document.getElementById("path")
var obj = document.getElementById("obj")
var l = 0
var tl = path.getTotalLength()
function getPointAtLengthWithRotation(path, length) {
var p1 = path.getPointAtLength(length)
var p2 = path.getPointAtLength(length + 3)
var deg = Math.atan2(p1.y - p2.y, p1.x - p2.x) * (180 / Math.PI);
return {
x: p1.x,
y: p1.y,
angle: deg
}
}
setInterval(function() {
l += 1
if (l > tl) l = 0;
var p = getPointAtLengthWithRotation(path, l)
obj.setAttribute("transform", "translate(" + p.x + "," + p.y + ") rotate(" + (p.angle + 180) + ")")
}, 30)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="200" height="200">
<path id="path" d="M 81.713425,82.629068 C 77.692791,85.788547 73.298237,77.367896 68.194886,79.039107 63.091534,80.710434 58.027628,96.952068 53.04637,97.140958 48.065112,97.329732 50.503508,75.285207 45.397105,74.05952 40.290703,72.833834 38.487501,93.968537 33.85932,91.287114 29.23114,88.605807 32.245641,70.914733 29.647307,66.19971 27.048973,61.484686 19.604932,68.733636 17.542589,63.315055 15.480245,57.896474 32.172733,59.004979 32.053727,53.363216 31.93472,47.721442 8.0865997,39.989401 9.2246856,34.665848 10.362772,29.342295 28.830448,38.693055 31.065274,33.7132 33.300101,28.733334 22.734045,13.601966 26.210126,9.6067771 29.686208,5.6115765 41.809938,29.357138 46.524268,27.383715 c 4.71433,-1.973424 3.011846,-23.1001292 8.022646,-23.3332919 5.0108,-0.2331744 4.529056,18.3713929 9.45006,20.4259809 4.921003,2.054588 12.017373,-15.4803016 16.717604,-13.058602 4.700233,2.421699 -6.261038,14.180819 -2.913997,18.778859 3.347041,4.59804 12.339067,-3.78046 13.896719,1.543011 1.557652,5.323471 -9.713912,13.199372 -9.176986,18.679109 0.536926,5.479772 19.347976,2.957331 18.124596,8.213665 -1.223374,5.256392 -21.036293,1.236997 -24.253076,5.968111 -3.216785,4.731114 9.342224,14.869033 5.321591,18.028511 z"
fill="none" stroke="grey" />
<path id="obj" d="M-5 -5 L5 0L-5 5z" fill="green" />
</svg>
getPointAtLength in Raphael returns an object with attribute 'alpha'. Alpha is the angle that you need along the curve. In the example above it would be p.alpha
So you should be able to apply a rotation to the object rotated by p.alpha,
Eg..
myRaphElement.transform('t' + p.x + ',' + p.y + 'r' + p.alpha).
The last part will rotate the element around its center.
If you can't create the raph element itself as the svg is inline, I suspect you may be better off with a library like Snap.svg (which has mostly same commands as by same author), or you could possibly dynamically rotate by css transform using something like 'rotate('+l.alpha+','+l.x+','+l.y+')'
Edit: I misread as it had Raphael in the tags, when its not being used.
I personally would use Snap for this case, as Raphael doesn't add a lot here. You could possibly create a Raphael element off screen with the same path as the inline element just to use the angle, but feels like overkill to load a library for that.
In Snap you could access the element with..
myElement = Snap('#svg_26')
p = myElement.getPointAtLength(-1*pathOffset);
myElement.transform('t' + p.x + ',' + p.y + 'r' + p.alpha)
<animateMotion rotate="auto" ... performs the guidance of automatically
<svg viewBox="0 0 150 100" width="300" height="200">
<path id="path" d="M 81.713425,82.629068 C 77.692791,85.788547 73.298237,77.367896 68.194886,79.039107 63.091534,80.710434 58.027628,96.952068 53.04637,97.140958 48.065112,97.329732 50.503508,75.285207 45.397105,74.05952 40.290703,72.833834 38.487501,93.968537 33.85932,91.287114 29.23114,88.605807 32.245641,70.914733 29.647307,66.19971 27.048973,61.484686 19.604932,68.733636 17.542589,63.315055 15.480245,57.896474 32.172733,59.004979 32.053727,53.363216 31.93472,47.721442 8.0865997,39.989401 9.2246856,34.665848 10.362772,29.342295 28.830448,38.693055 31.065274,33.7132 33.300101,28.733334 22.734045,13.601966 26.210126,9.6067771 29.686208,5.6115765 41.809938,29.357138 46.524268,27.383715 c 4.71433,-1.973424 3.011846,-23.1001292 8.022646,-23.3332919 5.0108,-0.2331744 4.529056,18.3713929 9.45006,20.4259809 4.921003,2.054588 12.017373,-15.4803016 16.717604,-13.058602 4.700233,2.421699 -6.261038,14.180819 -2.913997,18.778859 3.347041,4.59804 12.339067,-3.78046 13.896719,1.543011 1.557652,5.323471 -9.713912,13.199372 -9.176986,18.679109 0.536926,5.479772 19.347976,2.957331 18.124596,8.213665 -1.223374,5.256392 -21.036293,1.236997 -24.253076,5.968111 -3.216785,4.731114 9.342224,14.869033 5.321591,18.028511 z"
fill="none" stroke="grey" />
<polygon points="0,0 -5,-5 -5,5" style="fill:green">
<animateMotion begin="0s" dur="10s" rotate="auto" repeatCount="indefinite">
<mpath xlink:href="#path"></mpath>
</animateMotion>
</polygon>
</svg>
when i create dynamically svg text circle is working fine in number.but i am using small letter (example:- ffffffiiiisasdasdas) text circle create half but i need full circle and Shape should be react according to content which we are placing ..
please check image following
1.http://tinypic.com/view.php?pic=15fll6b&s=8
2.http://tinypic.com/view.php?pic=fe1181&s=8
following code use
<svg width="845" height="350" viewBox="0 0 845 350" clip-rule="nonzero" >
<g
data-ng-attr-fill="{{addText.color}}"
data-ng-attr-width="{{addText.w}}"
data-ng-attr-height="{{addText.h}}"
data-ng-attr-transform="{{rotate(addText)}}"
strok <defs>
<path stroke-width = "3"
fill="userSpaceOnUse"
data-ng-attr-id="temp-{{addText.id}}"
data-ng-attr-d="{{makeBox1(addText, true)}}" />
</defs>
<text ng-if="addText.text" glyph-orientation-vertical="90" lengthAdjust="spacingAndGlyphs" "
data-ng-attr-text-anchor="{{addText.text.anchor}}"
data-ng-attr-font-family="{{addText.text.font}}"
data-ng-attr-font-style="{{addText.text.italic ? 'italic' : 'none'}}"
data-ng-attr-font-weight="{{addText.text.bold ? 'bold' : 'normal'}}"
data-ng-attr-font-size="{{addText.text.size}}"
data-ng-attr-x="{{arcMid(addText)}}"
letter-spacing="2";
style="text-align:justify"
kerning="8">
<textPath data-ng-xlink-href="#temp-{{addText.id}}" method = "stretch"
writing-mode="lr-tb" clip-rule="nonzero" xlink:href="">
{{addText.text.text}}</textPath>
</text>
<path fill="none" stroke="#EEE" data-ng-attr-d="{{makeBox1(addText,true)}}" />
</g>
</svg>
javascript
$scope.makeBox1 = function makeBox1(item, temp) {
if (item.c == 1) {
var ma = $('#txtsearch').val();
var legth = ma.length;
console.log(legth + "m");
if (item.r == 0) {
item.r = item.h / 2;
}
var x1 = item.x + item.w / 2,
y1 = item.y + (item.h),
x2 = x1 + 1,
r = item.r;
if (temp) {
x1 = 270 + item.w / 2;
y1 = 30 + (item.h);
x2 = x1 + 1;
}
item.r = ((((((legth) * item.text.size * 5) / (legth / 0.9))) * legth * .02) + (legth * 0.09));
if (item.r > 137) {
item.r = 137;
}
x1 = 424.5;
y1 = 293;
r = item.r;
x2 = 425.5;
var y2 = 293
return "M " + x1 + " " + y1 + " " +
"A " + r + " " + r + " 0 1 1 " + x2 + " " + y2;
}
}
It's working for numbers because they always have a fixed width - or almost always. That is because you typically want numbers to align in columns - in invoices for instance.
Your complicated equation for calculating the circle radius is based on the font size. The formula has obviously been tweaked so it works well with the width of numerals in the font you are using. But it won't work with general non-numeric text. Or probably with numerals in a different font.
It is going to be hard to get this to work perfectly in every case, because different browsers may implement their <textPath> elements differently.
The best you can do is to measure the text length, and then calculate the radius from that, by dividing by (2*PI).
You can get the length of the text in a <text> element by calling getComputedTextLength() on the element.
var msg="ffffffiiiisasdasdas";
// Get SVG
var mysvg = document.getElementById("mysvg");
// Get text length
var tmp = document.createElementNS("http://www.w3.org/2000/svg", "text");
tmp.textContent = msg;
mysvg.appendChild(tmp);
var len = tmp.getComputedTextLength();
mysvg.removeChild(tmp);
//alert("len = "+len);
// Make the circle path for the msg to sit on
var x1 = 424.5,
y1 = 293,
x2 = 425.5,
y2 = 293;
var r = len / (2 * Math.PI);
var circ = document.createElementNS("http://www.w3.org/2000/svg", "path");
circ.setAttribute("id", "circ");
circ.setAttribute("d", "M " + x1 + " " + y1 + " " +
"A " + r + " " + r + " 0 1 1 " + x2 + " " + y2);
mysvg.appendChild(circ);
// Make the textPath element
var tp1 = document.createElementNS("http://www.w3.org/2000/svg", "text");
mysvg.appendChild(tp1);
var tp2= document.createElementNS("http://www.w3.org/2000/svg", "textPath");
tp2.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#circ");
tp2.textContent = msg;
tp1.appendChild(tp2);
svg text {
font-family: sans-serif;
font-size: 20px;
}
#circ {
fill: none;
stroke: black;
}
<svg id="mysvg" width="845" height="350" viewBox="0 0 845 350">
</svg>