How to rotate SVG Oval text along with path - javascript

I'm trying to rotate the SVG text path alone with an oval path like the moving text forward not
I tried but the current CSS is rotating the whole text and I'm trying is moving forward each text to next
svg {
overflow: visible !important;
padding:50px;
}
svg text{
-webkit-animation: loading 8s linear infinite;
-moz-animation: loading 8s linear infinite;
transform-origin: center
}
#-webkit-keyframes loading {
100%{
webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
#-moz-keyframes loading {
100%{
moz-transform: rotate(360deg);
transform: rotate(360deg);
}
}
<svg class="Oval_text_svg" width="400" fill="#C3A97E" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 511 746">
<defs>
<pattern id="img1_ad77a587-4b9f-4d4a-9ef6-976c76e4841d" patternUnits="userSpaceOnUse" width="610" height="915">
<image href="https://via.placeholder.com/728x890.png" x="0" y="0"></image>
</pattern>
</defs>
<path fill="url(#img1_ad77a587-4b9f-4d4a-9ef6-976c76e4841d)" id="Oval_text" d="M 255.5 745.5 C 246.8091888427734 745.5 238.0447235107422 744.8538818359375 229.4499053955078 743.5796508789062 C 220.9792785644531 742.3237915039062 212.4674530029297 740.42724609375 204.1510009765625 737.9429321289062 C 187.7884521484375 733.0548095703125 171.6926422119141 725.7604370117188 156.310546875 716.2623901367188 C 141.2030944824219 706.933837890625 126.6308212280273 695.3866577148438 112.9986343383789 681.9414672851562 C 99.49217987060547 668.620361328125 86.79063415527344 653.3209228515625 75.24672698974609 636.46826171875 C 63.70054626464844 619.6121826171875 53.21763610839844 601.0636596679688 44.08917999267578 581.3378295898438 C 34.87191009521484 561.4202880859375 26.95518112182617 540.1268920898438 20.5587272644043 518.0492553710938 C 14.04345417022705 495.5616455078125 9.039545059204102 472.0283508300781 5.685999870300293 448.1030883789062 C 2.244818210601807 423.552734375 0.5 398.2844543457031 0.5 373 C 0.5 347.7155456542969 2.244818210601807 322.447265625 5.685999870300293 297.8969116210938 C 9.039545059204102 273.9716491699219 14.04345417022705 250.4383697509766 20.5587272644043 227.9507293701172 C 26.95518112182617 205.8730926513672 34.87191009521484 184.5797271728516 44.08917999267578 164.6621856689453 C 53.21763610839844 144.9363708496094 63.70054626464844 126.3878173828125 75.24672698974609 109.5317306518555 C 86.79063415527344 92.67909240722656 99.49217987060547 77.379638671875 112.9986343383789 64.05854797363281 C 126.6308212280273 50.61336517333984 141.2030944824219 39.06618118286133 156.310546875 29.73763656616211 C 171.6926422119141 20.23954582214355 187.7884521484375 12.94518184661865 204.1510009765625 8.057090759277344 C 212.4674530029297 5.572727203369141 220.9792785644531 3.676181793212891 229.4499053955078 2.420363664627075 C 238.0447235107422 1.146090865135193 246.8091888427734 0.5 255.5 0.5 C 264.1908264160156 0.5 272.9552612304688 1.146090865135193 281.5500793457031 2.420363664627075 C 290.0207214355469 3.676181793212891 298.5325317382812 5.572727203369141 306.8489990234375 8.057090759277344 C 323.2115478515625 12.94518184661865 339.307373046875 20.23954582214355 354.689453125 29.73763656616211 C 369.7969055175781 39.06618118286133 384.3691711425781 50.61336517333984 398.0013732910156 64.05854797363281 C 411.5078125 77.379638671875 424.2093505859375 92.67909240722656 435.7532653808594 109.5317306518555 C 447.2994689941406 126.3878173828125 457.7823486328125 144.9363708496094 466.9108276367188 164.6621856689453 C 476.1280822753906 184.5797271728516 484.0448303222656 205.8730926513672 490.4412841796875 227.9507293701172 C 496.95654296875 250.4383697509766 501.96044921875 273.9716491699219 505.3139953613281 297.8969116210938 C 508.7551879882812 322.447265625 510.5 347.7155456542969 510.5 373 C 510.5 398.2844543457031 508.7551879882812 423.552734375 505.3139953613281 448.1030883789062 C 501.96044921875 472.0283508300781 496.95654296875 495.5616455078125 490.4412841796875 518.0492553710938 C 484.0448303222656 540.1268920898438 476.1280822753906 561.4202880859375 466.9108276367188 581.3378295898438 C 457.7823486328125 601.0636596679688 447.2994689941406 619.6121826171875 435.7532653808594 636.46826171875 C 424.2093505859375 653.3209228515625 411.5078125 668.620361328125 398.0013732910156 681.9414672851562 C 384.3691711425781 695.3866577148438 369.7969055175781 706.933837890625 354.689453125 716.2623901367188 C 339.307373046875 725.7604370117188 323.2115478515625 733.0548095703125 306.8489990234375 737.9429321289062 C 298.5325317382812 740.42724609375 290.0207214355469 742.3237915039062 281.5500793457031 743.5796508789062 C 272.9552612304688 744.8538818359375 264.1908264160156 745.5 255.5 745.5 Z" stroke="none"></path>
<text dy="-10">
<textPath xlink:href="#Oval_text" class="svg_title">#Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text #Text </textPath>
</text>
</svg>

