Making a looping drawSVG animation smoother - svg

I'm a novice with GSAP and am experimenting with the plugin. I want to create an effect where a rectangles border is drawn by a small path in a continuous loop. I've mostly achieved this, but the end of the animation is pretty choppy when it resets.
Here's a link to the codepen I'm working on.
How can I avoid that abrupt ending to the animation?
I've tried setting the animation to drawSVG: "100% 115%" and that didn't work.
I haven't tried changing the easing yet as I don't think that would fix it.
Thanks for helping!

I believe this tutorial is exactly what you're trying to achieve Codepen
code required for codepen link
I forked your pen so you can see it in action
Codepen
Hope this is what you where looking for

The previous answer didn't sit right with me as I felt like something more elegant could be done. I found a simple solution where GSAP isn't used, but CSS animations are.
The result is a smooth animation using only one element.
GSAP manipulates CSS animations anyway so this is a simplification of the original problem.
https://codepen.io/strigiforme/pen/zLbYWm
<svg viewbox = "0 0 200 200">
<rect id = "rect" x = "85" y = "20" width="30" height="30" stroke = "red" stroke-width = "1.5" fill="none" stroke-dasharray="30 90" stroke-dashoffset = "240"></rect>
body {
background-color: #222;
overflow: hidden;
}
#rect{
animation: draw 5s linear infinite;
}
#keyframes draw{
14%{
stroke: orange;
}
28%{
stroke: yellow;
}
42%{
stroke: green;
}
56%{
stroke: blue;
}
70%{
stroke: indigo;
}
84%{
stroke: violet;
}
98%{
stroke: red;
}
to{
stroke-dashoffset: 0;
}
}

Related

change color of button with text and svg without using inline svg [duplicate]

