Please view the demo here: https://codepen.io/bsoutendijk/pen/gOvLMaq
Expected behavior: After clicking "update path" button, the path will change, and the text will appear along the path by the end of the animation.
Actual behavior: The text appears correctly on the path upon load, but when clicking update path, the text does not appear in the correct place.
code:
const path = document.getElementById('myPath');
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('clicked!');
path.setAttribute('d', getRandomCurve());
})
function getRandomCurve() {
const curveSize = Math.round(Math.random() * 300);
return `M 100 350 q ${curveSize} -300 300 0`
}
#myPath {
transition: all ease-out 2s;
}
<div>
<button id="myButton">Update Path</button>
<svg height="400" width="450">
<path id="myPath" d="M 100 350 q 150 -300 300 0" stroke="blue" stroke-width="5" fill="none" />
<text style="font-size: 2em;">
<textPath startOffset="30%" text-anchor="middle" href="#myPath">
Hello World
</textPath>
</text>
</svg>
</div>
How do I use textPath in a way that it can work with animated paths?
const path = document.getElementById("myPath2");
function getRandomCurve() {
const curveSize = Math.round(Math.random() * 300);
return `M 100 350 q ${curveSize} -300 300 0`;
}
setInterval(() => {
const pathDef = getRandomCurve();
path.setAttribute("d", pathDef);
}, 1000)
.anim {
transition: 1s;
}
<div>
<svg height="400" width="450">
<path id="myPath2" class="anim" d="M 100 350 q 150 -300 300 0" stroke="blue" stroke-width="5" fill="none" />
<text x="0" y="0" style="font-size: 2em;">
<textPath startOffset="30%" href="#myPath2" class="anim">
Hello World
<animateTransform attributeName="transform" dur="2s" repeatCount="indefinite"/>
</textPath>
</text>
</svg>
</div>
Oh, i can do it :)
"animateTransform" can fix your problem.
"repeatCount" - important attribute, try to remove it and your animation will stop after "dur"
Related
I'm using SVG in HTML to draw a bended word along a path. Now that I managed to do draw the text, I need to split the word in 2 colors along the path that the word sits on. You can see the effect I'm trying to make in the image. Does anyone know how can I split the word in such a way? (I'm not sure if this matters, but the word is constantly bended, stretched and moved by the user, by modifying the "d" attribute of the path.)
var path = document.getElementById("Path");
var textPath = document.getElementById("TextPath");
document.onmousemove = function(e){
path.setAttribute("d", `M 100 100 q ${e.x-100} ${e.y-100} 230 0`);
textPath.setAttribute("textLength", path.getTotalLength() + "px")
}
svg {
width: 500px;
height: 500px;
}
<svg>
<path id="Path" d="M 100 100 q 50 -100 230 0" stroke="#000000" stroke-width="1" fill="none"></path>
<text id="Text" fill="#000000" font-size="64px">
<textPath startOffset="0%" id="TextPath" alignment-baseline="middle" href="#Path" startOffset="0%" startOffset="0%">Example</textPath>
</text>
</svg>
Here's a simpler version of what I have now. What I want to happen, is to color everything above the curve with one color and everything below with another.
One way of doing it: you use the text twice: once filled with color A and once filled with color B. Next you clip the second text with the path.
<svg viewBox="0 0 260 200">
<defs>
<path id="pth" d="M70,150 C10,40 240,40 180,150" stroke="red" fill="none" />
<text id="txt" font-size="45" text-anchor="middle" dominant-baseline="central">
<textPath font-size="35" startOffset="50%" href="#pth">
SSSSSSSSSSSS
</textPath>
</text>
<clipPath id="cp">
<use href="#pth" />
</clipPath>
</defs>
<use href="#txt" fill="blue" />
<use href="#txt" fill="orange" clip-path="url(#cp)" />
</svg>
I have radios aligned on an arch.
I would like to add a transition that slides the selected inner blue dot along the arch to the newly selected radio.
I have a working demo setup.
WORKING DEMO
See a solution in the fiddle (click on the blue button):
Just put the button in a "g" and rotate it with transition:
const button = d3.select("#button");
let angle = 60;
button.on("click", () => {
angle = -angle;
button.transition().duration(1000).attr("transform", `translate(${cX}, ${cY}) rotate(${angle})`);
});
Another option is to use animateMotion along the path:
<svg width="300" height="200">
<path fill="none" stroke="blue" stroke-width="3"
d="M 20,20 A 100,100 0 0 0 220,20" />
<circle r="10" fill="white" stroke="blue" stroke-width="3">
<animateMotion dur="3s" repeatCount="indefinite"
path="M 20,20 A 100,100 0 0 0 220,20 A 100,100 1 1 1 20,20" />
</circle>
</svg>
I am trying to work out why the code below no-longer works in Firefox or Chrome.
The files were last modified over ten years ago.
The static svg dispays, but the script does not run. To my considerable surprise, it does work as it should in Edge, as does a more complicated diagram with interactive elements.
There is probably some obscure setting I need to doctor in Firefox, but I don't know where to look. I don't know when I last tried one of these files, but I would be fairly sure they still worked a couple of years ago.
The code is probably full of daftnesses, as I have done very little javascript, and I probably should now be using requestanimationframe, but that is not the point - it has worked, and still does in Edge.
(Question edited to remove link to irrelevant SMIL version of the animation.)
This is the html file:
<html>
<head>
<title>
SVG slider-crank animated by script
</title>
</head>
<body onload="main()">
<script type="text/javascript">
<!--
var svgdoc = null;
var crank = null;
var crosshead = null;
var conrod = null;
var pi = Math.PI;
function main()
{
var timer = null;
var angle = 0;
var diagram = document.getElementById('svg');
if (diagram && diagram.contentDocument)
{
svgdoc = diagram.contentDocument;
}
else
{
try
{
svgdoc = diagram.getSVGDocument();
}
catch(exception)
{
alert("Unable to get SVG document");
}
}
crank = svgdoc.getElementById('ShowCrank');
crosshead = svgdoc.getElementById('ShowCrosshead');
conrod = svgdoc.getElementById('ShowConRod');
timer = setInterval(function(){(angle = rotation(angle))}, 25);
}
function rotation(angle)
{
var step = 3;
var theta = angle * pi / 180;
var alpha = Math.asin(Math.sin(theta) / 5);
var offset = 100 * (Math.cos(theta) -1) - 500 * (Math.cos(alpha) - 1);
crank.setAttributeNS(null, 'transform', ("rotate(" + angle + ", 800, 300)"));
crosshead.setAttributeNS(null, 'transform', ("translate(" + offset + ", 0)"));
conrod.setAttributeNS(null, 'transform', ("translate(" + offset + ", 0) rotate(" + (alpha * 180 / pi) + ", 400, 300)"));
angle = angle < 360 - step ? angle + step : 0;
return angle;
}
-->
</script>
<object id="svg" type="image/svg+xml" data="Slider_Crank.svg" width="1200" height="800">
<param name="src" value="Slider_Crank.svg">
</object>
</body>
</html>
This is the svg file:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
width="1200"
height="800">
<title> Slider-Crank </title>
<defs>
<rect
id="Slidebar"
stroke-width="1"
stroke="black"
fill="silver"
fill-opacity="1"
x="0"
y="-12"
width="300"
height="24"
/>
<g id="Crosshead" stroke-width="1" stroke="black" fill-opacity="1">
<rect
fill="gold"
x="-50"
y="-25"
width="100"
height="50"
/>
<circle cx="0" cy="0" r="15" fill="white"/>
</g>
<g id="Crank" stroke-width="1" stroke="black" fill-opacity="1">
<path fill="silver"
d="M 99.959 40.000
A 40 40 0 0 0 99.959, -40.000
A 450 450 0 0 1 9.950, -49.000
A 50 50 0 1 0 9.950, 49.000
A 450 450 0 0 1 99.959, 40.000
z"/>
<circle cx="100" cy="0" r="25" fill="white"/>
<circle cx="0" cy="0" r="30" fill="lightgrey"/>
</g>
<g id="ConRod" stroke-width="1" stroke="black" fill-opacity="0.7">
<path fill="silver"
d="M 12.387 21.715
A 30 30 0 0 1 27.551 17.776
L 453.475 22.035
A 30 30 0 0 1 473.243 29.733
A 40 40 0 0 1 473.243 -29.733
A 30 30 0 0 1 453.475 -22.035
L 27.551 -17.776
A 30 30 0 0 1 12.387 -21.715
A 25 25 0 0 1 12.387 21.715
z"/>
<circle cx="0" cy="0" r="25" fill="silver"/>
<circle cx="0" cy="0" r="15" fill="white"/>
<circle cx="500" cy="0" r="40" fill="silver"/>
<circle cx="500" cy="0" r="25" fill="white"/>
</g>
</defs>
<use id="ShowTopSlidebar" xlink:href="#Slidebar" x="150" y="263"/>
<use id="ShowBottomSlidebar" xlink:href="#Slidebar" x="150" y="337"/>
<use id="ShowCrosshead" xlink:href="#Crosshead" x="400" y="300"/>
<use id="ShowCrank" xlink:href="#Crank" x="800" y="300"/>
<use id="ShowConRod" xlink:href="#ConRod" x="400" y="300"/>
</svg>
Thanks to Robert Longson: Firefox about:config setting 'security.fileuri.strict_origin_policy'
Okay so i am relatively new to both svgand snap.svg.
However i am trying out the different features and how to create all sorts of different text elements.
Normal text is not a challenge however i started wondering how do i make text that actually bends?
Say for instance i want to create a text such as this:
As you can see the text bends.
My Ultimate goal is to use snap.svgto allow the user to bend the text however I'm not quite sure on how to do this.
Has anyone attempted to bend text and is able to point me in the right direction?
SVG is necessary to define a path with curved dimensions.
Here an example:
<svg height="70" width="300">
<defs>
<path id="myTextPath" d="M 30 55 q 100 -46 240 0" />
</defs>
<rect x="0" y="0" height="70" width="300" fill="#292425" />
<text x="10" y="100" style="font-size: 18px; stroke: #E6E6E6;">
<textPath xlink:href="#myTextPath">INVITATION TIL BRYLLUP</textPath>
</text>
</svg>
Update:
And this is a simple example where a user can bend the text, in real time.
Using VanillaJS (Javascript) and Snap.svg.
(function() {
var orientation = document.getElementById("orientation");
orientation.addEventListener("change", function() {
bendText(this.value);
});
function bendText(value) {
var snap = Snap("#myTextPath");
snap.attr("d", "M 30 55 q 100 " + value * -1 + " 240 0");
}
})();
input[type=range][orient=vertical] {
writing-mode: bt-lr;
/* IE */
-webkit-appearance: slider-vertical;
/* WebKit */
width: 8px;
height: 175px;
padding: 0 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg.js"></script>
<input id="orientation" type="range" orient="vertical" value="46" max="46" min="-46" />
<svg height="70" width="300">
<defs>
<path id="myTextPath" d="M 30 55 q 100 -46 240 0" />
</defs>
<rect x="0" y="0" height="70" width="300" fill="#292425" />
<text x="10" y="100" style="font-size: 18px; stroke: #E6E6E6;">
<textPath xlink:href="#myTextPath">INVITATION TIL BRYLLUP</textPath>
</text>
</svg>
Demo
You can just use a textpath attribute, which takes a path and positions the string along it.
var s = Snap(500,500);
var path = "M 100 200 C 200 100 300 0 400 100 C 500 200 600 300 700 200 C 800 100 900 100 900 100";
var text = s.text(50,50,'Hi there, Im a textpath that curves along a path string')
.attr({ 'textpath': path })
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg.js"></script>
docs
I have the following SVG which works fine on it's own: http://jsfiddle.net/bL3k48jn/1/
However, when imported with fabric.loadSVGFromString the text is no long attached to the path, and the font styles are lost.
The SVG
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px">
<path id="textPath" fill="none" stroke-width="5" stroke="yellow"
d="
M 250 -250
m 0, 250
a -250,250 0 1,0 0,500
a 250,-250 0 1,0 0,-500
"
/>
<text fill="black" font-family = "arial" text-anchor="middle">
<textPath startOffset="50%" fill="white" stroke="black" stroke-width="2" font-size="36px" font-weight="bold" xlink:href="#textPath">Happy Birthday Dad!</textPath>
</text>
</svg>
You should see a circle with some text following the bottom edge of the curve.
The code I'm using to import the SVG is here: http://jsfiddle.net/hnzgy940/2/
The Javascript
var canvas = new fabric.Canvas('c');
// This is the SVG as a single line
var str = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="500px" height="500px"><path id="text_path" d="M 250 -250 m 0,250 a -250,250 0 1,0 0,500 a 250,-250 0 1,0 0,-500" fill="none" stroke-width="5" stroke="yellow"/><text font-family="arial" text-anchor="middle"><textPath startOffset="50%" fill="white" stroke="black" stroke-width="2" font-size="36px" font-weight="bold" xlink:href="#text_path">Happy Birthday Dad!</textPath></text></svg>';
fabric.loadSVGFromString(str, function(objects, options) {
svg_text = fabric.util.groupSVGElements(objects, options)
svg_text.set({
originX: 'center',
originY: 'center',
left: canvas.width / 2,
top: canvas.height / 2
});
svg_text.setCoords();
canvas.add(svg_text);
canvas.renderAll();
});
It appears to be ignoring the xlink:href attribute, on the <textPath> element, along with the font styles, which I guess means it might just be ignoring the entire <textPath> element, but I can't see why.
I had a problem previously where the text wouldn't show at all, but I think this may have been because I didn't have the correct xmlns attribute.
Text Paths are not supported yet in Fabric, but there is a branch:
https://github.com/lirmont/fabric.js/tree/Curve-Text
Also, see the conversation:
https://github.com/kangax/fabric.js/issues/729