We are using Svelte/Sapper front-end with client-side routing. When user is navigating between different pages, SVGs on the page sometimes glitch-out. Example:
(SVG source in the end of the question)
This glitching is caused by using HTML <base> tag and referencing linear gradient effect with CSS url() function. This results in invalid URL (base + element id) inside SVG and effect will not be applied.
Related issues to my problem:
SVG Showing up as a black box in Safari (Mobile / Desktop)
Using base tag on a page that contains SVG marker elements fails to render marker
iOS - Linear Gradients showing as Black
SVGs turn to black in Safari during transformations, akin to a bug with tag
SVG path fill rendering can break after window.history.pushState
SVG url() doesn't work under in Safari and Firefox
My question is then: Can I reference effect such as <linearGradient> without relaying on url() function to avoid this issue entirely?
Hacky solution
I solved this issue by listening to end of animation event and then adding and in setTimeout removing display: inline-block; on <svg> element. This is forcing browser to re-render it and fixes glitch after each animation. This is obviously hacky solution with lot of technical debt. I will post different answer with solution if I find different/better way of solving this.
SVG icon source:
<svg xmlns="http://www.w3.org/2000/svg" style="fill: currentColor;" class="w-8 h-8" viewBox="0 0 24 24">
<defs>
<path id="a" d="M12 10.3L2.4 1v22l9.6-9.2 1.8-1.8 5.4-5.2v10.5l-3.7-3.5-1.8 1.7 7.9 7.6V1L12 10.3zm-7.1 6.9V6.8l5.4 5.2-5.4 5.2z">
</path>
</defs>
<clipPath id="b">
<use href="#a" overflow="visible"></use>
</clipPath>
<g clip-path="url(#b)">
<linearGradient id="c" gradientUnits="userSpaceOnUse" x1="-732.18" y1="294.75" x2="-731.55" y2="294.75" gradientTransform="scale(47.46 -47.46) rotate(45 -9.66 1030.9)">
<stop offset="0" stop-color="#201351"></stop>
<stop offset=".25" stop-color="#201351"></stop>
<stop offset=".39" stop-color="#620f3c"></stop>
<stop offset=".53" stop-color="#990b2a"></stop>
<stop offset=".65" stop-color="#c1081e"></stop>
<stop offset=".74" stop-color="#d90716"></stop>
<stop offset=".8" stop-color="#e20613"></stop>
<stop offset="1" stop-color="#e20613"></stop>
</linearGradient>
<path fill="url(#c)" d="M-8.6 12L12-8.6 32.6 12 12 32.6z"></path>
</g>
</svg>
The cleanest solution would be to reference all icons from one SVG sprite file at a static address.
For example https://example.com/assets/sprite.svg would quote the icon markup in <symbol> elements:
<svg xmlns="http://www.w3.org/2000/svg">
<!-- use the viewBox from your icon <svg> element -->
<symbol id="unique-icon" viewBox="0 0 24 24">
<!-- quote everything inside your icon <svg> element -->
</symbol>
<!-- more icons -->
</svg>
and icon usage would look like this:
<svg><use xlink:href="https://example.com/assets/sprite.svg#unique-icon></svg>`.
Note you can still apply styles to that <use> element, which will be inherited by the icon markup inside the shadow DOM.
Related
I have an SVG animation which runs well on every browser. But, when zoomed in on Safari, the animation breaks. It looks like the animation is bleeding through the right and bottom edges.
And, this only happens when the SVG is in an img tag. If the SVG is used inline, there is no issue as such.
When the page is zoomed in to 125% on macOS Safari, you'll get,
At first, it looked like an issue with my screen or with my personal preferences set in my browser. But, this is a real issue that I could reproduce on other machines too and also with other animated SVGs. For example, open up https://loading.io/spinner or http://samherbert.net/svg-loaders/ in macOS Safari with a zoom level other than 100% as you'll see the same behavior.
Snippet (save this SVG as a .svg image and use it in an img tag to reproduce this behavior)
<svg width="36" height="36" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25" />
<path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z">
<animateTransform
attributeName="transform"
type="rotate"
dur="0.75s"
values="0 12 12;360 12 12"
repeatCount="indefinite"
/>
</path>
</svg>
I could reproduce this rendering bug on Otter Browser on windows (also webkit based) and found a kind of workaround:
Once the html body contains an inlined svg element with an <animateTransform> definition – SMIL animations in <img> tags will be rendered correctly.
In my tests you can append a hidden pseudo animation (not containing any animated element) like this:
<svg style="position:absolute; width:0; height:0; overflow:hidden;" >
<animateTransform
repeatCount="indefinite"
dur="1s"
attributeName="transform" />
</svg>
So it doesn't need to be the exact animation as used in the svg file.
Example: add animation fix via js:
function addAnimateFix() {
let svgFix =
`<svg style="position:absolute; width:0; height:0; overflow:hidden;" >
<animateTransform
repeatCount="indefinite"
dur="1s"
attributeName="transform" />
</svg>`;
document.body.insertAdjacentHTML('beforeend', svgFix)
}
<p><button type="button" onclick="addAnimateFix()">addAnimateFix</button></p>
<img height="200px" width="200px" src="https://svgshare.com/i/ia5.svg" alt="">
<img height="200px" width="200px" src="https://svgshare.com/i/i_v.svg" alt="">
Alternative workaround
background-image is apparently not affected by this bug.
So setting a background image on your image or wrapping parent element (like <figure>) could be a viable option.
I have been trying to implement the following requirements.
1. SVG path to have multiple colors on different sections (eg - red color the curves and rest of it, in black color)
2. Only allow mouse event- click on those colored areas (curves)
I have tried with plain javascript and snap.svg
Plain html & Javascript
[Codepen using HTML & Js][1]
SNAP.svg
[Codepen using SNAP.svg][2]
[1]: https://codepen.io/sanathko1234/pen/vvMQQZ
[2]: https://codepen.io/sanathko1234/pen/OrGoRa
How can this be achieved?
One solution would be to use the path twice: first the green one and next the one using stroke-dasharray. The dashes are only over the curves. If you don't like the position or the length of the dashes change them to what you need. The gaps are not sensitive to mouse events, only the dashes are.
In css I've added #gold:hover{cursor:pointer} so that you can see that only the dashes are sensitive to the mouse.
I hope it helps.
svg{border:1px solid}
use{fill:none;stroke-width:18;}
#gold:hover{cursor:pointer}
<svg viewBox="-10 50 580 360" width="580" height="360" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="svg_1" d="m555,272c1,0.76736 4,85.76736 -71,97.76736c-75,12 -387,-39 -388,-39.76736c0,-0.23264 -29,-1.23264 -45,-21.23264l-42,-124.76736c-3,-11.23264 -3,-21.23264 3,-26.23264c6,-5 46,-67 69,-69.76736l474,184z" />
</defs>
<g>
<title>background</title>
<rect fill="#fff" id="canvas_background" height="360" width="580" x="-10" y="50"/>
</g>
<g>
<title>Layer 1</title>
<use xlink:href="#svg_1" stroke="green" />
<use xlink:href="#svg_1" stroke="gold" stroke-dasharray ="130 370 110 60 90 40 90 400 52.45" id="gold" pointer-events="stroke" />
</g>
</svg>
Observation: the sum of the dashes and the gaps is 1342.45 which is also the total path length.
I am trying to render circles which have a text inside, that runs along a given path.
The markup d3 produces looks fine, but Chrome is not showing the texts.
Upon inspection it says text elements have 0 width and 0 height.
This is sample markup including only two circles:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 950 600">
<g>
<g transform="translate(334.14792673070184,58.96385042366173)">
<defs>
<path id="path-1" d="m5,50 a45,45 0 0 0 90,0"></path>
</defs>
<circle class="circle" fill="#ccc" cx="50" cy="50" r="50"></circle>
<text fill="#333" font-size="15px">
<textpath xlink:href="#path-1" start-offset="0%">123456</textpath>
</text>
<use xlink:href="#path-1" fill="#666" opacity="0.1"></use>
</g>
</g>
<g transform="translate(374.66047394649974,371.7948729806046)">
<defs>
<path id="path-2" d="m5,50 a45,45 0 0 0 90,0"></path>
</defs>
<circle class="circle" fill="#ccc" cx="50" cy="50" r="50"></circle>
<text fill="#333" font-size="15px">
<textpath xlink:href="#path-2" start-offset="0%">123456</textpath>
</text>
<use xlink:href="#path-2" fill="#666" opacity="0.1"></use>
</g>
</svg>
When I inspect the markup in Chrome console and click "Edit as HTML" on the SVG element, make a random change, save & exit - the SVG suddenly renders correctly.
The exact same thing happens in Firefox and Opera.
Copy pasting the generated markup into a jsfiddle renders everything as expected.
I have tried pulling the < defs > tags out of each individual group into a single global < defs > but it did not solve the problem.
I have also looked at user-agent-stylesheet and other CSS rules that might interfere with rendering.
Is this a problem with how the SVG tag is included and/or the container element's width/height properties? I have been trying different things to fix this for a couple of hours now...
Here is the full SVG markup http://pastebin.com/J2Lz8p23
Here are the relevant parts in my code http://pastebin.com/Bym8kJVN
I'm making a web interactive test for musicians, and got stuck up on, well, interactiveness.
Users are supposed to move notes on a stave and adjust them.
SVG pathes are rendered as notes, with a separate parent div container for each note. Parent div is dragged on to the stave, and then can be resized ( I'm using jQuery UI for this, with option "handles" set to "e", so div can be resized only to the right). After this point I can not figure out how to correctly resize the SVG note inside it, because what needed to be resized, is not the whole SVG, but just one part.
SVGs look like this:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="32" viewBox="0 0 24 32" class="svgnote" preserveAspectRatio="none">
<path d="M0 23.703q0-0.922 0.672-1.797 0.313-0.344 0.766-0.711t1-0.727q0.531-0.25 1.031-0.359t0.953-0.109q0.953 0 1.688 0.531v-17.344h13.578v18.797q0 0.953-0.641 1.781-0.641 0.875-1.703 1.352t-2.047 0.477q-0.859 0-1.563-0.516t-0.703-1.375q0-0.984 0.703-1.797 0.609-0.797 1.719-1.438 0.578-0.25 1.055-0.359t0.93-0.109q0.969 0 1.703 0.531v-15.188h-12.484v16.641q0 0.953-0.641 1.781-0.641 0.875-1.695 1.352t-2.055 0.477q-0.891 0-1.594-0.516-0.672-0.516-0.672-1.375z"></path>
</svg>
Picture example
I added a picture, where above red figure
it's an initial state of a note;
upper red arrow is pointing out on a line, which is the only part of svg that needs to stretch out to the right.
lower red arrows aside -- the note should (somehow?) be rotated up and down, to make 3rd state possible
is what final state of note I'm looking for.
What I have done:
except excessive googling, not much. I tried to set "width" of SVG image to "100%" of the parent div, and add preserveAspectRatio="none", but all I got is ugly stretched note.
As for rotation I do not have even a single idea from what should I start.
I'd be eternally grateful if someone point out for what should I google, or maybe a library should I use. I'm sensing a solution to this is close somewhere, I'm just missing it because of my very limited experience in the field.
Thank you all.
Here's a 10mins hack. Connecting bar needs more work but the gist is use a collection of <symbol>for your musical notation and a better path (eg: polygon?) for the connecting bar.
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="note-1" viewBox="0 0 313 340">
<g xmlns="http://www.w3.org/2000/svg" transform="matrix(-1,0,0,-1,313,340)" id="g11029">
<path d="M 303.13715,299.65106 C 299.74131,301.47103 297.93187,304.76561 299.04493,307.24402 C 300.23219,309.88766 304.31194,310.63374 308.15151,308.90939 C 311.99107,307.18503 314.14367,303.63999 312.95641,300.99636 C 311.76914,298.35272 307.6894,297.60664 303.84983,299.33099 C 303.60986,299.43876 303.36355,299.52973 303.13715,299.65106 z " style="opacity:0.9;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.2;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" id="path11031"/>
<path d="M 299.50465,305.98445 L 299.50465,339.57202" style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="path11033"/>
</g>
</symbol>
<g class="first" transform="translate(0,20)">
<use xlink:href="#note-1" />
</g>
<g class="second" transform="translate(30,10)">
<use xlink:href="#note-1" />
</g>
<path stroke-width="4" stroke="black" d="M87,20 L117,10"></path>
</svg>
I have these icons and I'd like to procedurally add a drop shadow (basically, something, anything) to them on hover so they don't look so hokey.
They are SVG's so in theory I can prepend something like this:
<filter id="f1" x="0" y="0" width="200%" height="200%">
<feOffset result="offOut" in="SourceAlpha" dx="20" dy="20" />
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="10" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
and some javascript magic to apply it on mouse over. This could possibly save ages of design work.
Problem is, the svg's are presented as <a style='background-image:url(icon.svg)' />.
Is there a way to get into the SVG element?
No, it's not directly possible. A workaround if you need this would be to use inline svg in html, or reference the svg files with either <object>, <iframe> or <embed>.
An example of using inline svg and a filter effect on hover here. The svg part looks like this essentially:
<svg width="400" height="400" viewBox="-2 -2 36 32">
<defs>
<style>
#stack polygon:hover { filter: url(#glow); }
</style>
<filter id="glow">
<feMorphology radius="0.7"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix"
values="0 0 0 0 0
0 0 0 0.9 0
0 0 0 0.9 0
0 0 0 1 0"/>
</filter>
</defs>
<g id="stack" class="icon" fill="#850508">
<polygon points="0,20 16,24 32,20 32,24 16,28 0,24"/>
<polygon points="0,12 16,16 32,12 32,16 16,20 0,16"/>
<polygon points="0,4 16,0 32,4 32,8 16,12 0,8"/>
</g>
</svg>
If you're using SVG as an image then you can't get to the image's DOM and manipulate it via javascript.
While you could load them using XMLHTTPRequest and then insert them into the main document as inline data using the DOMParser object, this exposes you to the security issues that the browsers are trying to protect you from by locking down image access i.e. the image could change and you may be loading arbitrary javascript into your page.
What would seem simplest and safest to me is if you just alter the image files directly using an editor and add the filter into them then use the modified images.
What if you use jquery addclass and simply link to another svg that has your changes?
You can also create an svg element with html code (right click on file --> view with notepad) and you will have the code there -- there are online converters that can make all neccessary tweaks and then you can enter the svg code with javascript
I don't know how to do it in the way you show... as a background image... but if you can load the svg file inside a div as in this example you can you can use my importer (a modified version of others importers founded on github) that it's here: http://www.dariomac.com/Document/Raphael-Utils. You can also see this storify where I describe all the steps followed by me to import SVG directly from file.