I want to use this technique and change the SVG color, but so far I haven't been able to do so. I use this in the CSS, but my image is always black, no matter what.
My code:
.change-my-color {
fill: green;
}
<svg>
<image class="change-my-color" xlink:href="https://svgur.com/i/AFM.svg" width="96" height="96" src="ppngfallback.png" />
</svg>
2020 answer
CSS Filter works on all current browsers
To change any SVGs color
Add the SVG image using an <img> tag.
<img src="dotted-arrow.svg" class="filter-green"/>
To filter to a specific color, use the following Codepen (click here to open the codepen) to convert a hexadecimal color code to a CSS filter:
For example, output for #00EE00 is
filter: invert(42%) sepia(93%) saturate(1352%) hue-rotate(87deg) brightness(119%) contrast(119%);
Add the CSS filter into this class.
.filter-green{
filter: invert(48%) sepia(79%) saturate(2476%) hue-rotate(86deg) brightness(118%) contrast(119%);
}
To change the color of any SVG, you can directly change the SVG code by opening the SVG file in any text editor. The code may look like the below code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="500px" height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
<g>
<path d="M114.26,436.584L99.023,483h301.953l-15.237-46.416H114.26z M161.629,474.404h-49.592l9.594-29.225h69.223
C181.113,454.921,171.371,464.663,161.629,474.404z"/>
/* Some more code goes on */
</g>
</svg>
You can observe that there are some XML tags like path, circle, polygon, etc.. There you can add your own color with help of the style attribute. Look at the below example
<path fill="#AB7C94" d="M114.26,436.584L99.023,483h301.953l-15.237-46.416H114.26z M161.629,474.404h-49.592l9.594-29.225h69.223
C181.113,454.921,171.371,464.663,161.629,474.404z"/>
Add the style attribute to all the tags so that you can get your SVG of your required color.
As per Daniel's comment, we can use fill attribute directly instead of fill element inside style attribute.
You can't change the color of an image that way. If you load SVG as an image, you can't change how it is displayed using CSS or JavaScript in the browser.
If you want to change your SVG image, you have to load it using <object>, <iframe> or using <svg> inline.
If you want to use the techniques in the page, you need the Modernizr library, where you can check for SVG support and conditionally display or not a fallback image. You can then inline your SVG and apply the styles you need.
See:
#time-3-icon {
fill: green;
}
.my-svg-alternate {
display: none;
}
.no-svg .my-svg-alternate {
display: block;
width: 100px;
height: 100px;
background-image: url(image.png);
}
<svg width="96px" height="96px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path id="time-3-icon" d="M256,50C142.229,50,50,142.229,50,256c0,113.77,92.229,206,206,206c113.77,0,206-92.23,206-206
C462,142.229,369.77,50,256,50z M256,417c-88.977,0-161-72.008-161-161c0-88.979,72.008-161,161-161c88.977,0,161,72.007,161,161
C417,344.977,344.992,417,256,417z M382.816,265.785c1.711,0.297,2.961,1.781,2.961,3.518v0.093c0,1.72-1.223,3.188-2.914,3.505
c-37.093,6.938-124.97,21.35-134.613,21.35c-13.808,0-25-11.192-25-25c0-9.832,14.79-104.675,21.618-143.081
c0.274-1.542,1.615-2.669,3.181-2.669h0.008c1.709,0,3.164,1.243,3.431,2.932l18.933,119.904L382.816,265.785z"/>
</svg>
<image class="my-svg-alternate" width="96" height="96" src="ppngfallback.png" />
You can inline your SVG. Tag your fallback image with a class name (my-svg-alternate):
<svg width="96px" height="96px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path id="time-3-icon" .../>
</svg>
<image class="my-svg-alternate" width="96" height="96" src="ppngfallback.png" />
And in CSS use the no-svg class from Modernizr (CDN: http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.7.2.js ) to check for SVG support. If there isn't any SVG support, the SVG block will be ignored and the image will be displayed, otherwise the image will be removed from the DOM tree (display: none):
.my-svg-alternate {
display: none;
}
.no-svg .my-svg-alternate {
display: block;
width: 100px;
height: 100px;
background-image: url(image.png);
}
Then you can change the color of your inlined element:
#time-3-icon {
fill: green;
}
If you want to change the color dynamically:
Open the SVG in a code editor
Add or rewrite the attribute of fill of every path to fill="currentColor"
Now, that svg will take the color of your font color, so you can do something like:
svg {
color : "red";
}
Only SVG with path information. You can't do that to the image... as the path you can change stroke and fill information and you are done. like Adobe Illustrator
So, via CSS you can overwrite the path fill value:
path { fill: orange; }
But if you want a more flexible way as you want to change it with a text when having some hovering effect going on, use:
path { fill: currentColor; }
body {
background: #ddd;
text-align: center;
padding-top: 2em;
}
.parent {
width: 320px;
height: 50px;
display: block;
transition: all 0.3s;
cursor: pointer;
padding: 12px;
box-sizing: border-box;
}
/*** desired colors for children ***/
.parent{
color: #000;
background: #def;
}
.parent:hover{
color: #fff;
background: #85c1fc;
}
.parent span{
font-size: 18px;
margin-right: 8px;
font-weight: bold;
font-family: 'Helvetica';
line-height: 26px;
vertical-align: top;
}
.parent svg{
max-height: 26px;
width: auto;
display: inline;
}
/**** magic trick *****/
.parent svg path{
fill: currentcolor;
}
<div class='parent'>
<span>TEXT WITH SVG</span>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128" viewBox="0 0 32 32">
<path d="M30.148 5.588c-2.934-3.42-7.288-5.588-12.148-5.588-8.837 0-16 7.163-16 16s7.163 16 16 16c4.86 0 9.213-2.167 12.148-5.588l-10.148-10.412 10.148-10.412zM22 3.769c1.232 0 2.231 0.999 2.231 2.231s-0.999 2.231-2.231 2.231-2.231-0.999-2.231-2.231c0-1.232 0.999-2.231 2.231-2.231z"></path>
</svg>
</div>
I added a test page - to color SVG via Filter settings:
For example,
filter: invert(0.5) sepia(1) saturate(5) hue-rotate(175deg)
Upload & Color your SVG - Jsfiddle
I took the idea from: Swapping Fill Color on Image Tag SVGs
Solution 1 - Edit SVG to point to the currentColor
<svg>... fill: currentColor stroke: currentColor ...</svg>
Then you can control the color of the stroke and the fill from your CSS content:
svg {
color: blue; /* Or any color of your choice. */
}
Pros and cons:
Simple and uses conventional supported CSS.
Suitable if:
You control the SVG
SVG can be included inline in the HTML.
Solution 2 - CSS mask property
<i class="icon"></i>
.icon {
-webkit-mask-size: cover;
mask-size: cover;
-webkit-mask-image: url(https://url.of.svg/....svg);
mask-image: url(https://url.of.svg/....svg);
background-color: blue; /* Or any color of your choice. */
width: 20px;
height: 20px;
}
}
Pros and cons
Relatively easy to use
Browser support for the mask CSS property is partial.
Suitable if:
SVG is external, and included via URL
Meant to be used on modern known browsers.
Solution 3 - CSS Filter property - static color
If the color is known in advance, you can use https://codepen.io/sosuke/pen/Pjoqqp to find the filter needed to change your SVG to the desired color. For example, to convert the svg to #00f:
<img src="https://url.of.svg/....svg" class="icon">
.icon {
filter: invert(8%) sepia(100%) saturate(6481%) hue-rotate(246deg) brightness(102%) contrast(143%);
}
If your original color isn't black, prefix the list of filters with brightness(0) saturate(100%) to convert it first to black.
Pros and cons:
There might be a small, nonsignificant difference between the result and the desired color.
Suitable if:
Desired color is known in advance.
External image
SVG mask on a box element with a background color will result:
body{ overflow:hidden; }
.icon {
--size: 70px;
display: inline-block;
width: var(--size);
height: var(--size);
transition: .12s;
-webkit-mask-size: cover;
mask-size: cover;
}
.icon-bike {
background: black;
animation: 4s frames infinite linear;
-webkit-mask-image: url(https://image.flaticon.com/icons/svg/89/89139.svg);
mask-image: url(https://image.flaticon.com/icons/svg/89/89139.svg);
}
#keyframes frames {
0% { transform:translatex(100vw) }
25% { background: red; }
75% { background: lime; }
100% { transform:translatex(-100%) }
}
<i class="icon icon-bike" style="--size:150px"></i>
Note - SVG masks are not supported in Internet Explorer browsers
The easiest way would be to create a font out of the SVG using a service like https://icomoon.io/app/#/select or such. Upload your SVG, click "generate font", include font files and CSS content into your side and just use and style it like any other text. I always use it like this because it makes styling much easier.
But as mentioned in the article commented by #CodeMouse92, icon fonts mess up screen readers (and are possibly bad for SEO). So rather stick to the SVGs.
You can try to color it with this css filter hack:
.colorize-pink {
filter: brightness(0.5) sepia(1) hue-rotate(-70deg) saturate(5);
}
.colorize-navy {
filter: brightness(0.2) sepia(1) hue-rotate(180deg) saturate(5);
}
.colorize-blue {
filter: brightness(0.5) sepia(1) hue-rotate(140deg) saturate(6);
}
To simply change the color of the SVG file:
Go to the SVG file and under styles, mention the color in fill:
<style>.cls-1{fill: #FFFFFF;}</style>
Target the path within the 'svg' tag:
<svg>
<path>....
</svg>
You can do it inline, like:
<path fill="#ccc">
Or
svg{
path{
fill: #ccc
To change the color of an SVG element, I have found out a way while inspecting the Google search box search icon below:
.search_icon {
color: red;
fill: currentColor;
display: inline-block;
width: 100px;
height: 100px;
}
<span class="search_icon">
<svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></svg>
</span>
I have used a span element with "display:inline-block", height, width and setting a particular style "color: red; fill: currentColor;" to that span tag which is inherited by the child svg element.
You can change SVG coloring with CSS if you use some tricks.
I wrote a small script for that.
go through a list of elements which do have an SVG image
load the SVG file as XML
fetch only the SVG part
change color of path
replace src with the modified SVG image as an inline image
$('img.svg-changeable').each(function () {
var $e = $(this);
var imgURL = $e.prop('src');
$.get(imgURL, function (data) {
// Get the SVG tag, ignore the rest
var $svg = $(data).find('svg');
// Change the color
$svg.find('path').attr('fill', '#000');
$e.prop('src', "data:image/svg+xml;base64," + window.btoa($svg.prop('outerHTML')));
});
});
The code above might not be working correctly. I've implemented this for elements with an SVG background image which works nearly similar to this.
But anyway, you have to modify this script to fit your case.
Method 1
The easy and effect way:
Open your .svg file with any text editor
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 477.526 477.526" style="enable-background:new 0 0 477.526 477.526;
fill: rgb(109, 248, 248);" xml:space="preserve">
<svg />
Give an style attribute and fill that with color.
Another way
Fill with color in your shape. Here i have rect shape fill="white".
<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg">
<g>
<title>background</title>
<rect fill="#fff" id="canvas_background" height="602" width="802" y="-1"
x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%"
id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%"
width="100%"/>
</g>
</g>
</svg>
2022 Web Component <load-file> answer
This (8 line) native Web Component loads external content, and injects it into the DOM.
It is explained and documented in a DEV blog post: <load-file> Web Component.
Full source code:
customElements.define("load-file", class extends HTMLElement {
// declare default connectedCallback as sync so await can be used
async connectedCallback(
// call connectedCallback with parameter to *replace* SVG (of <load-file> persists)
src = this.getAttribute("src"),
// attach a shadowRoot if none exists (prevents displaying error when moving Nodes)
// declare as parameter to save 4 Bytes: 'let '
shadowRoot = this.shadowRoot || this.attachShadow({mode:"open"})
) {
// load SVG file from src="" async, parse to text, add to shadowRoot.innerHTML
shadowRoot.innerHTML = await (await fetch(src)).text()
// append optional <tag [shadowRoot]> Elements from inside <load-svg> after parsed <svg>
shadowRoot.append(...this.querySelectorAll("[shadowRoot]"))
// if "replaceWith" attribute
// then replace <load-svg> with loaded content <load-svg>
// childNodes instead of children to include #textNodes also
this.hasAttribute("replaceWith") && this.replaceWith(...shadowRoot.childNodes)
}
})
<load-file src="//load-file.github.io/heart.svg">
<!-- elements inside load-file are MOVED to shadowDOM -->
<style shadowRoot>
svg {
height: 180px; /* Stack Overflow subwindow height */
}
path:nth-child(2n+2) {
fill: GREEN; /* shadowDOM style does NOT style global DOM */
}
</style>
</load-file>
If the same SVG must be used multiple times with different colors, define the set of paths within a hidden SVG which serves as the master copy. Then place new instances which refer to the master path with their individual fills.
Note: This approach only works with inline <svg> tags. It will not work with <img> tags loading .svg files.
:root {
fill: gray;
}
.hidden {
display: none;
}
svg {
width: 1em;
height: 1em;
}
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" class="hidden">
<path id="s_fave" d="m379 21c-57 0-104 53-123 78-19-25-66-78-123-78-74 0-133 68-133 151 0 45 18 88 49 116 0.5 0.8 1 2 2 2l197 197c2 2 5 3 8 3s5-1 8-3l206-206c2-2 3-3 5-5 0.8-0.8 1-2 2-3 23-28 35-64 35-102 0-83-60-151-133-151z"/>
<path id="s_star" d="m511 196c-3-10-13-18-23-19l-148-13-58-137c-4-10-14-17-25-17-11 0-21 6-25 17l-58 137-148 13c-11 1-20 8-23 19-3 10-0.3 22 8 29l112 98-33 145c-2 11 2 22 11 28 5 3 10 5 16 5 5 0 10-1 14-4l127-76 127 76c9 6 21 5 30-1 9-6 13-17 11-28l-33-145 112-98c8-7 11-19 8-29z"/>
</svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_fave"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_star"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_fave" fill="red"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_star" fill="gold"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_fave" fill="purple"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_star" fill="silver"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_fave" fill="pink"></use></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use href="#s_star" fill="blue"></use></svg>
Here the fast and furious way :)
body {
background-color: #DEFF05;
}
svg {
width: 30%;
height: auto;
}
svg path {
color: red;
fill: currentcolor;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 514.666 514.666"><path d="M514.666,210.489L257.333,99.353L0,210.489l45.933,19.837v123.939h30V243.282l33.052,14.274v107.678l4.807,4.453 c2.011,1.862,50.328,45.625,143.542,45.625c93.213,0,141.53-43.763,143.541-45.626l4.807-4.452V257.557L514.666,210.489z M257.333,132.031L439,210.489l-181.667,78.458L75.666,210.489L257.333,132.031z M375.681,351.432 c-13.205,9.572-53.167,33.881-118.348,33.881c-65.23,0-105.203-24.345-118.348-33.875v-80.925l118.348,51.112l118.348-51.111 V351.432z"></path></svg>
For example, in your HTML:
<body>
<svg viewBox="" width="" height="">
<path id="struct1" fill="#xxxxxx" d="M203.3,71.6c-.........."></path>
</svg>
</body>
Use jQuery:
$("#struct1").css("fill", "<desired colour>");
Check out this code. It works.
<div>
<!-- YouTube -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
<path fill="white"
d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z" />
</svg>
<!-- Instagram -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="white"
d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z" />
</svg>
</div>
CSS
svg {
fill: white;
}
For a better resolution about Manish Menaria's (thank you so much for your help) response, use this filter generator instead a purposed generator: https://angel-rs.github.io/css-color-filter-generator/
.filter-green{
filter: invert(48%) sepia(79%) saturate(2476%) hue-rotate(86deg) brightness(118%) contrast(119%);
}
Use an svg <mask> element.
This is better than other solutions because:
Closely matches your original code.
Works in IE!
The embedded image can still be an external, unmodified file.
The image does not even have to be an SVG.
Color is inherited from font-color, so easy to use alongside text.
Color is a normal CSS color, not a strange combination of filters.
<svg style="color: green; width: 96px; height: 96px" viewBox="0 0 100 100" preserveAspectRatio="none">
<defs>
<mask id="fillMask" x="0" y="0" width="100" height="100">
<image xlink:href="https://svgur.com/i/AFM.svg" x="0" y="0" width="100" height="100" src="ppngfallback.png" />
</mask>
</defs>
<rect x="0" y="0" width="100" height="100" style="stroke: none; fill: currentColor" mask="url("#fillMask")" />
</svg>
https://jsfiddle.net/jamiegl/5jaL0s1t/19/
If you want to do this to an inline SVG file, that is, for example, a background image in your CSS content:
background: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='rgba(31,159,215,1)' viewBox='...'/%3E%3C/svg%3E");
Of course, replace the ... with your inline image code.
There are some problems with Manish Menaria's answer, if we convert white color it shows gray.
So I added some tweaks, and the below example specifically shows how to change the color in the material icon:
<mat-icon class="draft-white" svgIcon="draft" aria-hidden="false"></mat-icon>
.draft-white{
filter: brightness(0) invert(1);
}
You can use a font icon to use any CSS option in SVG
I was searching for a way to have any CSS options, like animation for SVG, and I ended up to generate a font icon with my SVG(s) and then used it inside a span (like Font Awesome), so any CSS option, like coloring, was available on it.
I used https://icomoon.io to convert my SVG image to a font icon. Then you can use it like Font Awesome or MaterialIcon inside HTML elements.
I found it a bit clumsy, but it is definitely a working way to dynamically change the color of an SVG included with <img> tag.
In the SVG file, you can add CSS content the following way:
<svg ...>
<defs>
<style>
...
<style>
<defs>
There you can use #media rules, with which the SVG can look outside itself for contextual circumstances. There's an aspect-ratio media feature that applies to the SVG box (e.g., the <img> tag). You can create different contexts for the SVG by stretching the SVG box a little bit.
This way you can also make the favicon the same SVG that appears on the website, but with a different color. (In this case, no other SVG boxes should be square-shaped.)
/* img stretched horizontally (if SVG is square-shaped) */
#media (min-aspect-ratio: 1000/999) {
path {
fill: blue;
}
}
/* img stretched vertically (if SVG is square-shaped) */
#media (max-aspect-ratio: 999/1000) {
path {
fill: green;
}
}
/* img with exact sizes */
#media (aspect-ratio: 86/74) {
path {
fill: red;
}
}
/* favicon with light browser theme */
#media (aspect-ratio: 1/1) and (prefers-color-scheme: light) {
path {
fill: black;
}
}
/* favicon with dark browser theme */
#media (aspect-ratio: 1/1) and (prefers-color-scheme: dark) {
path {
fill: white;
}
}
One very important thing
The SVG must contain viewBox information, so that the stretching does not affect the graphics. Example:
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
Actually, there is a quite more flexible solution to this problem: writing a Web Component which will patch SVG as text at runtime. I also published in a gist with a link to JSFiddle.
👍 filter: invert(42%) sepia(93%) saturate(1352%) hue-rotate(87deg) brightness(119%) contrast(119%);
<html>
<head>
<title>SVG with color</title>
</head>
<body>
<script>
(function () {
const createSvg = (color = '#ff9933') => `
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="76px" height="22px" viewBox="-0.5 -0.5 76 22">
<defs/>
<g>
<ellipse cx="5" cy="10" rx="5" ry="5" fill="#ff9933" stroke="none" pointer-events="all"/>
<ellipse cx="70" cy="10" rx="5" ry="5" fill="#ff9933" stroke="none" pointer-events="all"/>
<path d="M 9.47 12.24 L 17.24 16.12 Q 25 20 30 13 L 32.5 9.5 Q 35 6 40 9 L 42.5 10.5 Q 45 12 50 6 L 52.5 3 Q 55 0 60.73 3.23 L 66.46 6.46" fill="none" stroke="#ff9933" stroke-miterlimit="10" pointer-events="stroke"/>
</g>
</svg>`.split('#ff9933').join(color);
function SvgWithColor() {
const div = Reflect.construct(HTMLElement, [], SvgWithColor);
const color = div.hasAttribute('color') ? div.getAttribute('color') : 'cyan';
div.innerHTML = createSvg(color);
return div;
}
SvgWithColor.prototype = Object.create(HTMLElement.prototype);
customElements.define('svg-with-color', SvgWithColor);
document.body.innerHTML += `<svg-with-color
color='magenta'
></svg-with-color>`;
})();
</script>
</body>
</html>
My answer would be this. But I’m not 100% sure if it works for everyone:
Select 'svg' and then 'path'. And you can change 'fill' then.
.eye-icon-container {
width: 33px;
height: 33px;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
:hover {
background-color: #ddf0ff;
}
:active {
background-color: #1d398d;
svg {
path {
fill: #fff;
}
}
}
}
If you have a single-colour SVG with varying opacities that you simply want to tint to a different colour then there is another approach that can be used: the feFlood SVG filter.
This solution is not as straightforward as a single-line CSS, however:
It works on SVGs inside of an img element.
This doesn't require editing the source SVG at all.
It allows you to simply choose a target colour for the SVG and not worry about complex colour transforms, like hue-rotate.
Here is an example:
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<defs>
<filter id="recolourFilter" filterUnits="userSpaceOnUse">
<feFlood flood-color="aquamarine" result="flood" />
<feComposite in="flood" in2="SourceAlpha" operator="in" />
</filter>
</defs>
</svg>
<img style="filter: url(#recolourFilter);" width="300" src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Bitmap_VS_SVG.svg" />
In the above example, we create an inline SVG to define the filters and then we apply it to the image. Inside of the <filter> block we first define the fill colour that we want via <feFlood> and then we create a composite image using the alpha channel of the source plus the flood colour. Finally, the filter is applied to the whole image via the filter CSS property on the img element.
I learned about this technique from this Smashing Magasine article. It's a highly recommended read if you want to learn more about SVG filters.
A few additional things to note:
This filter can be applied to any HTML element via the CSS filter property.
The same filter can be reused multiple times on the same page.
If you are using an inline SVG then the <defs> block can form part of the svg element and the filter can still be applied to the whole SVG or on selective elements. This avoids needing a separate SVG element for the filters.
A good approach is to use a mixin to control stroke colour and fill colour. My 'svg's are used as icons.
#mixin icon($color, $hoverColor) {
svg {
fill: $color;
circle, line, path {
fill: $color
}
&:hover {
fill: $hoverColor;
circle, line, path {
fill: $hoverColor;
}
}
}
}
You can then do the following in your SCSS file:
.container {
#include icon(white, blue);
}