If you need to animate separate elements containing text like illustrated in your gif:
css offset-path might be an option.
The actual motion path is defined by offset-path property
Css: ellipse motion path
offset-path: path('M 50 37.5 C 50 58.2 38.8 75 25 75 C 11.2 75 0 58.2 0 37.5 C 0 16.8 11.2 0 25 0 C 38.8 0 50 16.8 50 37.5 Z');
svg {
overflow: visible;
height: 90vmin;
display: inline-block;
}
text {
font-size: 10px;
}
.textLabel {
offset-path: path('M 50 37.5 C 50 58.2 38.8 75 25 75 C 11.2 75 0 58.2 0 37.5 C 0 16.8 11.2 0 25 0 C 38.8 0 50 16.8 50 37.5 Z');
offset-distance: 0%;
offset-rotate: 0deg;
animation: rotate1 3s linear infinite forwards;
}
.textLabel2 {
offset-distance: 50%;
animation-name: rotate2;
}
#keyframes rotate1 {
to {
offset-distance: 100%;
}
}
#keyframes rotate2 {
to {
offset-distance: 150%;
}
}
<svg class="Oval_text_svg" width="400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 75">
<!-- optional: show motion path -->
<path stroke="#ccc" stroke-width="1" fill="none" id="oval" d="M 50 37.5 C 50 58.2 38.8 75 25 75 C 11.2 75 0 58.2 0 37.5 C 0 16.8 11.2 0 25 0 C 38.8 0 50 16.8 50 37.5 Z" /> <g class="textLabel">
<circle cx="0" cy="0" r="10%" fill="red" />
<text fill="#fff" x="0" y="0" text-anchor="middle" dominant-baseline="central">
01
</text>
</g>
<g class="textLabel textLabel2">
<circle cx="0" cy="0" r="10%" fill="red" />
<text fill="#fff" x="0" y="0" text-anchor="middle" dominant-baseline="central">
02
</text>
</g>
</svg>
Changing offset-distance values will allow you to change offsets along the motion path (e.g offset-distance: 50% for the second element).

Related

how to make svg curve at one end

im using svg for circular progress bar , i want to make one end curve not the both end . how is this possible ?
how can i implement one end curve in svg?
svg {
height: 80vh;
margin: 10vh auto;
border: 1px solid red;
display: block;
transform: rotate(-90deg);
}
svg circle {
stroke-width: 10;
fill: transparent;
}
#outer {
stroke: lightgrey;
}
#inner {
stroke: blue;
animation: value 2.5s linear forwards;
stroke-linecap: round;
}
#keyframes value {
0% {
stroke-dasharray: 0 100;
}
100% {
stroke-dasharray: 90 100;
}
}
<svg viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle id="outer" cx="50" cy="50" r="40" />
<circle id="inner" pathLength="100" cx="50" cy="50" r="40" />
</svg>
The easiest way would be to mask the starting point of the blue circle.
For this you will need a <mask> like so:
<mask id="m">
<rect width="100" height="100" fill="white"/>
<rect x="85" y="40" width="10" height="10" />
</mask>
Please observe that the first rectangle is white and it covers the whole chart. (Everything under a white pixel will be visible). The smaller rectangle is black and covers the starting point of the blue circle. Everything under a black pixel will be invisible.
svg {
height: 80vh;
margin: 10vh auto;
border: 1px solid red;
display: block;
transform: rotate(-90deg);
}
svg circle {
stroke-width: 10;
fill: transparent;
}
#outer {
stroke: lightgrey;
}
#inner {
stroke: blue;
animation: value 2.5s linear forwards;
stroke-linecap: round;
}
#keyframes value {
0% {
stroke-dasharray: 0 100;
}
100% {
stroke-dasharray: 90 100;
}
}
<svg viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<mask id="m">
<rect width="100" height="100" fill="white"/>
<rect x="85" y="40" width="10" height="10" />
</mask>
<circle id="outer" cx="50" cy="50" r="40" />
<circle id="inner" pathLength="100" cx="50" cy="50" r="40" mask="url(#m)" />
</svg>
Try this code:
svg {
height: 80vh;
margin: 10vh auto;
border: 1px solid red;
display: block;
transform: rotate(-90deg);
border-top-right-radius: 20px;
}
svg circle {
stroke-width: 10;
fill: transparent;
}
#outer {
stroke: lightgrey;
}
#inner {
stroke: blue;
animation: value 2.5s linear forwards;
stroke-linecap: round;
}
#keyframes value {
0% {
stroke-dasharray: 0 100;
}
100% {
stroke-dasharray: 90 100;
}
}
<svg viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle id="outer" cx="50" cy="50" r="40" />
<circle id="inner" pathLength="100" cx="50" cy="50" r="40" />
</svg>
I came up with an alternative solution than enxaneta suggested. The problem with using a mask is that when your value goes over 96% or so, the circle isn't completely filled and the mask is revealed.
Instead, you can set a rounded progress line on top of another progress line that has flat endcaps. By rotating the rounded progress line by roughly 5 degrees, the flat end is revealed.
Here's how to do that in React Native with react-native-svg:
const radius = 60;
let myPercentage = 40;
const circleCircumference = 2 * Math.PI * radius;
const valueOffset = circleCircumference -
(circleCircumference * myPercentage * 0.98) / 100;
<Svg height={radius * 2 + 30} width={radius * 2 + 30}>
<G rotation={-90} originX={radius + 15} originY={radius + 15}>
// Background gray circle
<Circle
cx="50%"
cy="50%"
r={radius}
stroke="rgb(60, 60, 60)"
fill="transparent"
strokeWidth="10"
strokeDasharray={circleCircumference}
strokeLinecap="butt"
/>
// Background progress circle with flat ends
<Circle
cx="50%"
cy="50%"
r={radius}
stroke={"rgb(0, 51, 204)"}
fill="transparent"
strokeWidth="10"
strokeDasharray={circleCircumference}
strokeDashoffset={valueOffset}
strokeLinecap="butt"
/>
// Progress circle with round ends rotated by 5 degrees
<Circle
cx="50%"
cy="50%"
r={radius}
stroke={rgb(0, 51, 204)}
fill="transparent"
rotation={5}
originX={radius + 15}
originY={radius + 15}
strokeWidth="10"
strokeDasharray={circleCircumference}
strokeDashoffset={valueOffset}
strokeLinecap="round"
/>
</G>
</Svg>

