I'm trying to recreate a meter in SVG.
When trying to rotate a <line> or a <path> using transform and a transition, the line / path takes a wierd route before reaching the destination angle.
Observed that it works differently between laptop and extended monitors.
How to fix this ?
CODEPEN : https://codepen.io/sparkeplug/pen/zYWZxvX
BEHAVIOR IN DIFFERENT MONITORS :
EXTENDED MONITOR BEHAVIOR :
LAPTOP MONITOR BEHAVIOR :
Im expecting the line to rotate with the lines bottom coordinates as transform origin ( like a typical meter ).
EXPECTED BEHAVIOR SAMPLE IMAGE :
Move the centre of rotation coords out of the transform and set them with a transform-origin property instead.
const btn = document.getElementById("btn");
const meter = document.getElementById("idMeter");
let flag = false;
btn.addEventListener("click", function () {
console.log("test");
const angle1 = "79";
const angle2 = "-48";
meter.setAttribute("transform", `rotate(${flag ? angle1 : angle2})`);
flag = !flag;
});
.container {
display: flex;
position: relative;
flex-direction: column;
justify-content: center;
align-items: flex-start;
height: 20rem;
width: 100%;
}
.svg {
border: 1px solid red;
}
.meter {
transition: all 0.2s ease-in-out;
transform-origin: 250px 350px;
}
<main class="container">
<svg class="svg" viewbox="0 0 500 500">
<g class="meter" id="idMeter">
<line x1="250" y1="350" x2="250" y2="100" stroke="black" stroke-width="3"></line>
</g>
</svg>
<button id="btn">Animate</button>
</main>
I have an animated SVG in react that is supposed to draw some lines down the screen starting from the top to the bottom relative to 100vh adn 100vw. the problem is it works on my native resolution but not when the window is resized (i made the svg in gimp in respect to my native res). I have tried every single css combination i can think of but i just cant get it to work as needed.
(images at the bottom for an idea of what it should look like vs what happens when resized)
thanks in advance :)
SVG:
<svg className="line_svg" xmlns="http://www.w3.org/2000/svg"
width="auto" height="100%"
viewBox="0 0 2560 1440">
<path id="Selection"
fill="none" stroke="green" stroke-width="2"
d="M 1279.00,0.00
C 1280.66,5.65 1283.93,7.93 1288.00,12.00
1288.00,12.00 1306.00,30.00 1306.00,30.00
1306.00,30.00 1374.00,98.00 1374.00,98.00
1374.00,98.00 1548.00,271.00 1548.00,271.00
1548.00,271.00 1606.00,329.00 1606.00,329.00
1612.96,335.96 1631.41,355.28 1638.00,360.00
1638.00,360.00 1610.91,389.00 1610.91,389.00
1610.91,389.00 1576.91,425.00 1576.91,425.00
1576.91,425.00 1549.09,454.00 1549.09,454.00
1549.09,454.00 1457.91,551.00 1457.91,551.00
1457.91,551.00 1396.09,616.00 1396.09,616.00
1396.09,616.00 1151.91,875.00 1151.91,875.00
1151.91,875.00 1124.09,904.00 1124.09,904.00
1124.09,904.00 1014.91,1020.00 1014.91,1020.00
1014.91,1020.00 989.04,1047.00 989.04,1047.00
989.04,1047.00 978.00,1059.00 978.00,1059.00
978.00,1059.00 965.83,1072.00 965.83,1072.00
964.29,1073.71 960.88,1076.96 960.17,1079.00
959.20,1081.77 961.03,1083.93 962.63,1086.00
962.63,1086.00 976.00,1101.00 976.00,1101.00
976.00,1101.00 1030.58,1162.00 1030.58,1162.00
1030.58,1162.00 1087.83,1226.00 1087.83,1226.00
1087.83,1226.00 1213.83,1367.00 1213.83,1367.00
1213.83,1367.00 1261.07,1420.00 1261.07,1420.00
1266.18,1425.63 1275.10,1437.95 1282.00,1440.00
1280.25,1433.86 1276.28,1430.56 1272.08,1426.00
1272.08,1426.00 1256.07,1408.00 1256.07,1408.00
1256.07,1408.00 1196.16,1341.00 1196.16,1341.00
1196.16,1341.00 1044.16,1171.00 1044.16,1171.00
1044.16,1171.00 993.17,1114.00 993.17,1114.00
993.17,1114.00 963.00,1081.00 963.00,1081.00
963.00,1081.00 975.04,1068.00 975.04,1068.00
975.04,1068.00 1024.09,1016.00 1024.09,1016.00
1024.09,1016.00 1052.91,986.00 1052.91,986.00
1052.91,986.00 1144.09,889.00 1144.09,889.00
1144.09,889.00 1205.91,824.00 1205.91,824.00
1205.91,824.00 1450.09,565.00 1450.09,565.00
1450.09,565.00 1477.91,536.00 1477.91,536.00
1477.91,536.00 1586.09,421.00 1586.09,421.00
1586.09,421.00 1612.96,393.00 1612.96,393.00
1612.96,393.00 1623.00,382.00 1623.00,382.00
1623.00,382.00 1635.28,369.00 1635.28,369.00
1636.74,367.34 1640.17,363.91 1640.83,362.00
1641.84,359.07 1639.76,357.00 1637.94,355.00
1637.94,355.00 1623.00,340.00 1623.00,340.00
1623.00,340.00 1560.00,277.00 1560.00,277.00
1560.00,277.00 1359.00,77.00 1359.00,77.00
1359.00,77.00 1304.00,22.00 1304.00,22.00
1298.18,16.18 1286.24,2.12 1279.00,0.00 Z" />
</svg>
CSS:
.front_page_parent{
width: 100vw;
height: 100vh;
display: block;
margin: 0 auto;
padding: 0;
}
.line_svg{
display: block;
margin: 0 auto;
max-width: 100vw;
max-height: 100vh;
stroke-dasharray: 6000;
animation: draw 2s ease-in;
fill: green;
overflow: hidden;
}
Structure of App
export const App = () => {
return (
<div className="app">
<FrontPage />
</div>
)
}
export const FrontPage = () => {
return (
<div className="font_page_parent" style={BackgroundImageStyle}>
<svg className="line_svg" xmlns="http://www.w3.org/2000/svg"
width="auto" height="100%"
viewBox="0 0 2560 1440">
<path id="Selection"
fill="none" stroke="green" stroke-width="2"
d="......."- />
</svg>
</div>
working full screen
not working resized
I have a div with an SVG background. The div height and width do not change. The only thing that changes is the SVG file used for the background when the div is hovered.
The issue seems to be that when the SVG background is replaced (even after the asset is preloaded) the image seems to "shake" before settling into place. I've triple checked and both SVG assets are the exact same height and width and viewport, the only difference is the coloring.
For reference, the issue can be seen on this page: https://tqt.uwaterloo.ca/.
When hovering the "hamburger" icon the issue can be seen.
When hovering the "search" icon there is no issue, however the "search" icon has the exact same SVG replacement on it and it's working as expected.
Final note: This issue is only replicatable on retina (2x+) monitors.
There are two bugs here, one in Blink (Chrome and Chromium based browsers) and one in webkit, both caused by the transitioning of the background-image property.
Here is a minimal repro of these bugs:
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 26">
<path d="M0,0h34v4H0V0z M0,22h34v3.9H0V22z M0,11h34v4H0V11z" fill="black"/>
</svg>`;
const url = 'url(data:image/svg+xml,' + encodeURIComponent( svg ) + ')';
const urls = [ url, url.replace( 'black', 'red' ) ];
let i = 0;
setInterval(() =>
document.querySelector( '.burger' ).style.backgroundImage = urls[ (++i % 2) ],
500
);
.burger {
height: 30px;
width: 100px;
background-position: center;
background-repeat: no-repeat;
transition: background-image .2s linear;
}
<div class="burger">
</div>
Chrome's bug is due to the fact your svg files don't have their own width and height attributes, setting one will fix it there.
Safari bug is caused by the retina scaling, on which they weirdly apply the transition too... For this one a fix would be harder, but setting the correct height will make it less visible since we'd only have antialiasing artifacts to move.
Here is the same snippet as above, with just the height and width attributes set:
const svg = `<svg width="34" height="26" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 26">
<path d="M0,0h34v4H0V0z M0,22h34v3.9H0V22z M0,11h34v4H0V11z" fill="black"/>
</svg>`;
const url = 'url(data:image/svg+xml,' + encodeURIComponent( svg ) + ')';
const urls = [ url, url.replace( 'black', 'red' ) ];
let i = 0;
setInterval(() =>
document.querySelector( '.burger' ).style.backgroundImage = urls[ (++i % 2) ],
500
);
.burger {
height: 30px;
width: 100px;
background-position: center;
background-repeat: no-repeat;
transition: background-image .2s linear;
}
<div class="burger">
</div>
Now, there should probably be a note that if targeting only quite recent browsers is not an issue for you, you could achieve the same effect by using only CSS filters, saving one network request:
const svg = `<svg width="34" height="26" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 26">
<path d="M0,0h34v4H0V0z M0,22h34v3.9H0V22z M0,11h34v4H0V11z"/>
</svg>`;
// This time we generate only a single black url
const url = 'url(data:image/svg+xml,' + encodeURIComponent( svg ) + ')';
document.querySelector( '.burger' ).style.backgroundImage = url;
.burger {
height: 30px;
width: 100px;
background-position: center;
background-repeat: no-repeat;
transition: filter .2s linear;
}
.burger:hover {
/*
Using formula from
https://stackoverflow.com/a/43959856/3702797
to safely fallback on black for browsers withtou support for filters
*/
filter: invert(10%) sepia(100%) saturate(10000%) ;
}
hover the icon to change its color.
<div class="burger">
</div>
I'm trying to center the <path> animation horizontally and vertically (after having scaled it down with transform="scale(0.5)) while keeping the <svg> at 100% width and height of its container. I'm using Snap.svg.
As you can see below, the <svg> is fine but the <path> is all crammed up in the upper left corner.
var s = Snap("#me");
var myPath = s.select("#mypath");
function reset( el ) {
el.stop();
el.attr({ "stroke-dashoffset": 125 });
};
function startAnim( el ) {
el.animate( { "stroke-dashoffset": 600 }, 1000 );
};
reset( myPath );
s.mouseover( function() {
startAnim( myPath );
} );
s.mouseout( function() {
reset( myPath );
} );
.test {
border: 1px solid black;
height: 500px;
width: 500px;
}
#me {
border: 2px solid green;
}
#mypath {
border: 2px solid blue;
display: flex;
justify-content: center;
align-content: center;
flex-direction: column;
}
<script src="http://tedbeer.net/lib/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">
<div class="pie">
<svg id="me" viewBox="0 0 350 350">
<!--<svg id="me" viewBox="0 0 350 350" width="100" height="100">-->
<path id="mypath" d="M 175, 175 m 0, -75 a 75, 75 0 1, 0 0, 150 a 75, 75 0 1, 0 0, -150" fill="none" stroke="#ccc" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000" transform="scale(0.5)">
</path>
</svg>
</div>
</div>
If you want to amend the transform via Snap you would do it very slightly different.
The end result is probably the same to the browser as mefs solution, as it will end up having a similar matrix on the element itself, its really down to whats the most convenient.
You can specify a center point for the scaling, just add it after the scale x & y parameters.
myPath.transform('s0.5,0.5,175,175');
jsfiddle
Edit: Actually I'm an idiot, its even more simple. I just remembered that Snap automatically uses the center of an object on transform strings if not specified. So in fact you can reduce it to this..
myPath.transform('s0.5');
jsfiddle
The position of your object is affected by the scaling transform. Add a translation in order to move the object to the correct position, right after scale:
transform="scale(0.5)translate(175,175)"
updated snippet:
var s = Snap("#me");
var myPath = s.select("#mypath");
function reset( el ) {
el.stop();
el.attr({ "stroke-dashoffset": 125 });
};
function startAnim( el ) {
el.animate( { "stroke-dashoffset": 600 }, 1000 );
};
reset( myPath );
s.mouseover( function() {
startAnim( myPath );
} );
s.mouseout( function() {
reset( myPath );
} );
.test {
border: 1px solid black;
height: 500px;
width: 500px;
}
#me {
border: 2px solid green;
}
#mypath {
border: 2px solid blue;
display: flex;
justify-content: center;
align-content: center;
flex-direction: column;
}
<script src="http://tedbeer.net/lib/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">
<div class="pie">
<svg id="me" viewBox="0 0 350 350">
<!--<svg id="me" viewBox="0 0 350 350" width="100" height="100">-->
<path id="mypath" d="M 175, 175 m 0, -75 a 75, 75 0 1, 0 0, 150 a 75, 75 0 1, 0 0, -150" fill="none" stroke="#ccc" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000" transform="scale(0.5)translate(175,175)">
</path>
</svg>
</div>
</div>
Edit: possible other options based on the question in your comment:
option 1
If you do not want to have the translate at all, You can change the viewBox coordinates, this way:
<svg id="me" viewBox="-75 -75 350 350" width="100%" height="100%">
Please note that I haven't taken the time to calculate the exact ones that would be relevant to you, but you get the idea. You should adapt the four values so that they match to the viewport you want to obtain.
option 2
do some math to replace your current path coordinates by the ones for an object twice smaller, so that you don't need the scale transform anymore.