SVG elements to zoom whole SVG group on click or mouseover

I would like to use the circles within my SVG file to trigger a zoom in centred on the circle. I have got it working with a div acting as the trigger for the zoom but if I instead apply id="pin" to one of the circle elements within the SVG it no longer zooms in. Can anyone tell me why this is?
Is there a better way for me to achieve what I am trying to do? Ideally, I would like it to be possible to click to zoom and then to access other interactivity within the SVG while zoomed in.
If this is not possible is there a simple way to zoom and pan an SVG and to be able to access SVG interactivity while zoomed?
If I have missed something obvious please forgive me, I’m very much still learning the basics!
Rough example:
CodePen link
<div id="pin">click to trigger zoom</div>
<div class="map" id="mapFrame">
<svg class="image" id="mapSVG" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1920 1442.5"" style="
enable-background:new 0 0 1920 924.9;" xml:space="preserve">
<g id="Layer_7" data-name="Layer 7">
<image width="1800" height="1350" transform="translate(0) scale(1.069)" opacity="0.3"
xlink:href="https://media.npr.org/assets/img/2020/07/04/seamus-coronavirus-d3-world-map-20200323_wide-a3888a851b91a905e9ad054ea03e177e23620015.png" />
</g>
<g id="one">
<circle cx="929.664" cy="944.287" r="81.191"/>
</g>
<g id="two">
<circle cx="638.164" cy="456.863" r="81.191" />
</g>
<g id="three">
<circle cx="1266.164" cy="498.868" r="81.191" />
</g>
</svg>
</div>
<script src="app.js"></script>
svg {
width: 100%;
height: auto;
}
#pin {
position: absolute;
height: 65px;
width: 75px;
top: 300px;
left: 550px;
padding: 10px;
background-color: yellow;
}
let imgElement = document.querySelector('#mapFrame');
let pinElement = document.querySelector('#pin');
pinElement.addEventListener('click', function (e) {
imgElement.style.transform = 'translate(-' + 0 + 'px,-' + 0 + 'px) scale(2)';
pinElement.style.display = 'none';
});
imgElement.addEventListener('click', function (e) {
imgElement.style.transform = null;
pinElement.style.display = 'block';
});
When you click on the circle, you are also clicking on the background image as well, triggering two events which is essentially cancelling the zoom. You can see this if you place alert('click 1'); and alert('click 2'); in your listeners.
This doesn't happen on the #pin element because it's outside background div and avoids the event bubbling up. This is solved by adding event.stopPropagation();
Code from your CodePen:
let imgElement = document.querySelector('#mapFrame');
let pinElement = document.querySelector('#one'); //changed to #one
pinElement.addEventListener('click', function (e) {
imgElement.style.transform = 'translate(-' + 0 + 'px,-' + 0 + 'px) scale(2)';
pinElement.style.display = 'none';
event.stopPropagation(); //added to prevent bubbling
});
imgElement.addEventListener('click', function (e) {
imgElement.style.transform = null;
pinElement.style.display = 'block';
});