how to write #keyframes CSS, in Javascript [duplicate]

This question already has answers here:
Passing parameters to css animation
(3 answers)
Closed 2 years ago.
i have this svg with the respective css which animates the path.
HTML
<div>
<svg version="1.1" baseProfile="full" width="400" height="300" xmlns="http://www.w3.org/2000/svg">
<path class="grey" fill="none" stroke="#B7C4D2" stroke-width="10" d="M 136 277 A 100 100 0 1 1 264 277"></path>
<path class="blue" fill="none" stroke="#30A7F4" stroke-width="10" d="M 136 277 A 100 100 0 1 1 264 277"></path>
</svg>
</div>
CSS
.blue {
stroke-dasharray: 490;
stroke-dashoffset: 490;
animation: draw 2s linear forwards;
}
#keyframes draw {
to {
stroke-dashoffset: 260;
}
}
i want to make the "stroke-dashoffset: 260" value dynamic, any way to do that via JS?
One performant way of changing CSS with JS is using CSS variables.
you'd then have:
.blue {
stroke-dasharray: 490;
stroke-dashoffset: 490;
animation: draw 2s linear forwards;
}
#keyframes draw {
to {
stroke-dashoffset: var(--stroke-dashoffset);
}
}
and in your JS:
const theBlue = document.querySelectorAll('svg .blue')[0]
theBlue.style.setProperty('--stroke-dashoffset', 260)
you can inject internal style and you can send offest value to setAnimationOffest(120) dynamically.
function addAnimation(name, body) {
if (!dynamicStyles) {
dynamicStyles = document.createElement('style');
dynamicStyles.type = 'text/css';
document.head.appendChild(dynamicStyles);
}
dynamicStyles.sheet.insertRule(`#keyframes ${ name } {
${ body }
}`, dynamicStyles.length);
}
let dynamicStyles = null;
function addAnimation(name, body) {
if (!dynamicStyles) {
dynamicStyles = document.createElement('style');
dynamicStyles.type = 'text/css';
document.head.appendChild(dynamicStyles);
}
dynamicStyles.sheet.insertRule(`#keyframes ${ name } {
${ body }
}`, dynamicStyles.length);
}
function setAnimationOffest(offest){
addAnimation('draw', `
to { stroke-dashoffset: ${offest}; }
`);
};
setAnimationOffest(120);
.blue {
stroke-dasharray: 490;
stroke-dashoffset: 490;
animation: draw 2s linear forwards;
}
#keyframes draw {
to {
stroke-dashoffset: 260;
}
}
<div>
<svg version="1.1" baseProfile="full" width="400" height="300" xmlns="http://www.w3.org/2000/svg">
<path class="grey" fill="none" stroke="#B7C4D2" stroke-width="10" d="M 136 277 A 100 100 0 1 1 264 277"></path>
<path class="blue" fill="none" stroke="#30A7F4" stroke-width="10" d="M 136 277 A 100 100 0 1 1 264 277"></path>
</svg>
</div>

Get HTML after Javascript execution/modification with WKWebView and Regex in Swift 4+

