Switching "d" value in svg path using javascript? - javascript

I'm trying to switch out the "d" value and replace it with a dynamically generated number, but I keep getting "unexpected number" as my output. Here's what I currently have:
<path id="pathA" d="M 0 0 l 0 255" stroke="none" stroke-width="0" fill="none"/>
<script>
var sink = document.getElementById("pathA");
sink.setAttribute("d", M 0 0 l 0 (round*25.5));
</script>

Your script as presented has two problems.
First, The second argument you're passing to the setAttribute is not correct. Try:
sink.setAttribute("d", "M 0 0 1 0 " + (round * 25.5));
Second, you don't provide a value for round at any point.

Related

Get DOM element by custom attribute value inside SVG

Having this sample SVG (which gets created in Microsoft Visio):
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="34.6243in" height="10.4874in"
viewBox="0 0 2492.95 755.093" xml:space="preserve" color-interpolation-filters="sRGB" class="st13">
<g id="shape1014-243" v:mID="1014" v:groupContext="shape" transform="translate(30.3431,-54.0037)">
<title>Team frame.1171</title>
<desc>Test</desc>
<v:custProps>
<v:cp v:nameU="Theme" v:lbl="MY_UNIQUE_ID_1" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
v:ask="false" v:langID="0" v:cal="0" v:val="VT0(0):26"/>
</v:custProps>
<v:textBlock v:margins="rect(0,0,0,0)" v:tabSpace="42.5197"/>
<v:textRect cx="100.34" cy="791.095" width="200.69" height="72.0035"/>
<path d="M56.21 84.35 L55.85 84.35 L17.01 84.35 A17.0079 17.0079 -180 0 0 0 101.35 L0 738.09 A17.0079 17.0079 -180 0
0 17.01 755.09 L183.67 755.09 A17.0079 17.0079 -180 0 0 200.68 738.09 L200.68 101.35 A17.0079 17.0079 -180
0 0 183.67 84.35 L144.83 84.35" class="st10"/>
<text x="5.13" y="809.09" class="st12" v:langID="2057"><v:paragraph v:horizAlign="1"/><v:tabList/>Arrivals</text>
</g>
</svg>
I want to access the 'g' element. The problem is I can't simply use document.getElementById("shape1014-243"); as the ID will be randomly generated by Visio.
The value which I know I can access it by, is this value v:lbl="MY_UNIQUE_ID_1". Now, how can I access the 'g' element knowing only that value?
I can do the following:
Get all v:cp elements with var elements = document.getElementsByTagName("v:cp");
Loop through them and check if elements[X].getAttribute("v:lbl") === "MY_UNIQUE_ID_1"
If true, then get the 'g' element using element[X].parentElement.parentElement
But is there a better way? I tried document.querySelector('[v:lbl="MY_UNIQUE_ID_1"]'); but it returns error 'not a valid selector'.
I tried the code in the question. I am expecting a solution
I would probably start with searching for the element you actually know :
let elem = document.querySelector(`*[v\\:lbl="MY_UNIQUE_ID_1"]`);
(note the \\ escaping the colon)
Then use this and loop on elem.parentElement then its parent element and so on until I find one that has nodeName as g. (Unfortunately CSS does not have a way to select an element that contains another specific element -- that I know of at least)
This works:
let elem = document.querySelector(`*[v\\:lbl="MY_UNIQUE_ID_1"]`);
while(elem != null && elem.nodeName != `g`){
elem = elem.parentNode;
}
if(elem !== null){
// do something with elem
}
Streamlining what you already proved to work:
Find the element by your known, unique label
get the closest g element ( parentElement.parentElement )
the example below will log the id of the g element, so shows "shape1014-243"
function getG( uniqueLabel ){
var elementOne = document.querySelector(`v\\:cp[v\\:lbl='${uniqueLabel}']`);
return elementOne.closest('g');
}
function logGetG( uniqueLabel ){
console.log(getG(uniqueLabel).id);
}
<button onClick='logGetG("MY_UNIQUE_ID_1");'>Get G!</button>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="34.6243in" height="10.4874in"
viewBox="0 0 2492.95 755.093" xml:space="preserve" color-interpolation-filters="sRGB" class="st13">
<g id="shape1014-243" v:mID="1014" v:groupContext="shape" transform="translate(30.3431,-54.0037)">
<title>Team frame.1171</title>
<desc>Test</desc>
<v:custProps>
<v:cp v:nameU="Theme" v:lbl="MY_UNIQUE_ID_1" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
v:ask="false" v:langID="0" v:cal="0" v:val="VT0(0):26"/>
</v:custProps>
<v:textBlock v:margins="rect(0,0,0,0)" v:tabSpace="42.5197"/>
<v:textRect cx="100.34" cy="791.095" width="200.69" height="72.0035"/>
<path d="M56.21 84.35 L55.85 84.35 L17.01 84.35 A17.0079 17.0079 -180 0 0 0 101.35 L0 738.09 A17.0079 17.0079 -180 0
0 17.01 755.09 L183.67 755.09 A17.0079 17.0079 -180 0 0 200.68 738.09 L200.68 101.35 A17.0079 17.0079 -180
0 0 183.67 84.35 L144.83 84.35" class="st10"/>
<text x="5.13" y="809.09" class="st12" v:langID="2057"><v:paragraph v:horizAlign="1"/><v:tabList/>Arrivals</text>
</g>
</svg>

