How to fill only a part of the SVG? [duplicate] - javascript

This question already has answers here:
Fill only Half a star with SVG
(4 answers)
Closed 5 years ago.
I want to be able to pass a number/percentage to a function that fills a part of an svg. For example in an svg of a bucket if the real life bucket is filled 90% of the way and it returns 10 liters as an integer I want the bucket svg on my website to be filled 90% of the way with blue.
I believe you can portion an SVG into parts with an ID and fill each ID based on the number the function is passed and then edit each part with some kind of jquery function? However, I feel like filling only part of an SVG must be a more popular thing to do but I haven't been able to find a straightforward/simple way of doing this?

The simplest way to do this is with a <linearGradient>.
function setWaterLevel(percent)
{
document.getElementById("stop1").setAttribute("offset", percent+"%");
document.getElementById("stop2").setAttribute("offset", percent+"%");
}
document.getElementById("slider").addEventListener("input", function(evt) {
setWaterLevel(evt.target.value);
});
setWaterLevel(0);
<svg width="300px" viewBox="0 0 100 100">
<defs>
<linearGradient id="water" x1="0" y1="1" x2="0" y2="0">
<stop id="stop1" offset="0%" stop-color="blue"/>
<stop id="stop2" offset="0%" stop-color="transparent"/>
</linearGradient>
</defs>
<polygon points="0,0, 100,0, 80,100, 20,100"
fill="url(#water)" stroke="black"/>
</svg>
<br>
<input id="slider" type="range" min="0" max="100" step="10" value="0"/>

Your best solution is probably going to be to use two <canvas> tags and write the fill portion over the container portion. That way you can calculate both separately and don't have to worry about a partial SVG file.
Doing it this way, you'll have much greater control over everything.

Related