I have been trying to retrieve some data from a website which seems like javascript performs a modification before/after load
I want to find a specific DIV into HTML, and it just doesn't appear to be there the way I'm doing this, it loads only a part of the HTML but don't the one I need to.
Here is my code:
let url = URL(string: "https://www.spaceweatherlive.com")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil{
print(error)
}else{
// let htmlcontent = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
let htmlcontent = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
// let regex = try? NSRegularExpression(pattern: "<h4 class=\"text-center\">Kp[0-9]</h4>", options: .caseInsensitive)
let substring = "<h4 class=\"text-center\">Kp[0-9]"
let matched = self.matches(for: substring, in: htmlcontent!)
print(matched)
if htmlcontent!.contains(substring){
print("Substring FOUND!!")
}else{
// print("Nope, printing whole html:")
// print(htmlcontent)
}
// print(htmlcontent)
}
}
task.resume()
Function to search for substring
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
String(text[Range($0.range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
Thanks! any help or advice to try is very appreciated
This is the DIV which I need to retrieve (KP[0-9] value)
We can maybe start with a simple expression and capture the SVG highchart part of the indicator that we have in the question:
(<div id="Kp_gauge".+?<\/div><\/div><\/div><\/div>)
Then, the title part can be written or attached to it with another similar expression.
Here, we are saying that start from:
<div id="Kp_gauge"
collect everything (.+?) to closest:
<\/div><\/div><\/div><\/div>
We can do the same for the title, if we wish, or we can just on the top of that write or add Kp-index and the problem may be solved.
The h3` title is always going to be:
<h3 class="card-header text-center">Kp-index <span class="text-primary" data-toggle="tooltip" title="" data-original-title="The Kp-index is a geomagnetic activity index based on data from magnetometers around the world. The gauge below shows the most recently observed Kp-value from the Planetary K-index of the NOAA SWPC and can be used to make a rough estimate of the current global geomagnetic conditions."><svg class="svg-inline--fa fa-info-circle fa-w-16" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="info-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" data-fa-i2svg=""><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg><!-- <i class="fas fa-info-circle" aria-hidden="true"></i> --></span></h3>
which can be simply added.
DEMO
const regex = /(<div id="Kp_gauge".+?<\/div><\/div><\/div><\/div>)/gm;
const str = `<div id="Kp_gauge" style="height: 170px; overflow: hidden;" data-highcharts-chart="6"><div id="highcharts-c5v532c-27" dir="ltr" class="highcharts-container " style="position: relative; overflow: hidden; width: 448px; height: 170px; text-align: left; line-height: normal; z-index: 0; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);"><svg version="1.1" class="highcharts-root" style="font-family:"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif;font-size:12px;" xmlns="http://www.w3.org/2000/svg" width="448" height="170" viewBox="0 0 448 170"><desc>Created with Highcharts 7.1.1</desc><defs><clipPath id="highcharts-c5v532c-28-"><rect x="0" y="0" width="428" height="145" fill="none"></rect></clipPath></defs><rect fill="none" class="highcharts-background" x="0" y="0" width="448" height="170" rx="0" ry="0"></rect><rect fill="none" class="highcharts-plot-background" x="10" y="10" width="428" height="145"></rect><g class="highcharts-pane-group" data-z-index="0"><path fill="#eee" d="M 108 140.5 A 116 116 0 0 1 339.9999420000048 140.38400001933334 L 293.5999652000029 140.4304000116 A 69.6 69.6 0 0 0 154.4 140.5 Z" class="highcharts-pane " stroke="#cccccc" stroke-width="1"></path></g><g class="highcharts-grid highcharts-yaxis-grid" data-z-index="1"><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 108 140.5" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 114.99565598883464 100.82566337422242" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 135.13884459819855 65.93663727636142" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 165.99999999999997 40.041053161005124" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 203.8568113906361 26.262300650583867" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 244.14318860936393 26.262300650583867" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 282 40.04105316100511" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 312.86115540180145 65.93663727636144" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 333.00434401116536 100.82566337422242" opacity="1"></path><path fill="none" data-z-index="1" class="highcharts-grid-line" d="M 224 140.5 L 340 140.5" opacity="1"></path></g><rect fill="none" class="highcharts-plot-border" data-z-index="1" x="10" y="10" width="428" height="145"></rect><g class="highcharts-axis highcharts-yaxis" data-z-index="2"><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 108 140.5 L 118 140.5" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 114.99565598883464 100.82566337422242 L 124.39258219669372 104.24586480747911" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 135.13884459819855 65.93663727636142 L 142.79928902938835 72.36451337322681" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 165.99999999999997 40.041053161005124 L 170.99999999999997 48.701307198849506" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 203.8568113906361 26.262300650583867 L 205.59329316730538 36.11037818070595" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 244.14318860936393 26.262300650583867 L 242.40670683269462 36.11037818070595" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 282 40.04105316100511 L 277 48.70130719884949" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 312.86115540180145 65.93663727636144 L 305.2007109706117 72.36451337322683" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 333.00434401116536 100.82566337422242 L 323.60741780330625 104.24586480747911" opacity="1"></path><path fill="none" class="highcharts-tick" stroke="#ccd6eb" stroke-width="2" d="M 340 140.5 L 330 140.5" opacity="1"></path><path fill="none" class="highcharts-axis-line" data-z-index="7" d="M 108 140.5 A 116 116 0 0 1 339.9999420000048 140.38400001933334 M 224 140.5 A 0 0 0 0 0 224 140.5 "></path></g><g data-z-index="2" class="highcharts-data-labels highcharts-series-0 highcharts-solidgauge-series highcharts-tracker" transform="translate(10,10) scale(1 1)"><g class="highcharts-label highcharts-data-label highcharts-data-label-color-0 highcharts-tracker" data-z-index="1" transform="translate(181,84)"></g></g><g class="highcharts-series-group" data-z-index="3"><g data-z-index="0.1" class="highcharts-series highcharts-series-0 highcharts-solidgauge-series highcharts-tracker" transform="translate(10,10) scale(1 1)" clip-path="url(#highcharts-c5v532c-28-)"><path fill="rgb(85,191,59)" d="M 98 130.5 A 116 116 0 0 1 155.89957006990173 30.09910338080769 L 179.13974204194105 70.25946202848462 A 69.6 69.6 0 0 0 144.4 130.5 Z" sweep-flag="0" stroke-linecap="round" stroke-linejoin="round" class="highcharts-point highcharts-color-0"></path></g><g data-z-index="0.1" class="highcharts-markers highcharts-series-0 highcharts-solidgauge-series " transform="translate(10,10) scale(1 1)" clip-path="none"></g></g><text x="10" class="highcharts-title" data-z-index="4" style="color:#333333;font-size:18px;fill:#333333;" y="24"></text><text x="224" text-anchor="middle" class="highcharts-subtitle" data-z-index="4" style="color:#666666;fill:#666666;" y="24"></text><g class="highcharts-legend" data-z-index="7"><rect fill="none" class="highcharts-legend-box" rx="0" ry="0" x="0" y="0" width="8" height="8" visibility="hidden"></rect><g data-z-index="1"><g></g></g></g><g class="highcharts-axis-labels highcharts-yaxis-labels" data-z-index="7"><text x="98" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="144.49999999999997" opacity="1">0</text><text x="105.59872978097555" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="101.40546194096572" opacity="1">1</text><text x="127.47840016700879" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="63.50876117949602" opacity="1">2</text><text x="160.99999999999997" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="35.38079912316074" opacity="1">3</text><text x="202.1203296139668" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="20.414223120461784" opacity="1">4</text><text x="245.87967038603324" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="20.414223120461784" opacity="1">5</text><text x="287" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="35.38079912316073" opacity="1">6</text><text x="320.5215998329912" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="63.508761179496034" opacity="1">7</text><text x="342.40127021902447" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="101.40546194096574" opacity="1">8</text><text x="350" style="color:#666666;cursor:default;font-size:11px;fill:#666666;" text-anchor="middle" transform="translate(0,0)" y="144.5" opacity="1">9</text></g></svg><div class="highcharts-loading highcharts-loading-hidden" style="position: absolute; background-color: rgb(255, 255, 255); opacity: 0; text-align: center; z-index: 10; left: 10px; top: 10px; width: 428px; height: 145px; display: none;"><span class="highcharts-loading-inner" style="font-weight: bold; position: relative; top: 45%;">Loading…</span></div><div class="highcharts-data-labels highcharts-series-0 highcharts-solidgauge-series highcharts-tracker" style="position: absolute; left: 10px; top: 10px; opacity: 1; visibility: inherit;"><div class="highcharts-label highcharts-data-label highcharts-data-label-color-0 highcharts-tracker" style="position: absolute; left: 181px; top: 84px; opacity: 1;"><span data-z-index="1" style="position: absolute; font-family: "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif; font-size: 11px; white-space: nowrap; font-weight: bold; color: rgb(0, 0, 0); margin-left: 0px; margin-top: 0px; left: 5px; top: 5px;"><h4 class="text-center">Kp3</h4><div class="text-center" style="font-size:10px;color:silver">18-21 UTC</div></span></div></div></div></div>`;
let m;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
console.log(`Found match, group ${groupIndex}: ${match}`);
});
}
Edit
For getting the Kp we can simply use an expression similar to:
<h[1-6] class="text-center">(Kp[0-9]+)<\/h[1-6]>
const regex = /<h[1-6] class="text-center">(Kp[0-9]+)<\/h[1-6]>/gm;
const str = `<span data-z-index="1" style="position: absolute; font-family: "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif; font-size: 11px; white-space: nowrap; font-weight: bold; color: rgb(0, 0, 0); margin-left: 0px; margin-top: 0px; left: 5px; top: 5px;"><h4 class="text-center">Kp3</h4><div class="text-center" style="font-size:10px;color:silver">18-21 UTC</div></span>`;
let m;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
console.log(`Found match, group ${groupIndex}: ${match}`);
});
}
DEMO

