In this demo a circle is rendered using animateMotion and I need to be able to update the position of the circle after the initial rendering depending on where the user clicks.
<svg
#svgRoot
id="svgRoot"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<path
id="circlePath"
d="M25.67 79.86a34.98 34.41 90 0 1 0-49.47a34.98 34.41 90 0 1 48.67 0a34.98 34.41 90 0 1 0 49.47"
fill="none"
stroke-linecap="round"
pathLength="270"
/>
</defs>
<use id="track" href="#circlePath" stroke="#D3D7DB" stroke-width="8" />
<use
id="progress"
href="#circlePath"
stroke="#008000"
stroke-width="10"
stroke-dasharray="60 270"
/>
<circle
cx="0"
cy="0"
r="6"
stroke-width="5"
fill="#FFFFFF"
stroke="#008000"
>
<animateMotion
id="thumb"
fill="freeze"
begin="0s"
dur="0s"
repeatCount="0"
keyPoints="0.6;0.6"
keyTimes="0;1"
calcMode="linear"
>
<mpath xlink:href="#circlePath" />
</animateMotion>
</circle>
</svg>
Also this is a Stackblitz for experimentation.
I thought that assigning a the new value to keyPoints within animateMotion would update the position of the circles, but when I play around with it developer tooling the changes don't change the rendering.
How do we update the position of the circle programmatically?
OK after looking at this question I realized that we have to call beginElement() on the animateMotion element after making changes to the attributes.
Calling beginElement() moves the circle to the new position.
I have created the following code with the intent of drawing a line between hundred points:
<svg width={"100"} height={"100"} className='svg' stroke='#000'>
<line strokeWidth={1}
x1={xDataSet[1]} x2={xDataSet[2]} x3={xDataSet[2]} x4={xDataSet[3]} x5={xDataSet[4]} x6={xDataSet[5]} x7={xDataSet[6]} x8={xDataSet[7]} x9={xDataSet[8]} x10={xDataSet[9]}
x11={xDataSet[10]} x12={xDataSet[11]} x13={xDataSet[12]} x14={xDataSet[13]} x15={xDataSet[14]} x16={xDataSet[15]} x17={xDataSet[16]} x18={xDataSet[17]} x19={xDataSet[18]} x20={xDataSet[19]}
x21={xDataSet[20]} x22={xDataSet[21]} x23={xDataSet[22]} x24={xDataSet[23]} x25={xDataSet[24]} x26={xDataSet[25]} x27={xDataSet[26]} x28={xDataSet[27]} x29={xDataSet[28]} x30={xDataSet[29]}
x31={xDataSet[30]} x32={xDataSet[31]} x33={xDataSet[32]} x34={xDataSet[33]} x35={xDataSet[34]} x36={xDataSet[35]} x37={xDataSet[36]} x38={xDataSet[37]} x39={xDataSet[38]} x40={xDataSet[39]}
x41={xDataSet[40]} x42={xDataSet[41]} x43={xDataSet[42]} x44={xDataSet[43]} x45={xDataSet[44]} x46={xDataSet[45]} x47={xDataSet[46]} x48={xDataSet[47]} x49={xDataSet[48]} x50={xDataSet[49]}
x51={xDataSet[50]} x52={xDataSet[51]} x53={xDataSet[52]} x54={xDataSet[53]} x55={xDataSet[54]} x56={xDataSet[55]} x57={xDataSet[56]} x58={xDataSet[57]} x59={xDataSet[58]} x60={xDataSet[59]}
x61={xDataSet[60]} x62={xDataSet[61]} x63={xDataSet[62]} x64={xDataSet[63]} x65={xDataSet[64]} x66={xDataSet[65]} x67={xDataSet[66]} x68={xDataSet[67]} x69={xDataSet[68]} x70={xDataSet[69]}
x71={xDataSet[70]} x72={xDataSet[71]} x73={xDataSet[72]} x74={xDataSet[73]} x75={xDataSet[74]} x76={xDataSet[75]} x77={xDataSet[76]} x78={xDataSet[77]} x79={xDataSet[78]} x80={xDataSet[79]}
x81={xDataSet[80]} x82={xDataSet[81]} x83={xDataSet[82]} x84={xDataSet[83]} x85={xDataSet[84]} x86={xDataSet[85]} x87={xDataSet[86]} x88={xDataSet[87]} x89={xDataSet[88]} x90={xDataSet[89]}
x91={xDataSet[90]} x92={xDataSet[91]} x93={xDataSet[92]} x94={xDataSet[93]} x95={xDataSet[94]} x96={xDataSet[95]} x97={xDataSet[96]} x98={xDataSet[97]} x99={xDataSet[98]} x100={xDataSet[99]}
y1={yDataSet[1]} y2={yDataSet[2]} y3={xDataSet[2]} y4={xDataSet[3]} y5={xDataSet[4]} y6={xDataSet[5]} y7={xDataSet[6]} y8={xDataSet[7]} y9={xDataSet[8]} y10={xDataSet[9]}
y11={yDataSet[10]} y12={yDataSet[11]} y13={yDataSet[12]} y14={yDataSet[13]} y15={yDataSet[14]} y16={yDataSet[15]} y17={yDataSet[16]} y18={yDataSet[17]} y19={yDataSet[18]} y20={yDataSet[19]}
y21={yDataSet[20]} y22={yDataSet[21]} y23={yDataSet[22]} y24={yDataSet[23]} y25={yDataSet[24]} y26={yDataSet[25]} y27={yDataSet[26]} y28={yDataSet[27]} y29={yDataSet[28]} y30={yDataSet[29]}
y31={yDataSet[30]} y32={yDataSet[31]} y33={yDataSet[32]} y34={yDataSet[33]} y35={yDataSet[34]} y36={yDataSet[35]} y37={yDataSet[36]} y38={yDataSet[37]} y39={yDataSet[38]} y40={yDataSet[39]}
y41={yDataSet[40]} y42={yDataSet[41]} y43={yDataSet[42]} y44={yDataSet[43]} y45={yDataSet[44]} y46={yDataSet[45]} y47={yDataSet[46]} y48={yDataSet[47]} y49={yDataSet[48]} y50={yDataSet[49]}
y51={yDataSet[50]} y52={yDataSet[51]} y53={yDataSet[52]} y54={yDataSet[53]} y55={yDataSet[54]} y56={yDataSet[55]} y57={yDataSet[56]} y58={yDataSet[57]} y59={yDataSet[58]} y60={yDataSet[59]}
y61={yDataSet[60]} y62={yDataSet[61]} y63={yDataSet[62]} y64={yDataSet[63]} y65={yDataSet[64]} y66={yDataSet[65]} y67={yDataSet[66]} y68={yDataSet[67]} y69={yDataSet[68]} y70={yDataSet[69]}
y71={yDataSet[70]} y72={yDataSet[71]} y73={yDataSet[72]} y74={yDataSet[73]} y75={yDataSet[74]} y76={yDataSet[75]} y77={yDataSet[76]} y78={yDataSet[77]} y79={yDataSet[78]} y80={yDataSet[79]}
y81={yDataSet[80]} y82={yDataSet[81]} y83={yDataSet[82]} y84={yDataSet[83]} y85={yDataSet[84]} y86={yDataSet[85]} y87={yDataSet[86]} y88={yDataSet[87]} y89={yDataSet[88]} y90={yDataSet[89]}
y91={yDataSet[90]} y92={yDataSet[91]} y93={yDataSet[92]} y94={yDataSet[93]} y95={yDataSet[94]} y96={yDataSet[95]} y97={yDataSet[96]} y98={yDataSet[97]} y99={yDataSet[98]} y100={yDataSet[99]}
/>
</svg>
There can obviously be several reasons for this not working. To give you a hint on what the problem might be, here is some context:
The program does not crash. Instead it loads everything perfectly, except there is no line
The problem is not that the datasets are empty or something similar. Both datasets contain hundred numbers in the form of strings ('10', for example).
Side note: If there is a more efficient way to do this, I would like some ideas on that as well, lmao.
Thanks in advance.
The line element has four co-ordinate attributes — x1, x2, y1 and y2 — representing the start and end points of the line for the two axis.
All the other x* and y* attributes you provide are ignored.
You seem to be trying to use it to draw a polygon.
If you're aiming at drawing a complex lines (e.g "zig zag line") – <polyline> would be the most appropriate element.
svg {
display:inline-block;
width:10vw;
overflow:visible
}
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<polyline points="0,0 50,50 100,0 150, 50" fill="none" stroke="#000" stroke-width="10" />
</svg>
<!--closed shape – since polygon will automatically close a shape -->
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<polygon points="0,0 50,50 100,0 150, 50" fill="none" stroke="#000" stroke-width="10" />
</svg>
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<path d="M0, 0 L50, 50 L100,0 L150, 50" fill="none" stroke="#000" stroke-width="10" />
</svg>
<!--closed shape – caused by Z(closepath) command -->
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<path d="M0, 0 L50, 50 L100,0 L150, 50z" fill="none" stroke="#000" stroke-width="10" />
</svg>
As #Quentin pointed out, <line> can only display single straight line segements.
If you actually want to draw a "solid" closed shape (that should e.g have a color fill) – polygon is the way to go.
Depending on your further usage in your app you might also consider using a path element instead.
I am trying to draw empty circles as a markers for line in SVG. Problem I am having is that I can see line inside the empty circles. How can I "erase" it?
Options I have considered:
Adjust line to be shorter and position markers "outside" of the line. Unfortunately this does not work well for my scenario. I have arbitrary lines as an input(could be any possible path element) and calculating "shorter" lines is quite difficult in general case.
I could create a mask which is basically same line in black and smaller sized markers to be used as "holes". Problem with this is that I would basically have to duplicate all the lines in mask and in case when markers have more complex shape than a circle this also becomes quite complicated.
Is there anything else I could try?
<svg width="200" height="200">
<marker id="line-start" markerWidth="14" markerHeight="14" refX="14" refY="7" markerUnits="userSpaceOnUse" orient="auto-start-reverse" overflow="visible">
<circle fill="none" stroke="#666" cx="7" cy="7" r="7" />
</marker>
<path stroke="#666" marker-start="url(#line-start)" marker-end="url(#line-start)" stroke-width="2" d="M10,100 L 190,100" />
</svg>
The easiest thing to do is to add a fill that's the same color as the background. But if you have an image or complex gradient background, then you can do a green-screen replacement. Use a fill on the marker of 100% red (or green or blue - whatever you're not using elsewhere), and then use a filter to select and remove it.
<svg width="200px" height="200px">
<defs>
<filter id="empty" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feColorMatrix type="matrix" values="1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
1 -255 -255 1 0"/>
<feComposite operator="out" in="SourceGraphic"/>
</filter>
<marker id="line-start" markerWidth="14" markerHeight="14" refX="14" refY="7" markerUnits="userSpaceOnUse" >
<circle stroke="#666" cx="7" cy="7" r="7" fill="red"/>
</marker>
</defs>
<path filter="url(#empty)" stroke="#666" marker-start="url(#line-start)" marker-end="url(#line-start)" stroke-width="2" d="M50,100 L 180,100" />
</svg>
I'm using D3.js to draw some SVG shapes. When the shapes overlap, I would like to either dilate or erode the parent/child shapes accordingly to get rid of overlap.
However, these shapes only have a stroke outline, no fill.
There are several examples that use the native SVG filters to achieve this effect, but they all rely on shapes having a fill color.
When I set fill="none" for erode filter, the shape disappears. When I do so for the dilate filter, I am left with a shape with a big stroke-width. I just want a static shape stroke (with transparent fill or no fill).
Here's my JSFiddle.
https://jsfiddle.net/programmingprincess/2q3zd0o5/4/
The red/blue/green shape on the left would be perfect IF the blue shape had an empty fill, and only the blue outline. In the JS Fiddle, they use the green to create a "mocked" blue stroke.
The two shapes to the right show what happens when I mess with the stroke and fill values for the green shape.
You can apply your erode filter on the path within a mask, then apply that mask to the same path.
<svg viewBox="0 0 612 792">
<defs>
<filter id="erode">
<feMorphology operator="erode" in="SourceGraphic" radius="12" />
</filter>
<path id="myPath" d="M193.193,85c23.44,0.647,45.161,0.774,62,12c1.596,1.064,12,11.505,12,13
c0,2.941,8.191,5.669,3,12c-3.088,3.767-6.01-0.758-11-1c-19.56-0.948-33.241,12.296-33,34c0.163,14.698,8.114,24.492,4,41
c-1.408,5.649-6.571,15.857-10,21c-2.484,3.726-7.898,10.784-12,13c-4.115-11.677,2.686-27.29-6-35c-6.693-5.942-20.021-4.051-26,1
c-13.573,11.466-11.885,41.492-7,58c-5.8,1.772-18.938,7.685-23,12c-6.752-10.805-15.333-17.333-24-26c-3.307-3.307-9.371-12-15-12
c-16.772,0-13.963-15.741-13-28c1.283-16.324,1.727-28.24,4-42c1.276-7.72,8-16.411,8-23c0-7.416,15.945-29,23-29
c4.507,0,17.678-8.701,24-11C164.853,90.76,178.27,88.546,193.193,85" />
<mask id="myMask">
<!-- Everything under a white pixel will be visible -->
<rect x="0" y="0" width="612" height="792" fill="white" />
<use href="#myPath" fill="black" filter="url(#erode)"></use>
</mask>
</defs>
<path d="M50,50 L150,150 L250,10 Z" fill="green"></path>
<use href="#myPath" fill="purple" mask="url(#myMask)"></use>
</svg>
Codepen
Using ksav idea of a mask I modified it a bit by drawing the stroke inside the mask after eroding the shape. Because the stroke color is always black when using erode I add an additional filter to invert the mask using a feColorMatrix
<svg viewBox="0 0 612 792">
<defs>
<filter id="erode">
<feMorphology operator="erode" in="SourceGraphic" radius="12" />
</filter>
<filter id="invert">
<feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
</filter>
<path id="myPath" d="M193.193,85c23.44,0.647,45.161,0.774,62,12c1.596,1.064,12,11.505,12,13
c0,2.941,8.191,5.669,3,12c-3.088,3.767-6.01-0.758-11-1c-19.56-0.948-33.241,12.296-33,34c0.163,14.698,8.114,24.492,4,41
c-1.408,5.649-6.571,15.857-10,21c-2.484,3.726-7.898,10.784-12,13c-4.115-11.677,2.686-27.29-6-35c-6.693-5.942-20.021-4.051-26,1
c-13.573,11.466-11.885,41.492-7,58c-5.8,1.772-18.938,7.685-23,12c-6.752-10.805-15.333-17.333-24-26c-3.307-3.307-9.371-12-15-12
c-16.772,0-13.963-15.741-13-28c1.283-16.324,1.727-28.24,4-42c1.276-7.72,8-16.411,8-23c0-7.416,15.945-29,23-29
c4.507,0,17.678-8.701,24-11C164.853,90.76,178.27,88.546,193.193,85" />
<mask id="myMask">
<!-- Everything under a white pixel will be visible -->
<g filter="url(#invert)">
<rect x="0" y="0" width="612" height="792" fill="white" />
<use href="#myPath" stroke-width=9 stroke="#000" fill="white" filter="url(#erode)"></use>
</g>
</mask>
</defs>
<path d="M50,50 L150,150 L250,10 Z" fill="green" stroke="#00f" stroke-width=4></path>
<rect x="0" y="0" width="612" height="792" fill="purple" mask="url(#myMask)" />
</svg>
I am using d3.js to build a timeline which has axis.
Now, in order to make the time range more visible i have increased the axis's tick to a certain width and length as shown in the image below
The gray and white stripes below are actually the tick lines of the axis which looks something like this in terms of code:
<g class="tick" transform="translate(1241.7410071942445,0)" style="opacity: 1;">
<line y2="-457px" x2="0" y1="55px" style="stroke-width: 202px;"></line>
<text y="3" x="0" dy=".71em" style="text-anchor: middle;">Oct 05</text>
</g>
Now, I want to create a border around these stripes. I tried something like this:
<g class="tick" transform="translate(1241.7410071942445,0)" style="opacity: 1;">
<line y2="-457px" x2="0" y1="55px" style="stroke-width: 202px; border: 1px solid black"> </line>
<text y="3" x="0" dy=".71em" style="text-anchor: middle;">Oct 05</text>
</g>
As expected, this clearly did not work and I cannot find any way to achieve that. Any Ideas?
You can use an SVG filter to add an outline around a line.
<svg width="200" height="200"
viewPort="0 0 200 200" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="outline" x="-20%" y="-20%" width="140%" height="140%">
<feMorphology operator="dilate" radius="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
<filter>
</defs>
<g filter="url(#outline)" >
<line x1="40" y1="120"
x2="120" y2="40"
stroke="red"
stroke-width="10"/>
</g>
</svg>