Is it possible to make SVG equal to path?

Helo I have a problem. I am using npm package which provides pack of weather icons. The problem is the SVG box is big and it breaks positioning that is a lot of white space on website, it does not look nice. Is negative margin the way>
import {wiCloudy} from 'weather-icons-react'
const WeatherDataBox = () => {
return (
icon=<WiCloudy size={256} />
)}
<svg stroke="currentColor" fill="currentColor"
stroke-width="0" viewBox="0 0 30 30"
attr="[object Object]" size="256" height="256" width="256">
<path>*long code*</path>
</svg>
I changed viewbox and it reduced empty background
let ICON_SIZE = 256
let VIEWBOX = "5 5 20 20"
<WiCloudy viewBox={VIEWBOX} size={ICON_SIZE} />

Convert svg to react-native-svg

What is the easiest way to do this? I found plenty of svg to JSX converters, which is what I want, but that doesn't work in react-native. I need to convert the svg code to something I can display in my app using react-native-svg. Thanks!
I can think of the following options. The one you use will depend on the amount of files you have to convert.
Option 1 (few files)
Copy-paste your svg code on this site and check the React Native checkbox. This will give you the code which you can then use with react-native-svg
Use that output within the following code (replace the SvgComponent with what was generated):
import React, { Component } from 'react';
import { View } from 'react-native';
import Svg, { Circle, Path } from 'react-native-svg';
const SvgComponent = props => (
<Svg viewBox="0 0 48 1" style={props.style}>
<Path d="M0 0h48v1H0z" fill="#063855" fillRule="evenodd" />
</Svg>
);
class MySVGIcon extends Component {
render() {
const { style } = this.props;
const component = SvgComponent(style);
return <View style={style}>{component}</View>;
}
}
export default MySVGIcon;
Option 2 (many files)
Instead of converting them, you can embed them directly in your code with react-native-remote-svg.
For instance you can do this:
import Image from 'react-native-remote-svg';
class MyImageClass extends Component {
render () {
// Embed code or read a SVG file...
const mySVGImage = '<svg width="48px" height="1px" viewBox="0 0 48 1" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect id="Rectangle-5" x="25" y="36" width="48" height="1"></rect></svg>';
return (
<Image source={{
uri: "data:image/svg+xml;utf8," + mySVGImage
}}/>
);
}
}
Option 3 (coolest option)
It isn't free like the two other options, but it isn't expensive either. PaintCode is one of those tools that I just love. Version 3+ now supports java-script. So you could load your svg files in it, and it will spit out code that you can use in your app. The advantage here that the interface is friendlier than the first option above, and you can then animate each object inside the svg file from your code.
I'm using expo for building react native app and I had the same inquiry but I found some solution here react-native-svg. I use the XML Option which was as below.
STEP 1- first I used this link convert SVG to JSX for React to convert the SVG to JSX (Some times you don't need to convert it, it depends on icon itself).
STEP 2- open new file component and import react and import { SvgXml } from 'react-native-svg', then create a normal functional component inside.
STEP 3- wrap the converted SVG icon tag with Quotation mark to be like this "<svg>some icon code inside</svg>" then store it in a variable var, const, let..etc.
STEP 4- return <SvgXml xml={the variable goes here} />.
STEP 5- export the component as usual.
here is an example code
import React from 'react'
import { SvgXml } from 'react-native-svg';
function FastRunningIcon(props) {
const xml = `<svg
xmlns="http://www.w3.org/2000/svg"
width="${props.width}"
height="${props.height}"
viewBox="0 0 21.244 20"
>
<path
fill="#595959"
d="M7.379 20a1.21 1.21 0 01-.783-.291.235.235 0 01-.04-.041 1.258 1.258 0 01-.293-1.036 2.179 2.179 0 01.544-1.049c.387-.459 1.089-1.209 2.085-2.231H6.874a1.426 1.426 0 01-1.062-.412 1.263 1.263 0 01-.332-.863 1.166 1.166 0 01.345-.85 1.7 1.7 0 011.01-.425c.454-.05 1.532-.07 2.575-.09h.187c.4-.008.649-.009.869-.01h.506L8.4 8.925 8.38 8.9a2.075 2.075 0 01-.2-1.877 3.3 3.3 0 011.165-1.272l.013-.013L12.412 3.6l-1.221-.957-4.011.094h-.04c-.064 0-.116.007-.168.007a2.209 2.209 0 01-.642-.1 1.271 1.271 0 01-.43-.213.755.755 0 01-.279-.478.014.014 0 00-.006-.006s-.006 0-.007-.007A1.132 1.132 0 016.078.9 2.949 2.949 0 017.02.492a.547.547 0 01.067-.013l4.356-.465h.027a1.174 1.174 0 01.2-.014h.052a1.324 1.324 0 01.743.252l.008.005.008.005.019.018.019.013c.6.392 1.155.764 1.575 1.049.373.251.8.54 1.142.784.177.125.3.218.4.293a.7.7 0 01.087.077l.045.041a.251.251 0 00.028.022.364.364 0 01.105.112.038.038 0 00.011.014.111.111 0 01.015.026l.008.012c.171.258.752 1.14 1.041 1.675a1.8 1.8 0 01.226.678.438.438 0 01.08.212l.185 2.005 2.178.12h.013c.062.005.124.007.185.009h.032a2.025 2.025 0 01.66.1.857.857 0 01.451.358 1.063 1.063 0 01.12.625v.013a1.108 1.108 0 01-.6.851 2.419 2.419 0 01-1.022.252h-.027c-.568 0-3.523.171-3.545.173h-.08a1.093 1.093 0 01-.632-.324 1.458 1.458 0 01-.276-.454v-.022l-.006-.014a.384.384 0 01-.04-.093l-.226-1.3-.688.437-.483.307-.029.019-1.138.724.119.184.117.182c.267.414.555.863.88 1.374l.162.256.014.023.02.031.031.048.015.023c.464.732 1.165 1.838 1.365 2.211v.013a1.29 1.29 0 010 1.395 1.568 1.568 0 01-1.3.545h-.969l-.1.1-.026.027c-.005.005-.6.615-1.48 1.49-.9.9-1.878 1.874-2.284 2.231a3.069 3.069 0 01-1.2.757.963.963 0 01-.299.036zm2.7-4.647c-.019.02-1.972 2.024-2.616 2.788a1.347 1.347 0 00-.345.638.333.333 0 00.066.292l.008.007c.061.054.091.081.146.081a.442.442 0 00.113-.021 2.415 2.415 0 00.85-.571c.339-.3 1.428-1.376 2.257-2.2.333-.332.51-.514.682-.689.1-.1.2-.2.314-.32zm-.133-.894a.486.486 0 01.186.043h3.6a.771.771 0 00.611-.2c.062-.082.109-.186-.053-.478v-.014c-.133-.247-.908-1.49-1.58-2.55-.534-.841-1-1.572-1.18-1.847v-.009l-.076-.117-.067-.1-.014-.022a.426.426 0 01.127-.585l3.1-1.98a.424.424 0 01.266-.173l.366-.233.167-.106.473-.3.021-.013.141-.09a.683.683 0 00.319-.346.646.646 0 00-.12-.531c-.2-.365-.66-1.094-1.014-1.641l-.007-.011-.007-.012v.008a.075.075 0 010 .021l-.005-.01-.01-.009a.772.772 0 00-.071-.059l-.031-.023a10.05 10.05 0 00-.341-.256 53.275 53.275 0 00-.807-.553l-.091-.063-.015-.01-.023-.015-.011-.011-.014-.01-.16-.108c-.479-.323-.967-.645-1.289-.858l-.174-.115-.009-.005-.009-.006-.057-.038L12 .97V.965a.16.16 0 01-.022-.021.388.388 0 00-.278-.092.654.654 0 00-.127.012h-.014l-4.316.452a.014.014 0 00-.006.006s0 .006-.007.007a2.253 2.253 0 00-.651.266c-.115.084-.126.112-.119.185a.482.482 0 00.119.053A1.62 1.62 0 007 1.891h.13l4.21-.093a.456.456 0 01.266.093L13.399 3.3a.425.425 0 01-.026.677l-3.52 2.47a2.649 2.649 0 00-.889.93 1.1 1.1 0 00.146 1.076l.014.013a.01.01 0 010 .007.005.005 0 000 .006l3 4.4a.424.424 0 01.024.436.419.419 0 01-.37.229c-.008 0-1.056.005-2.151.027-1.131.022-2.4.059-2.7.093a.867.867 0 00-.517.186.268.268 0 00-.08.238.422.422 0 00.106.279.519.519 0 00.438.146h2.9a.382.382 0 01.12-.04.492.492 0 01.047-.014zm6.6-8.084l-.02.013-.026.012-.053.034-.008.005c-.53.343-.844.544-1.014.652l.281 1.609a.836.836 0 00.119.186.143.143 0 00.1.049h.063c.457-.028 2.77-.168 3.443-.17h.026a1.842 1.842 0 00.651-.146c.124-.066.139-.1.147-.173a.745.745 0 000-.122v-.012a2.468 2.468 0 00-.387-.036h-.041l-.22-.014H19.6l-2.54-.133a.428.428 0 01-.4-.385l-.12-1.368zm-8.41 4.693a.48.48 0 01-.063 0H.42a.43.43 0 01-.08-.851h7.729a.48.48 0 01.063 0 .43.43 0 110 .859zm-1.7-2.125a.539.539 0 01-.063 0H.42a.43.43 0 01-.08-.851h6.029a.539.539 0 01.063 0 .43.43 0 110 .859zm0-2.125a.542.542 0 01-.063 0H.42a.43.43 0 01-.08-.851h6.029a.542.542 0 01.063 0 .43.43 0 110 .859zm2.123-2.124a.51.51 0 01-.065 0H.42a.43.43 0 01-.08-.85h8.154a.51.51 0 01.065 0 .429.429 0 010 .859zm10.347 0a2.337 2.337 0 112.337-2.337 2.34 2.34 0 01-2.337 2.332zm0-3.825a1.472 1.472 0 00-1.488 1.488 1.487 1.487 0 102.974 0A1.471 1.471 0 0018.906.864z"
data-name="Shape Copy 4"
></path>
</svg>`
return <SvgXml xml={xml} />
}
export default FastRunningIcon;
You can simply try this tool that will do the conversion for you: https://github.com/MoOx/react-from-svg
It can convert SVG to React Native files, but also to Reason/ReScript React Native files, and even React DOM files with some option to remove fill or strokes attributes.

SVG - Duplicate image along path

I am currently trying to create a bicycle chain via SVG and javascript.
The result I am trying to create will look something like this:
The problem I am having is how to repeat a single chainlink image along the path of the chain. I want to be able to use a chainlink image (For example:
and then repeat this along the path that I define.
I know that it is trivial to repeat text along a path via SVG but is it possible to do the same with an image? The complexity comes from the fact that the chainlink will have to appear as one continuous line.
Here is what I have so far:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<svg width="700" height="500">
<path id="ChainPath" fill="none" stroke="red" d="M150 100 L400 100 C650 100 650 400 400 400 L150 400" />
<text>
<textPath href="#ChainPath" alignment-baseline="middle">
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
</textPath>
</text>
</svg>
</body>
</html>
Any help would be greatly appreciated.
Thanks
I tried to practically implement Robert Monfera's answer and found a few extra hitches. I started out computing the points of a polygon sitting, as he described, at the axes/connecting pins of the individual chain links, and all with exactly identical distance between them.
One thing that immediately strikes if you look at it is that a chain is not a smooth line. For the human eye, it might look like one, but describing the chain running around the gear as a circular arc is a bad approximation.
Look at the picture in the question. The right gear has 15 teeth. That means the angle between neighbouring chain links is 24°, and the length difference between an arc and the cord from point to point (which is the constructive length of the link) is approx. 0.8%. That doesn't sound like much, but for a link that you draw with a screen size of 50px, it's almost half a pixel. That is a difference that can be seen.
The next propostion was to use <marker> elements for the individual chain links with the origin in the center between the two pins. Since the polygon defined above has its points at the position of the pins, for this a second one is needed that connects the mids of all polygon segments.
When implementing that, the resulting chain looked like this (I've drawn the polygon on top to illustrate):
Where the "straight" part of the chain morphs to the "curved" one, the chain links are seriously misaligned. The reason is that the marker orientation bisects the incoming and outgoing tangent at a vertex. Both for the straight section and while following the arc of the gear, that works out correctly, but where both meet, it obviously doesn't.
To get the orientation of the markers correct, the tangents at the points must match the direction of the original polygon. Robert proposed to draw an arc between the points. That probaböy would work, but it is complicated to compute the correct arc radii and positions.
I've come up with a method that produces a smooth path that looks a bit crooked, but the line will never show in the end, and its computation is really straightforward - I actually did that with an Excel sheet.
Lets say we have a list of points marking the pin positions:
a b c d e f g ...
There are two kinds of links (front and back), so mark every other middle of two points:
a b c d e f g ...
ab cd ef
a, ab, c are in a straight line, same as c, cd, d and so on. Now if you draw a path with the following command
<path d="M a L ab C b c cd d e ef f g ..." marker-mid="url(#link1)" />
point a will hold no marker, as its not in the middle. The next vertices, which position the markers, are ab, cd, ef, ..., while b, c, d, e, ... are control points of a cubic Bezier curve. What that means is: the path tangent in vertex ab is the straight line from a to b, in cd from c to d, and so on.
Here is a screenshot from Inkscape to illustrate:
The other links can be describes accordingly as
<path d="M b L bc C c d de e f fg g ..." marker-mid="url(#link2)" />
If one wants to get fancy, it is even possible to shorten that a bit with the S command that takes the control point before a vertex and implicitely adds its reflection as the next control point after (points e, g, ... are computed):
<path d="M b L bc C c d de S f fg h hj ..." marker-mid="url(#link2)" />
For the grande finale, here is the finished drawing. Just for the heck of it, I've drawn the gear teeths with the same marker technique.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 600" width="600" height="450">
<defs>
<marker id="blade1" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
<path style="fill:#ececf4" d="M 16.5,-1.556 13.947,-1.78 A 14,14 0 0 1 -13.947,-1.78 L -16.5,-1.556 0,200 Z" />
<path style="fill:none;stroke:#000" d="M 16.5,-1.556 13.947,-1.78 A 14,14 0 0 1 -13.947,-1.78 L -16.5,-1.556" />
</marker>
<marker id="blade2" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
<path style="fill:#ececf4" d="M 16.5,0.507 13.694,-0.089 A 14,14 0 0 1 -13.694,-0.089 L -16.5,0.507 0,90 Z" />
<path style="fill:none;stroke:#000" d="M 16.5,0.507 13.694,-0.089 A 14,14 0 0 1 -13.694,-0.089 L -16.5,0.507" />
</marker>
<path id="plate" d="M -9.689,-10.392 C -4.782 -7.56 4.782 -7.56 9.689,-10.392 A 12,12 0 1 1 9.689,10.392 C 4.782 7.56 -4.782 7.56 -9.689,10.392 A 12,12 0 1 1 -9.689,-10.392 Z" />
<marker id="link1" style="stroke:#000" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
<use xlink:href="#plate" style="fill:#ddd" />
<circle id="pin" style="fill:#888;stroke:#000" cx="15.689" cy="0" r="4" />
<use xlink:href="#pin" x="-31.378" />
</marker>
<marker id="link2" style="stroke:#000" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
<use xlink:href="#plate" style="fill:#bbb" />
</marker>
<g id="center1">
<circle r="80" cx="250" cy="300" />
<g id="cq">
<circle r="10" cx="345" cy="265.42" />
<path d="M 252.984,200.049 A 100,100 0 0 1 324.557,233.367 25,25 0 0 0 361.957,200.182 150,150 0 0 0 254.883,150.086 25,25 0 0 0 252.984,200.049 Z" />
</g>
<use xlink:href="#cq" transform="rotate(90 250,300)" />
<use xlink:href="#cq" transform="rotate(180 250,300)" />
<use xlink:href="#cq" transform="rotate(270 250,300)" />
</g>
<mask id="m1">
<rect fill="white" x="50" y="100" width="400" height="400" />
<use xlink:href="#center1" fill="black" />
</mask>
<path id="center2" d="M 647.75 277.2 L 642.04 279.05 L 643.04 282.13 A 20 20 0 0 0 633.31 292.96 L 630.13 292.28 L 628.88 298.15 L 632.06 298.83 A 20 20 0 0 0 636.55 312.68 L 634.38 315.08 L 638.84 319.1 L 641.01 316.69 A 20 20 0 0 0 655.25 319.73 L 656.25 322.8 L 661.96 320.95 L 660.96 317.87 A 20 20 0 0 0 670.7 307.04 L 673.87 307.72 L 675.12 301.85 L 671.94 301.17 A 20 20 0 0 0 667.45 287.32 L 669.62 284.92 L 665.16 280.9 L 662.99 283.31 A 20 20 0 0 0 648.75 280.27 L 647.75 277.2 z" />
<mask id="m2">
<rect fill="white" x="572" y="220" width="160" height="160" />
<use xlink:href="#center2" fill="black" />
</mask>
</defs>
<g mask="url(#m1)">
<path style="fill:none;marker-mid:url(#blade1)" d="M 430,300 427.27,331.26 419.14,361.56 405.88,390 387.89,415.7 365.7,437.89 340,455.88 311.56,469.14 281.26,477.27 250,480 218.74,477.27 188.44,469.14 160,455.88 134.3,437.89 112.11,415.7 94.12,390 80.86,361.56 72.73,331.26 70,300 72.73,268.74 80.86,238.44 94.12,210 112.11,184.3 134.3,162.11 160,144.12 188.44,130.86 218.74,122.73 250,120 281.26,122.73 311.56,130.86 340,144.12 365.7,162.11 387.89,184.3 405.88,210 419.14,238.44 427.27,268.74 430,300 427.27,331.26" />
</g>
<use xlink:href="#center1" style="fill:none;stroke:#000" />
<g mask="url(#m2)">
<path style="fill:none;marker-mid:url(#blade2)" d="M 578.19,315.69 V 284.31 L 590.95,255.65 614.27,234.65 644.11,224.95 675.32,228.23 702.49,243.92 720.94,269.31 727.46,300 720.94,330.69 702.49,356.08 675.32,371.77 644.11,375.05 614.27,365.35 590.95,344.35 578.19,315.69 V 284.31"/>
</g>
<use xlink:href="#center2" style="fill:none;stroke:#000" />
<path style="fill:none;marker-mid:url(#link2)" d="M 281.26 477.27 L 265.63 478.63 C 250 480 218.74 477.27 203.59 473.21 S 160 455.88 147.15 446.89 112.11 415.7 103.11 402.85 80.86 361.56 76.79 346.41 70 300 71.37 284.37 80.86 238.44 87.49 224.22 112.11 184.3 123.21 173.21 160 144.12 174.22 137.49 218.74 122.73 234.37 121.37 281.26 122.73 296.41 126.79 341.88 138.97 357.03 143.03 402.5 155.2 417.66 159.26 463.13 171.43 478.28 175.49 523.75 187.66 538.91 191.72 584.38 203.89 599.54 207.95 645.01 220.12 660.16 224.18 702.49 243.92 711.71 256.61 727.46 300 724.2 315.35 702.49 356.08 688.91 363.92 645.01 379.88 629.85 383.94 584.38 396.11 569.22 400.17 523.75 412.34 508.6 416.4 463.13 428.57 447.97 432.63 402.5 444.8 387.35 448.86 341.88 461.03 326.72 465.09 281.26 477.27 265.63 478.63 L 250 480" />
<path style="fill:none;marker-mid:url(#link1)" d="M 311.56 469.14 L 296.41 473.21 C 281.26 477.27 250 480 234.37 478.63 S 188.44 469.14 174.22 462.51 134.3 437.89 123.21 426.79 94.12 390 87.49 375.78 72.73 331.26 71.37 315.63 72.73 268.74 76.79 253.59 94.12 210 103.11 197.15 134.3 162.11 147.15 153.11 188.44 130.86 203.59 126.79 250 120 265.63 121.37 311.56 130.86 326.72 134.91 372.19 147.08 387.35 151.14 432.82 163.31 447.97 167.37 493.44 179.54 508.6 183.6 554.07 195.77 569.22 199.83 614.69 212 629.85 216.06 675.32 228.23 688.91 236.08 720.94 269.31 724.2 284.65 720.94 330.69 711.71 343.39 675.32 371.77 660.16 375.82 614.69 388 599.54 392.05 554.07 404.23 538.91 408.28 493.44 420.46 478.28 424.51 432.82 436.69 417.66 440.74 372.19 452.92 357.03 456.97 311.56 469.14 296.41 473.21 L 281.26 477.27" />
</svg>
In SVG, I define one chainlink and create as many copies as necessary (reference point being at the center of the chainlink) :
<use class="chain chainlink1" xlink:href="#chainlink" />
<use class="chain chainlink2" xlink:href="#chainlink" />
<use class="chain chainlink3" xlink:href="#chainlink" />
<use class="chain chainlink4" xlink:href="#chainlink" />
....
in the css, I define the path to follow
.chain {
offset-path: path("m 50,150 c -2,-19 12,-36 24,-44 13,-8 176,-54 226,-56
60,0 100,49.17776 100,100 0,51 -42,99 -100,99 C 262,250
95,206 72,191 58,182 50,166 50,150 Z");
animation: cycle 20s linear infinite;
}
#keyframes cycle {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
And the delay of each chainlink
.chainlink2 {
animation-delay: 1s;
}
.chainlink3 {
animation-delay: 2s;
}
...
Durations and delays depends on length of the chainlink, path... and desired speed
Here is one solution: you can make a <polyline> or <polygon> with <marker> elements as long as you're willing to do the math to ensure that the distance between the polygon points exactly match the axis-to-axis distance of your chain element (ie. it's not the total length of the element, but the repeated placement distance that counts). It requires the use of a^2 + b^2 = c^2 where c is this pitch distance (a constant), and a and b are the horizontal and vertical distances from the preceding point, respectively. In other words, you need to calculate the x, y coordinates of the chain axis centers (*) (if you do that, then you might as well place them one by one, but then you'll have a lot of DOM elements).
You'll need orient="auto" to align the chain elements with the polygon, and also use the properties viewBox,refX, refY, markerWidth, markerHeight so that one of the axes of the chain element aligns perfectly with the polygon point (and then, via using the above Pythagoras theorem, the other chain element center will correspond to the next polygon point).
Here are a few examples for the <marker>:
https://codepen.io/monfera/pen/ppaRNK
https://codepen.io/monfera/pen/xXmpbY
https://codepen.io/monfera/pen/oLoRgX
As I look at your chain (normal bicycle chain), it's best to make two almost identical polygons atop of one another, with a half-pitch offset between the two:
in the background, the partially occluded chain elements
in the foreground, the 8-shaped, fully visible link plates
As a result, your chain will be seamless, as long as your points are at the right place. You can even set up a loop of transitioning between adjacent elements for an animated chain :-) (it's enough to transition with the chain pitch only, because a chain pitch translation will get you back to an indistinguishable view)
(*) As ccprog points out below, to make the individual chain elements better aligned, it's not an axis center but the chain center that must sit on the polygon. This might make the math a bit more tedious if there are sharp turns, but in this specific case (bicycle chain) the circle radii seem sufficiently large to not lead to a noticeable separation of the chain elements.

Checking text overflow on SVG textpath & jquery

First of all I looked at all possible related answers here but none of them seem to bring the answer I need so here I am.
Given a svg text path:
<svg viewBox="0 0 900 900"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
id="mysvg"
>
<defs>
<path id="myPath" d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
</defs>
<use xlink:href="#myPath" fill="none" stroke="red" />
<text id="names" font-family="Verdana" font-size="10" text-anchor="middle" >
<textPath xlink:href="#myPath" startOffset="50%">
My text is going to exceed at some point…
</textPath>
</text>
</svg>
At this point the text exceeds the textpath
I can't find a way to check for possible overflow through jquery. This command won't actually return undefined:
alert($("text#names").attr("textLength") );
I am trying to check for overflows in order to fit the text to the maximum length or so.
I had the same problem when adjusting font size so that the given text will be drawn with the largest possible font size without overflow. Its quite simple using plain JS.
1) Determine the np. of characters in the text element with a minimum font size:
textElement.css('font-size', 1);
var allCharCount = textElement[0].getNumberOfChars();
2) Then set font size to any value and determine the length again
var hasOverflow = allCharCount != textElement[0].getNumberOfChars();
getNumberOfChars() will only return the no. of chars drawn. If there is an overflow this number will be smaller then from the original whole string.
It looks like text.getNumberOfChars() has changed since the other answer was written, and now returns the total number of characters in the string, regardless of if they're rendered or not.
My approach to this problem is to:
Change the <textPath> element to draw on a much longer path, then calculate the text length using text.getComputedLength()
Change the <textPath> back to the original path and calculate length again
If the length on the original path is shorter than the length on the longer path, you know there's an overflow.
const textPath = document.querySelector('textPath');
const checkClipped = () => {
textPath.setAttribute('xlink:href', '#fullWidthPath');
const fullLength = textPath.getComputedTextLength();
textPath.setAttribute('xlink:href', '#myPath');
const curvedLength = textPath.getComputedTextLength();
return fullLength > curvedLength;
}
const findLongestString = () => {
const text = textPath.innerHTML;
if (checkClipped()) {
const newText = text.substring(0, text.length - 1);
textPath.innerHTML = newText;
return findLongestString(newText);
} else {
return text;
}
}
console.log(findLongestString())
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="mysvg">
<defs>
<path id="myPath" d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
<path id="fullWidthPath" d="M 0 0 L 0 10000" />
</defs>
<use xlink:href="#myPath" fill="none" stroke="red" />
<text id="names" font-family="Verdana" font-size="10" text-anchor="middle" >
<textPath xlink:href="#myPath" startOffset="50%">
My text is going to exceed at some point…
</textPath>
</text>
</svg>

Categories