Not cloning as expected

I have a JavaScript code that clones the last li node, if a user clicks on the button Add Choice or if an input clicked is the last input. The cross button on the right removes the li node it is located in. It won't remove if there's only one input element left.
Everything works fine except:
If you start removing the lis from bottom to top until there's only one input element and click on the input element, it will clone and add a new li. Now if you click on the second(cloned) input element, it doesn't clone. I get the following error in the console.
TypeError: el.parentNode.nextSibling.nextSibling.classList is undefined
Also, how would I go about attaching a focus event listener to the input element and trigger the cloning process without conflicting with the click event. I tried doing this the cloning is being done twice. If the user navigates through the inputs using the Tab key, and if the last input is focused. I'd like to trigger the cloning process.
var wheelBuilder = {
getNodes: function(c) {
return document.querySelectorAll(c);
},
getLast: function(e) {
return [].slice.call(e).pop();
},
insertAfter: function(n, r) {
r.parentNode.insertBefore(n, r.nextSibling);
},
clone: function() {
var inputs = wheelBuilder.getNodes('.choiceInput'),
lastInput = wheelBuilder.getLast(inputs),
cl = lastInput.parentNode.cloneNode(true);
wheelBuilder.insertAfter(cl, lastInput.parentNode);
var cross = wheelBuilder.getNodes('.cross'),
choiceInput = wheelBuilder.getNodes('.choiceInput'),
lastCross = wheelBuilder.getLast(cross),
lastChoiceInput = wheelBuilder.getLast(choiceInput);
lastCross.addEventListener('click', wheelBuilder.removeChoice);
lastChoiceInput.addEventListener('click', wheelBuilder.addIfLastInput);
},
addIfLastInput: function(e) {
var el = e.target,
inputs = wheelBuilder.getNodes('.choiceInput');
isLast = (inputs.length > 1) ? el.parentNode.nextSibling.nextSibling.classList.contains('input') : false;
if (!isLast) {
wheelBuilder.clone();
el.focus();
}
},
removeChoice: function(e) {
var choice = e.target.parentNode.parentNode.parentNode.parentNode,
node = choice.parentNode;
if (wheelBuilder.getNodes('.choiceInput').length > 1) {
node.removeChild(choice);
}
}
}
wheelBuilder.getNodes('.cross').forEach(function(el, _) {
el.addEventListener('click', wheelBuilder.removeChoice);
});
wheelBuilder.getNodes('.choiceInput').forEach(function(el, _) {
el.addEventListener('click', wheelBuilder.addIfLastInput);
});
var addChoice = document.getElementById('addChoice');
addChoice.addEventListener('click', wheelBuilder.clone);
.wheelBuilder {
position: absolute;
width: 100%;
font-size: 1.3em;
font-family: Sans;
}
.wheelBuilder .wrapper {
max-width: 60%;
margin: 0 auto;
margin-bottom: 50px;
padding: 0 10px 10px 10px;
}
.title #gears svg {
transform: translate(5px, 5px);
fill: #565656;
}
.wheelBuilder .title {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 1.5em;
font-weight: 500;
padding: 15px 20px;
margin: 0 0 20px 0px;
line-height: 40px;
outline: 0;
width: 100%;
background: #ffffff;
color: #565656;
box-shadow: 0px 0px 4px 4px #dfdfdf;
}
.wrapper ol {
position: relative;
padding: 0;
margin: 0.25em 0.125em;
width: 100%;
background: #ffffff;
padding: 20px;
box-shadow: 0px 0px 4px 4px #dfdfdf;
}
.choices {
position: relative;
list-style-type: none;
width: 100%;
}
.choices:first-child {
text-align: left;
color: #565656;
}
.choices:not(:last-child) {
margin-bottom: 10px;
}
.add {
text-align: left;
}
#plus svg {
z-index: 3;
transform: translate(20px, 7px);
fill: #565656;
cursor: pointer;
}
#addChoice, #applyChanges {
position: relative;
height: 40px;
padding: 0 .8em;
background: #ffffff;
border: 0;
font-size: 1.2em;
color: #565656;
cursor: pointer;
margin-top: 10px;
box-sizing: border-box;
border: 1px solid #ffffff;
box-shadow: 0px 0px 5px 3px #dfdfdf;
transition: 0.2s all ease-in;
}
#addChoice {
margin-left: -32px;
padding: 0 .8em 0 2.2em;
}
#applyChanges {
width: 100%;
}
#plus:hover + #addChoice, #addChoice:hover, #applyChanges:hover {
border: 1px solid #a8ab0a;
box-shadow: 0px 0px 5px 4px #d1d1d1;
}
.choiceInput {
width: 100%;
height: 40px;
background: #ffffff;
padding: 0 .4em;
color: #565656;
font-size: 1.2em;
border: 1px solid #cfcfcf;
transition: border .2s ease-in, box-shadow .2s ease-in;
}
.choiceInput:hover {
border: 1px solid #c6c85f;
box-shadow: inset 0 0 5px 1px #cfcfcf;
}
.choiceInput:focus {
border: 1px solid #a8ab0a;
box-shadow: inset 0 0 5px 1px #cfcfcf;
}
.cross {
position: absolute;
height: 40px;
right: 0;
top: 0;
}
.cross svg {
transform: translate(50%, 0);
}
.cross path {
cursor: pointer;
}
svg g .outline {
stroke:#c2c2c2;
fill:#ffffff;
}
svg g .x {
fill:none;
stroke:#c4c4c4;
stroke-width:2;
stroke-linecap:round;
}
.cross g:hover path {
stroke: #e75141;
}
#media only screen and (max-width: 480px) {
.toast p, .toast span, .spinBtn {
font-size: 18px;
line-height: 18px;
}
.wheelBuilder {
top: 120vmin;
}
.wheelBuilder .wrapper {
max-width: 90%;
}
.wrapper ol li {
padding: 0;
}
}
<div class="wheelBuilder">
<div class="wrapper">
<h2 class="title">
<span id="gears">
<svg xmlns="http://www.w3.org/2000/svg" width="30" viewBox="0 0 24 24">
<path d="M 16.064453 2 C 15.935453 2 15.8275 2.0966094 15.8125 2.2246094 L 15.695312 3.2363281 C 15.211311 3.4043017 14.773896 3.6598036 14.394531 3.9882812 L 13.457031 3.5839844 C 13.339031 3.5329844 13.202672 3.5774531 13.138672 3.6894531 L 12.201172 5.3105469 C 12.136172 5.4215469 12.166531 5.563625 12.269531 5.640625 L 13.078125 6.2402344 C 13.030702 6.4865104 13 6.7398913 13 7 C 13 7.2601087 13.030702 7.5134896 13.078125 7.7597656 L 12.269531 8.359375 C 12.166531 8.435375 12.137172 8.5774531 12.201172 8.6894531 L 13.138672 10.310547 C 13.202672 10.422547 13.339031 10.468969 13.457031 10.417969 L 14.394531 10.011719 C 14.773896 10.340196 15.211311 10.595698 15.695312 10.763672 L 15.8125 11.775391 C 15.8275 11.903391 15.935453 12 16.064453 12 L 17.935547 12 C 18.064547 12 18.1725 11.903391 18.1875 11.775391 L 18.304688 10.763672 C 18.789173 10.59553 19.227802 10.340666 19.607422 10.011719 L 20.542969 10.414062 C 20.660969 10.465063 20.797328 10.420594 20.861328 10.308594 L 21.798828 8.6875 C 21.863828 8.5765 21.833469 8.4344219 21.730469 8.3574219 L 20.923828 7.7578125 C 20.970992 7.5121818 21 7.2593796 21 7 C 21 6.7398913 20.969298 6.4865104 20.921875 6.2402344 L 21.730469 5.640625 C 21.833469 5.564625 21.862828 5.4225469 21.798828 5.3105469 L 20.861328 3.6894531 C 20.797328 3.5774531 20.660969 3.5310312 20.542969 3.5820312 L 19.605469 3.9882812 C 19.226104 3.6598036 18.788689 3.4043017 18.304688 3.2363281 L 18.1875 2.2246094 C 18.1725 2.0966094 18.064547 2 17.935547 2 L 16.064453 2 z M 17 5.25 C 17.966 5.25 18.75 6.034 18.75 7 C 18.75 7.967 17.966 8.75 17 8.75 C 16.034 8.75 15.25 7.967 15.25 7 C 15.25 6.034 16.034 5.25 17 5.25 z M 7.0644531 9 C 6.9354531 9 6.8275 9.0966094 6.8125 9.2246094 L 6.6386719 10.710938 C 5.8314079 10.940599 5.1026855 11.35237 4.5175781 11.921875 L 3.1582031 11.335938 C 3.0402031 11.284937 2.9038438 11.329406 2.8398438 11.441406 L 1.9023438 13.0625 C 1.8373437 13.1735 1.8677031 13.315578 1.9707031 13.392578 L 3.1679688 14.279297 C 3.0687954 14.672064 3 15.076469 3 15.5 C 3 15.923531 3.0687954 16.327936 3.1679688 16.720703 L 1.9707031 17.609375 C 1.8677031 17.685375 1.8383437 17.827453 1.9023438 17.939453 L 2.8398438 19.560547 C 2.9038438 19.672547 3.0402031 19.717016 3.1582031 19.666016 L 4.5175781 19.078125 C 5.1026855 19.64763 5.8314079 20.059401 6.6386719 20.289062 L 6.8125 21.775391 C 6.8275 21.903391 6.9354531 22 7.0644531 22 L 8.9355469 22 C 9.0645469 22 9.1725 21.903391 9.1875 21.775391 L 9.3613281 20.289062 C 10.168592 20.059401 10.897314 19.64763 11.482422 19.078125 L 12.841797 19.664062 C 12.959797 19.715062 13.096156 19.670594 13.160156 19.558594 L 14.097656 17.9375 C 14.162656 17.8265 14.132297 17.684422 14.029297 17.607422 L 12.832031 16.720703 C 12.931205 16.327936 13 15.923531 13 15.5 C 13 15.076469 12.931205 14.672064 12.832031 14.279297 L 14.029297 13.390625 C 14.132297 13.314625 14.161656 13.172547 14.097656 13.060547 L 13.160156 11.439453 C 13.096156 11.327453 12.959797 11.282984 12.841797 11.333984 L 11.482422 11.921875 C 10.897314 11.35237 10.168592 10.940599 9.3613281 10.710938 L 9.1875 9.2246094 C 9.1725 9.0966094 9.0645469 9 8.9355469 9 L 7.0644531 9 z M 8 13.5 C 9.105 13.5 10 14.395 10 15.5 C 10 16.605 9.105 17.5 8 17.5 C 6.895 17.5 6 16.605 6 15.5 C 6 14.395 6.895 13.5 8 13.5 z"></path>
</svg>
</span> Wheel Builder
</h2>
<ol>
<li class="choices">Choices (enter up to 50 choices):</li>
<li class="choices input">
<input class="choiceInput" type="text" autocomplete="off" value="" />
<span class="cross">
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 0 60 40" version="1.1">
<g>
<path class="outline" d="M10,20 l12,-20 h38 v40 h-38z" />
<path class="x" d="M40,20 m-7,-7 l14,14 m0,-14 l-14,14" />
</g>
</svg>
</span>
</li>
<li class="choices input">
<input class="choiceInput" type="text" autocomplete="off" value="" />
<span class="cross">
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 0 60 40" version="1.1">
<g>
<path class="outline" d="M10,20 l12,-20 h38 v40 h-38z" />
<path class="x" d="M40,20 m-7,-7 l14,14 m0,-14 l-14,14" />
</g>
</svg>
</span>
</li>
<li class="choices input">
<input class="choiceInput" type="text" autocomplete="off" value="" />
<span class="cross">
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 0 60 40" version="1.1">
<g>
<path class="outline" d="M10,20 l12,-20 h38 v40 h-38z" />
<path class="x" d="M40,20 m-7,-7 l14,14 m0,-14 l-14,14" />
</g>
</svg>
</span>
</li>
<li class="choices input">
<input class="choiceInput" type="text" autocomplete="off" value="" />
<span class="cross">
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 0 60 40" version="1.1">
<g>
<path class="outline" d="M10,20 l12,-20 h38 v40 h-38z" />
<path class="x" d="M40,20 m-7,-7 l14,14 m0,-14 l-14,14" />
</g>
</svg>
</span>
</li>
<li class="choices input">
<input class="choiceInput" type="text" autocomplete="off" value="" />
<span class="cross">
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 0 60 40" version="1.1">
<g>
<path class="outline" d="M10,20 l12,-20 h38 v40 h-38z" />
<path class="x" d="M40,20 m-7,-7 l14,14 m0,-14 l-14,14" />
</g>
</svg>
</span>
</li>
<li class="choices add">
<span id="plus">
<svg xmlns="http://www.w3.org/2000/svg" height="28" viewBox="0 0 32 32" version="1.1">
<path d="M 16 3 C 8.832031 3 3 8.832031 3 16 C 3 23.167969 8.832031 29 16 29 C 23.167969 29 29 23.167969 29 16 C 29 8.832031 23.167969 3 16 3 Z M 16 5 C 22.085938 5 27 9.914063 27 16 C 27 22.085938 22.085938 27 16 27 C 9.914063 27 5 22.085938 5 16 C 5 9.914063 9.914063 5 16 5 Z M 15 10 L 15 15 L 10 15 L 10 17 L 15 17 L 15 22 L 17 22 L 17 17 L 22 17 L 22 15 L 17 15 L 17 10 Z "></path>
</svg>
</span>
<input id="addChoice" type="button" name="addChoice" value="Add Choice..." />
</li>
<li class="choices">
<input id="applyChanges" type="button" name="applyChanges" value="Apply Wheel Changes" />
</li>
</ol>
</div>
</div>
I'd appreciate any help.
Now if you click on the second(cloned) input element, it doesn't clone. I get the following error in the console.
The issue is in your addIfLastInput method. el.parentNode.nextSibling.nextSibling.classList.contains('input') is a fragile code. Small changes in tree structure (as you can see) can crash your application. It seems you want to check if the parent element of the clicked element is the last li that has .choices.input class names. For that, you can simply code:
addIfLastInput: function(e) {
var el = e.target,
inputs = wheelBuilder.getNodes('.choices.input'),
isLast = el.parentNode === wheelBuilder.getLast(inputs);
if (isLast) {
/// ...
}
},
Also, how would I go about attaching a focus event listener to the input element and trigger the cloning process without conflicting with the click event.
I'd just listen to focus event instead of click.
wheelBuilder.getNodes('.choiceInput').forEach(function(el, _) {
el.addEventListener('focus', wheelBuilder.addIfLastInput);
});
And:
clone: function() {
// ...
lastChoiceInput.addEventListener('focus', wheelBuilder.addIfLastInput);
},
Here is a demo on jsfiddle.