Chrome does not render rotate if applied from javascript in <![CDATA[] > script tag inside an SVG [duplicate]

This question already has answers here:
SVG translate seems to behave different in Chrome/Chromium
(2 answers)
Closed 4 years ago.
Hello i have a very odd problem. If i set the attribute "transformation" with the value "rotate(30, 3, 3)" with javascript from outside the SVG tag, chrome actually renders everything perfectly. But if i set a tag inside the SVG and set the attribute from inside a tag. The rotation does not get applied at all. Firefox has no problem with that.
Edit: I am sorry, i was tired last night. Of course i have to execute the function once the object is loaded. In my original document the whole thing is called way after the object is loaded. But thanks, now the fiddle is accurate.
Here is a fiddle to show, that what i am trying is actually valid:
https://jsfiddle.net/k7a5jg85/
Here is a fiddle to show the problem:
https://jsfiddle.net/c2z29mhf/1/
<svg>
<script>
<![CDATA[
let rotationOffsetX = -20 + (30/2);
let rotationOffsetY = -20;
let rotationAngle = -(40 + 90);
document.getElementById('barrel1').setAttribute("transform",
"rotate(" +
rotationAngle + ", " +
rotationOffsetX + ", " +
rotationOffsetY + ")" );
]]>
</script>
<svg data-kind="dummy" id="barrel1" x="0" y="0" width="2" height="15">
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="0" y1="7.5" x2="2" y2="7.5">
<stop offset="0" style="stop-color:#1E263C"/>
<stop offset="0.5355" style="stop-color:#AAAABE"/>
<stop offset="1" style="stop-color:#1E263C"/>
</linearGradient>
<rect fill="url(#SVGID_10_)" width="2" height="15"/>
<rect y="15" fill="#1E263C" width="2" height="0.5"/>
</svg>
</svg>
The answer can be seen in the javascript console:
TypeError: document.getElementById(...) is null
You're executing the script before the <svg> element you are referencing has been loaded.

How to animate fill in SVG shapes along path? [duplicate]

This question already has answers here:
How to draw a fill svg?
(3 answers)
How to animate handwriting text on the web page using SVG?
(2 answers)
Closed 4 years ago.
This is quite a conundrum for a couple of days now.
I have an SVG shape that I want to animate — namely, I want it to change it's color along a specific path. A simple shape looks like this:
Shapes in my SVGs represent strokes constituting Chinese characters (Hanzi) — the example has been extracted from here:
Here are the highlights:
It has to be done on SVGs (I need scalability).
Each shape is bound by a path and filled with color.
Most SVGs will have multiple shapes, often intersecting each other.
The shapes don’t have a constant width.
Each shape will have a dedicated path/stroke guiding it's direction.
Each of the shapes has to be animated in sequence.
The animations should have a constant speed (makes it a bit tricky with different element sizes, I think).
My research so far indicates that using a fill transform will not work with such shapes:
<html>
<body>
<head>
</head>
<div style="display: flex; align-items: center; justify-content: center; position: absolute; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%);">
<div style="width: 100%; height: 100%; position: relative;">
<svg
xmlns="http://www.w3.org/2000/svg"
width="38.639015mm"
height="80.743088mm"
viewBox="0 0 38.639015 80.743087"
id="svg831">
<defs>
<linearGradient id="left-to-right">
<stop offset="0" stop-color="#4DAF4C">
<animate dur="2s" attributeName="offset" fill="freeze" from="0" to="1" />
</stop>
<stop offset="0" stop-color="#fff">
<animate dur="2s" attributeName="offset" fill="freeze" from="0" to="1" />
</stop>
</linearGradient>
</defs>
<g
id="layer1"
transform="translate(-103.9336,-77.49032)">
<g
transform="matrix(0.35277777,0,0,-0.35277777,55.696164,149.58928)"
id="g823">
<path
style="fill:url(#left-to-right);stroke:none;stroke-width:0.99999994"
d="m 139.73604,123.37501 c 1.33299,-40.000002 5.667,-67.333302 12.99999,-82.000002 2.06847,-4.338361 2.99033,-6.799913 6,-12.000001 16.66701,-25.999995 36.667,-42.333293 60,-48.999998 23.33301,-6.66669 31.83301,-6.499995 25.50001,0.50001 -6.333,6.99999 -8.5,26.499998 -6.50001,58.499986 h -3.99999 l -8.00001,-43.999988 c -22,7.33332 -40.66699,21.99999 -55.99999,43.999988 -1.77033,3.225113 -4.43601,9.376455 -6,15 -4.66701,11.333303 -7.66701,34.333306 -9,69.000005 v 5 c -0.667,21.333 -1,40.333 -1,57 l 9,7 -26,12 c 0.66699,-14 1.667,-39.333 3,-76"
id="my_path" />
</g>
</g>
</svg>
</div>
</div>
</body>
</html>
I also considered clipping path, but I an not sure how to clip/unclip one object along another object realiably so that neighboring strokes are not clipped. Some other solutions need a lot of manual tinkering, which I need to avoid due to the volume of data I want to process.
EDIT: I had to edit this question as it has been — in my opinion wrongly — marked as a duplicate, as I clearly stated my restrictions (the other question has been solved by converting everything into strokes).
There is, however, a potential solution that came to my mind, but I am not sure how to proceed: I could make my red stroke so thick that it's wider than my shape, and then apply a clipping path with the exact same coordinates as my shape — then it would suffice to just animate the red stroke. But can separate objects have non-summing clipping paths?

How to expose svg pattern property?

Say we have a complex SVG pattern to use in object fill:
<svg width=650 height=680>
<defs>
<radialGradient id='rg' cx='50%' cy='50%' r='50%' fx='50%' fy='50%'>
<stop offset='10%' style='stop-color:#3d3d3d;'/>
<stop offset='80%' style='stop-color:#3d3d3d;'/>
<stop offset='100%' style='stop-color:#fa9fb5; stop-opacity:1;' /> <!-- mutate stop-color per instance/application -->
</radialGradient>
<pattern id="texture" patternUnits="userSpaceOnUse" width="5" height="5" viewBox="0 0 5 5">
<rect width='5' height='5' fill='url(#rg)'/>
</pattern>
</defs>
</svg>
And it incorporates what could be called background color. (here it is stop-color from <stop offset='100%' style='stop-color:#fa9fb5; stop-opacity:1;' />). Actually we have 28 similar patterns. We have a color pallet (17 * 9 colors). And we want to have ability to create an SVG object for example rectangle or circle that would use a pattern yet would have a color from pallet as background.
It seems like code pre generation of 28 * 17 * 9 (3808) svg patterns each of 15-30 lines would make any mobile browser crush. While tipical application scenario is to use from 100 to 400 patterns.
We could generate and regenerate patterns while app is running yet it would be a messy job - keep svg dom clean, resource leaks and such also often regeneration of patterns on fly would probably require SVG to blink and reload.
There is an option to create an interface that would allow pattern background change and implement it using say D3js for each of 28 patterns... and store them in a factory... and each time we would need to create an object we would create instance and assign to it some random theme color... While I do like such solution and probably will end up with it (could check out what is CofeeScript they say it has clear classes and inheritance model) I would really enjoy some more simpler solution.
It would be great if we could declare inside svg pattern a modifiable value alike
<stop offset='100%' style='stop-color:{ExposedPatternValue}; stop-opacity:1;' />
So that when we use pattern we would be able to set it alike
fill: url(#texture-jeans?ExposedPatternValue=#333&OtherParam=0.5);
I wonder is there anything similar/alike in SVG (1.1/1.2/2.0) or we are stuck with static fill pattern models?

Using JavaScript in a background-image svg

I have a SVG (a cross) which changes the color of the lines based on the hash given to the SVG url using JavaScript.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<line x1="0" y1="0" x2="100%" y2="100%" stroke="black" stroke-width="1" />
<line x1="100%" y1="0" x2="0" y2="100%" stroke="black" stroke-width="1" />
<script>
if (window.location.hash) {
document.getElementsByTagName('line')[0].setAttribute('stroke', location.hash);
document.getElementsByTagName('line')[1].setAttribute('stroke', location.hash);
}
</script>
</svg>
This works perfectly fine as an <object> element (<object type="image/svg+xml" data="img/svg/cross.svg#ff0000"></object>), but fails as an img or css background-image.
How can I make this work as a CSS background-image?
Dynamic behavior in SVG that is used as an HTML image is disabled for security reasons. The reason is quite obvious - you can use an SVG image from a different domain and wouldn't really want it to run JavaScript code in the context of your document. So SVG used as an HTML image is essentially always static. There are some more details on http://www.schepers.cc/svg/blendups/embedding.html (thanks #RobertLongson for this link).
There is a work-around in Firefox: if you have inline SVG code (can be hidden) you can use a filter from that SVG code using the filter CSS property. Depending on what you are trying to achieve this can be a rather powerful tool. According to MDN Chrome and Safari should also support this but I'm not certain that they do.
I have found a solution myself that works for me.
I'm also using Sass, and with it I have found a base64 encode plugin.
With it, I can write svg in my CSS which is then encoded to base64. And I can also use variables.
The SASS code now looks like this:
background: url('data:image/svg+xml;base64,'
+ base64Encode('<svg xmlns="http://www.w3.org/2000/svg"><line x1="0" y1="0" x2="100%" y2="100%" stroke="#{$color-line}" stroke-width="1" /><line x1="100%" y1="0" x2="0" y2="100%" stroke="#{$color-line}" stroke-width="1" /></svg>'));
The base64 plugin is found here:
URL- or Base64- encode strings in Compass/SASS

How to trace out letters like a laser

Looking for ideas on how to animate what looks like a laser drawing out a word in a cursive font using SVG. The animation can be done with SMIL or JavaScript I don't care - though I think it would be easier with SMIL.
I am pretty sure if I could just get the letters represented as a path I could figure out how to animate a line from a fixed point to the word path - even if the path is non-continuous.
Any ideas?
EDIT
My demo was very basic, essentially I wrote animate functions for each letter and arranged their timing. Here is the letter X for example:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none">
<rect x="0" y="0" width="100%" height="100%" fill="black"/>
<path id="word" stroke="red" d="M10 10 L40 40 M40 10 L10 40" />
<line x1="10" y1="10" x2="25" y2="50" stroke="blue" stroke-width="0.5">
<animate attributeName="x1" begin="0s" dur="1s" values="10; 40;" />
<animate attributeName="y1" begin="0s" dur="1s" values="10; 40;" />
<animate attributeName="x1" begin="1s" dur="1s" values="40; 10;" />
<animate attributeName="y1" begin="1s" dur="1s" values="10; 40;" />
<set attributeName="visibility" to="hidden" begin="2s" />
</line>
</svg>
I am sure we can all agree that this is not an ideal long term solution... I thought it would be relatively easy to animate one end of a LINE along a path but I am having problems just getting the path...
Extract the paths from the glyphs in question, then apply a dash-array animation as seen in this example on each of the paths.
From a high level perspective, I would think you would want to do something like render the font to a canvas, then use the pixel information to generate the animation sequence. A simple algorithm could just trace from left to right, it would be a good deal harder to figure out a single stroke path, but that is doable as well.
You don't mention any idea of what platform or any time constraints, so its hard to get much closer than that.
One possibility... SVG Fonts are, I understand, stored as a sequence of SVG commands used to draw individual characters. The vector-based nature of drawing in SVG would seem like it would be amenable to 'tracing out' characters in realtime; you might be able to make a conversion utility to pre-convert SVG fonts to simple paths.

Categories

Resources