Rotating elements in an svg at different angles

I'm trying to create an svg where when you hover over one element in the image it triggers different animations on other parts of the image.
Example here: https://codepen.io/SHINZOC/pen/GRgXeey
Looking to have the pink rectangle disappear when hovering over while also having each square rotate at different angles. I can get one square to rotate but the others won't for some reason. Any help would be much appreciated!!
.pink_rectangle { transition: .3s;}
.pink_rectangle:hover {opacity: 0;}
#pink_rectangle:hover + #blue_square {
transform: rotate(45deg);
transform-origin: center center;
transform-box:fill-box;
}
#pink_rectangle:hover + #yellow_square {
transform: rotate(35deg);
transform-origin: center center;
transform-box:fill-box;
}
#pink_rectangle:hover + #orange_square {
transform: rotate(15deg);
transform-origin: center center;
transform-box:fill-box;
}
It is not yet possible to do what you ask with plain css you would need to use addEventListener("mouseover", function()); in javascript to make it work.
It doesn't work because CSS can't affect other css classes that are not contained within it.
If you don't know how to use addEventListener read a bit of this.

How to rotate part of an svg?

I'm trying to create an svg image where when you hover over part of an image it animates other parts of the image. It's a guitar pedal that when you hover over it, the light goes on and the dials rotate. However I can't figure out how to rotate the dials without the dials shooting off of the pedal. Any help would be much appreciated!!
Here's an example of it:
https://codepen.io/SHINZOC/pen/vYEaooM
.lo { transition: .1s;}
.lo:hover {opacity: 0;}
#Light_off:hover + #Dials {
transform: rotate(45deg);
transform-origin: center center;
}
Change the CSS to
.lo { transition: .1s;}
.lo:hover {opacity: 0;}
#Light_off:hover + #Dials {
transform: rotate(45deg);
transform-origin: center center;
transform-box:fill-box;
}
since you want to rotate around the fill box and not the view box.

SVG Path Fill property not working as it should

please have a look at this code and let me know where I'm going wrong, the fill property does not appear to be working? This is supposed to mimic a car turn signal.
I have checked and it appears this is the correct use of the fill property but it is not working as expected. Also I have tried with a much simple path such as a triangle and this worked - so what is unique about this shape if path = path?
function trafficLighttoggle() {
var pth = document.getElementById("pth");
if (pth.classList == "traffic") {
pth.classList = "white";
} else {
pth.classList = "traffic";
}
}
setInterval(trafficLighttoggle, 500);
.traffic {
stroke: #ffbf00;
stroke-width: 1px;
fill: #ffbf00;
fill-opacity: 1;
}
.white {
stroke: white;
stroke-width: 1px;
fill: white;
fill-opacity: 1;
}
<svg overflow="visible">
<path id="pth" class="traffic" d="M160,90 L210,50 M210,50 V70 M210,70 H290 M290,70 V120 M290,120 H210 M210,120 V140 M210,140 L160,90 Z"/>
</svg>
I found out what is wrong with my code, I got my path generator incorrect it should have been
<path id="pth" class="traffic" d="M160,90 L210,50 V70 H290 V120 H210 V140 Z"/>
So this is now resolved.
Thanks.

Resources