How to do a vertical wave animation of a ribbon in svg?

My question is that I want to create a svg file (Code is mentioned below) like a vertical ribbon(which is already done), now I want to give a wave effect that will start from the top to the bottom and will be in continuous mode. But it isn't working.
#keyframes thread{
from {
stroke-dashoffset: 200;
opacity:.5;
}
to{
stroke-dashoffset: 2;
opacity:1;
}
}
.anime{
stroke-dasharray: 200;
animation: thread 2s .4s forwards infinite ease-in-out;
}
<div class="position-absolute">
<svg height="200" width="200" >
<g class="anime">
<path id="shape-1" d="M100 0 c-20 20 -20 25 -10 40 s20 30 -2 60 h50 m11.5 -100 c-20 20 -20 25 -10 40 s20 30 -2 60"
fill="transparent" stroke="black" stroke-width="2"></path>
<path id="shape-2" d="M100 0 c-25 25 -25 30 -15 40 s25 35 -9 55 h64 m16.5 -105 c-25 25 -25 30 -15 45 s25 35 -4 60"
fill="transparent" stroke="black" stroke-width="2">
</path>
</g>
</svg>
</div>
I think you're looking for something like this(correct me if I'm wrong):
var svg = document.getElementById("cups");
var s = Snap(svg);
var ribbon1 = Snap.select('#ribbon1');
var ribbon2 = Snap.select('#ribbon2');
var ribbon1Points = ribbon1.node.getAttribute('d');
var ribbon2Points = ribbon2.node.getAttribute('d');
var toRibbon2 = function(){
ribbon1.animate({ d: ribbon2Points }, 1000, toRibbon1);
}
var toRibbon1 = function(){
ribbon1.animate({ d: ribbon1Points }, 1000, toRibbon2);
}
toRibbon1();
h1 {
text-align: center;
}
svg {
display: block;
margin: 0 auto;
}
#ribbon2 {
opacity: 0;
}
svg {
fill: lightgrey;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<svg width="100" height="130" viewBox="0 0 75 47" xmlns="http://www.w3.org/2000/svg">
<path id="ribbon1" class="cls-1" d="M64.5,64.5c0,15-4,19-4,31a17.23,17.23,0,0,0,10,16h28s-11-8-11-17,1-16,1-30-1-25-1-25h-26S64.5,49.5,64.5,64.5Z" transform="translate(-58.85 -39)"/>
<path id="ribbon2" class="cls-1" d="M61,60c0,15,5.5,23.5,5.5,35.5s-6,16-6,16h28s5-4,5-13S85,76,85,62s2.5-22.5,2.5-22.5h-26S61,45,61,60Z" transform="translate(-58.85 -39)"/>
</svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
</body>
</html>
it probably requires some tweaking in the paths itself to make for a more realistic effect. Here it is specifically applied to your paths:
var svg = document.getElementById("cups");
var s = Snap(svg);
var ribbon1 = Snap.select('#ribbon1');
var ribbon2 = Snap.select('#ribbon2');
var ribbon1Points = ribbon1.node.getAttribute('d');
var ribbon2Points = ribbon2.node.getAttribute('d');
var toRibbon2 = function() {
ribbon1.animate({
d: ribbon2Points
}, 1000, toRibbon1);
}
var toRibbon1 = function() {
ribbon1.animate({
d: ribbon1Points
}, 1000, toRibbon2);
}
toRibbon1();
h1 {
text-align: center;
}
svg {
display: block;
margin: 0 auto;
}
#ribbon2 {
opacity: 0;
}
svg {
fill: lightgrey;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<svg height="200" width="200">
<g class="anime">
<path id="ribbon1" d="M100 0 c-20 20 -20 25 -10 40 s20 30 -2 60 h50 m11.5 -100 c-20 20 -20 25 -10 40 s20 30 -2 60"
fill="transparent" stroke="black" stroke-width="2"></path>
<path id="ribbon2" d="M100 0 c-25 25 -25 30 -15 40 s25 35 -9 55 h64 m16.5 -105 c-25 25 -25 30 -15 45 s25 35 -4 60"
fill="transparent" stroke="black" stroke-width="2">
</path>
</g>
</svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
</body>
</html>
Here's an extensive guide on how to make this (which I based my answer on). It's basically two SVG paths that morph into each other over time.

Categories