I'm trying to use external svg file to link particular element from svg stack to "background-image"/"content" of the button element using #fragment identifier.
i.e.:
Button1 - background-image: url(icons.svg#fragment1);
Button2 - background-image: url(icons.svg#fragment2);
External icons.svg consists tons of fragments and pretty big.
Everything works perfectly in all browsers. However for each button that needs only one particular fragment it loads whole svg. At the end, having (let's say) 10 buttons, I've got 10 loaded instances of the whole svg.
An ideal solution (IMHO) would be to use the tag "use" with "inlined" SVG but as far as I know such combination wont work.
Using URI encoded fragments not a solution for my particular case.
What would be the best approach?
You can avoid using a background image if you use the svg as a child of your button element.
To use different icons for different states, you can add multiple icons and only make them visible depending on the state of the button.
button:hover use.hover,
button:active use.active {
display: inline;
}
button use:not(.default),
button:hover use.default,
button:active use.default,
button:active use.hover {
display: none;
}
<!-- inline your icons -->
<svg display="none">
<symbol id="icon1" viewBox="0 0 30 20">
<ellipse cx="15" cy="10" rx="15" ry="10" />
</symbol>
<symbol id="icon2" viewBox="0 0 30 20">
<rect x="5" y="5" width="20" height="10" />
</symbol>
<symbol id="icon3" viewBox="0 0 30 20">
<path d="M7,17H23L15,3z" />
</symbol>
</svg>
<button style="width:80px;height:30px;">
<svg width="100%" height="100%">
<use class="default" href="#icon1" />
<use class="hover" href="#icon2" />
<use class="active" href="#icon3" />
</svg>
</button>
Related
I have a sprite with icons. Everything is good when I'm working on localhost. But when I'm uploading files on hosting, something trimming my #plus after .svg.
Only this icon, other icons showing.
<span #click="increment">
<svg class="icon icon-plus">
<use href="../assets/icons/feather-sprite.svg#plus" />
</svg>
</span>
Your problem will be because of the way that icon (and many others in that set) are designed. It looks like this:
<symbol id="plus" viewBox="0 0 24 24">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</symbol>
The problem is that the icon is designed using simple straight lines. This is quite unusual for icon libraries. Normally they use filled shapes.
By default SVG shapes have a black "fill" and no "stroke". The fill is the area inside a shape, and the stroke is a boundary of a shape. Straight lines have no inside, so they have no fill to draw. And if a colour and size are not specified for the stroke, it won't be rendered either. That's why the icon is not showing up.
To fix that you need to specify a stroke and stroke-width for the icons that are like this.
.icon {
width: 100;
}
.icon-plus {
stroke: blue;
stroke-width: 4;
}
<svg class="icon icon-plus">
<symbol id="plus" viewBox="0 0 24 24">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</symbol>
<use href="#plus" />
</svg>
I'm trying to construct a buttons collection for a website. Since the buttons share several properties (colours and effects) I've decided to use SVG. In order to reduce the HTTP requests, I tried to nest several elements and filters inside the same SVG file. The idea is instantiate these elements to construct all the buttons, toggle switches, etc by simply using the <use> tag.
After reading great articles like this, this, and this I'm still not being able to do such thing. I found myself stuck. So I decided to simplify the problem as much as I could within the snippet down bellow.
<style>
svg,
img,
object {
width: 50px;
height: 50px;
border: 1px solid black;
}
h3 {
margin-top: 50px;
}
</style>
<h1>Swimming Veggies. The SVG club that's driving me nuts.</h1>
<h3>This is an inline SVG.</h3>
<svg viewBox="0 0 50 50">
<g id="pool">
<rect width="50" height="50" fill="aqua" />
</g>
<symbol id="tomato">
<circle cx="35" cy="35" r="10" fill="tomato" />
</symbol>
<g id="orange">
<circle cx="15" cy="15" r="12" fill="orange" />
</g>
</svg>
<p>Things work as expected here. The <g> elements #pool and #orange show up while the <symbol id="tomato"> doesn't. Great!</p>
<h3>This is a SVG using <use> to referencing elements from the inline SVG above.</h3>
<svg style="border: 1px solid black;" width="50" height="50">
<use xlink:href="#pool"></use>
<use xlink:href="#tomato"></use>
</svg>
<p>Note that the <symbol id="tomato"> element shows up here like the g elemet "pool". The "orange" element is NOT being <use>d. Works fine. Expected behaviour.</p>
<h3>This is a SVG being put as an IMG.</h3>
<img src="club.svg">
<p>Works fine to me and the absence of the <symbol id="tomato"> is the expeted behaviour. I don't want to use SVG this way though.</p>
<p> Unfortunately, IMGUR didn't accept SVGs and I didn't find an image sharing platform that accepts SVG files. The "club.svg" file has the same content as the commented code at the end of this HTML snippet. The SVG file is located ON THE SAME FOLDER of the HTML file. Sorry for the inconvenience :(</p>
<h3>This is a SVG being put as an OBJECT</h3>
<object type="image/svg+xml" data="club.svg"></object>
<p>This works as expected.</p>
<p>Again, the "club.svg" file content is shown as a comment inside and at the end of this HTML snippet.</p>
<h3>An unsuccessful attempt to extract elements from an OBJECT SVG </h3>
<object type="image/svg+xml" data="club.svg#tomato"></object>
<p>The browser is ignoring the "#tomato" after the file name. Is it possible to extract/manipulate elements inside the SVG file this way? If so, how?</p>
<h3>This is a SVG being put as an external reference.</h3>
<svg>
<use xlink:href="club.svg#pool"></use>
<use xlink:href="club.svg#tomato"></use>
<use xlink:href="club.svg#orange"></use>
</svg>
<p>That would be my preferable way to clone/manipulate elements from an external source (in this case, "club.svg"). Unfortunately, it's not working for me. It's not clonning the element. What am I doing wrong?</p>
<h3>Here goes the club.svg content (it's commented inside the snippet).</h3>
<!--
Down bellow is the code inside the club.svg file:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50">
<g id="pool">
<rect width="50" height="50" fill="aqua" />
</g>
<symbol id="tomato">
<circle cx="35" cy="35" r="10" fill="tomato" />
</symbol>
<g id="orange">
<circle cx="15" cy="15" r="12" fill="orange" />
</g>
</svg>
-->
By the way... I tried to open up the file on Brave, Opera, Chrome and Safari. All of them show the same results.
Any help will be very much appreciated.
In my Angular application, the user can insert an image into an SVG shape and apply a transform to the image interactively. By default, the image fills the shape.
It works well, except for one very annoying problem which occurs when:
The shape has no stroke
One side of the image is much darker than the opposite side
When these conditions are met, a very thin line appears on the side of the shape where the image is lighter. For example, if the bottom of the image is much darker than the top, the thin line will appear at the top of the shape. That line comes from the opposite side of the image, as if the pattern is trying to repeat. I know that there is nothing like a no-repeat attribute for image patterns, so I set the following pattern attributes to avoid repeating:
<pattern patternUnits="objectBoundingBox" x="0" y="0" width="1" height="1" ...>
It works but not perfectly. The thin line is not always visible on the screen, even when zooming, but it is most of the time. It causes us problems since these SVG shapes are part of book pages, and the thin line often ends up being visible in the final print.
Is there a good way to completely avoid pattern repeating? I tried extending the pattern so that it overflows the shape, but the extra size has to be somewhat significant to be effective:
<pattern x="-0.01" y="-0.01" width="1.02" height="1.02" ...>
When I run the code snippet below in my Chrome browser, the unwanted thin blue line is visible at the top of the shape. The problem is not specific to Chrome however; I see it on Firefox as well.
<div style="padding: 20px; width: 180px; height: 150px; background-color: yellow;">
<svg height="100%" width="100%" x="0%" y="0%" viewBox="0 0 1600 1200">
<defs>
<pattern id="pattern1" patternUnits="objectBoundingBox"
preserveAspectRatio="xMidYMid slice"
width="1" height="1" x="0" y="0" viewBox="0 0 1600 1200">
<image width="1600" height="1200" x="0" y="0"
xlink:href="https://i.ibb.co/vZ9spGH/1600x1200-1.png"></image>
</pattern>
</defs>
<rect fill="url('#pattern1')" height="100%" width="100%" x="0%" y="0%"></rect>
</svg>
</div>
Shape fills are fairly straightforward using a filter:
<div style="padding: 20px; width: 180px; height: 150px; background-color: yellow;">
<svg height="100%" width="100%" x="0%" y="0%" viewBox="0 0 1600 1200">
<defs>
<filter id="simple-image-fill" primitiveUnits="userSpaceOnUse">
<feImage width="1600" height="1200" x="0" y="0" preserveAspectRatio="xMidYMid slice"
xlink:href="https://i.ibb.co/vZ9spGH/1600x1200-1.png" result="image-res"/>
<feComposite operator="in" in2="SourceGraphic" in="image-res"/>
</filter>
</defs>
<rect filter="url(#simple-image-fill)" height="100%" width="100%" x="0%" y="0%"/>
</svg>
</div>
The text element inside the mask works as expected, but the div inside the foreignObject element is ignored.
<svg width='500' height='300' >
<defs>
<mask id='m'>
<text fill='white' x='0' y='35' font-size='35'>text element</text>
<foreignObject width='200' height='200' >
<div style='color:white;'>div element</div>
</foreignObject>
</mask>
</defs>
<rect width='200' height='200' mask='url(#m)' />
</svg>
Does anyone know how to get the foreignObject to work inside a mask? Is it even possible?
I need to make text automatically stretch in both dimensions, to fill a container. It will distort.
This shows the the container space in red
This shows what a long name would normally resize to put in that space and maintaining aspect ratio
.
This shows what my client wants to happen
.
I would prefer to use SVG but I will work with what works.
I have searched for a solution to the best of my abilities but all seem to either refer to maintaining aspect ratio or stretching text when the page or viewbox changes dimensions.
That's quite a broad question, but yes you can do it with svg, I'll let you implement it though since you didn't provided anything to chew on.
The key point is to set your svg's preserveAspectRatio to "none":
svg{
height: 100vh;
width: 50vw;
}
body{
margin:0;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 40" preserveAspectRatio="none">
<text x="0" y="35" font-family="Verdana" font-size="35">
Hello, out there
</text>
</svg>
</div>
If your text is already part of an SVG (as it appears in your example), you will probably need to use a nested <svg> element.
<svg width="400" height="400">
<rect width="400" height="400" fill="rebeccapurple"/>
<!-- rect representing area that our text has to squeeze into -->
<rect x="20" y="50" width="200" height="50" fill="white"/>
<!-- x y width height match above rect -->
<!-- viewBox values need to match text bounds -->
<svg x="20" y="50" width="200" height="50"
viewBox="0 8 244 28" preserveAspectRatio="none">
<text x="0" y="35" font-family="Verdana" font-size="35">
HELLO THERE
</text>
</svg>
</svg>
The hardest part is workoing out the correct values for viewBox. It needs to match the bounds of the (normal unsqueezed) text.