Firefox transform-origin on SVG still broken - css

When we apply a 2D CSS rotation to an SVG shape in Firefox (I'm on 63.0.1 - latest version), it gets misaligned. There are plenty of questions on this topic, eg. Setting transform-origin on SVG group not working in FireFox
I'm not seeing it as fixed, but perhaps I'm missing something. Best to look at my CodePen first: https://codepen.io/MSCAU/pen/GwozbO
Here's the gist of it:
circle {
fill: none;
transform-origin: center;
// transform-origin: 6px 6px; /* Makes no difference */
// transform-box: fill-box; /* Makes no difference */
}
circle:nth-child(1) {
stroke: red;
stroke-width: 2;
}
circle:nth-child(2) {
stroke: blue;
stroke-width: 1;
transform: rotate(-90deg);
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="120" height="120">
<circle cx="6" cy="6" r="5"/>
<circle cx="6" cy="6" r="5"/>
</svg>

Related

How to rotate a div across the axis of an svg circle

I have a black circle and a small red circle on the axis of the bigger one:
Both are created with simple svg code:
export default function App() {
return (
<div>
<svg className="main-svg" viewBox="0 0 100 100">
<circle pathLength="25" cx="50%" cy="50%" r="50%" />
</svg>
<svg viewBox="0 0 100 100">
<circle
className="small-circle"
transform-origin="center"
fill="green"
pathLength="25.5"
cx="50%"
cy="50%"
r="50%"
/>
</svg>
</div>
);
}
It's easy to rotate the smaller one, because both circles have the same radius so you just need to change the degree:
svg {
position: absolute;
}
.small-circle {
transform: rotate(20deg);
stroke-linecap: round;
stroke-width: 5;
stroke-dasharray: 0 25;
stroke-dashoffset: -2.5;
fill: none;
stroke: red;
}
However I would like to insert some content into that red circle svg, namely use a div, and to my knowledge it's very difficult to do with svg. How can I create a rotating div instead of the red svg, hopefully without constant pixel values to it's sizable like the svgs?
The code on stackblitz: https://stackblitz.com/edit/react-ts-fwmjzn?file=style.css
Although <foreignObject can be inserted to SVG, It's hard to implement your requirement.
Because of .small-circle is rendered by stroke, it's hard to locate x and y's positions when it is rotating.
Maybe using div and CSS3 is a good implement.
https://stackblitz.com/edit/react-ts-iq2aqm?file=App.tsx,style.css,index.tsx
import * as React from 'react';
import './style.css';
export default function App() {
return (
<div className="container">
<svg className="main-svg" viewBox="0 0 100 100">
<circle pathLength="25" cx="50%" cy="50%" r="50%" />
</svg>
<div className="small-circle">A</div>
</div>
);
}
h1,
p {
font-family: Lato;
}
body {
margin: 0;
}
.container {
position: relative;
outline: 1px solid red;
}
#keyframes rotate {
to {
transform: rotate(1turn);
}
}
.small-circle {
width: 5vw;
height: 5vw;
border-radius: 50%;
background-color: coral;
top: 50%;
margin-top: -2.5vw;
margin-left: -2.5vw;
position: absolute;
outline: 1px solid red;
transform-origin: 52.5vw center;
animation: rotate 8s linear infinite;
text-align: center;
}
hope help you.

Why is my SVG path start drawing in two places at once?

I am new to learning SVG and css animations. I am simply trying to draw the letter "T". My issue is that the path begins animating in two different spots. I want it to draw the horizontal line first before drawing the vertical line. What am I not understanding? Below is what i have thus far. Thanks.
.letter_loader {
fill: none;
stroke: #000;
stroke-width: 8px;
stroke-dasharray: 200px;
stroke-dashoffset: 200px;
animation: move 5s linear forwards;
}
#keyframes move {
100% {
stroke-dashoffset: 0;
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="129.204 94.714 359.102 415.224" width="355.1" height="411.22">
<path class="letter_loader"
d="M175.2 250.76 L275.2 250.76 M225.2 250.76 L225.2 350.76" />
</svg>
The way I would approach this wouldn't be the best way...
First, setup a box/line that blends in with the background. It should be compiled before the <path>. It should be as wide as the line and be as tall as the horizontal line's width. Then, start the vertical line at the top of the box/line that is supposed to blend in with the background. What is supposed to happen is although the animations start a the same time, the vertical line won't be seen by the viewer until the horizontal line finishes. This may be a bit more difficult if your background is a linear gradient.
Here is what I am thinking:
.letter_loader {
fill: none;
stroke: #000;
stroke-width: 8px;
stroke-dasharray: 200px;
stroke-dashoffset: 200px;
animation: move 5s linear forwards;
}
#keyframes move {
100% {
stroke-dashoffset: 0;
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400" style='background-color: #f1f1f1;'>
<path class="letter_loader"
d="M150 150 L250 150 M200 50 L200 250" />
<path style="stroke: #f1f1f1; stroke-width: 8px;" d="M200 50 L 200 146"/>
</svg>
You are drawing a single svg path with a single class, so, the entire path will be drawn with the same animation that starts with stroke-dashoffset:200px and after 5 seconds become 0px.
One option that I think (I'm not an expert with svg or animations, if someone is and I'm saying bullshit, please tell me) you can use, is to separate the path in two, one for top of T other for the base. So in the T base you add another class with another animation, that starts a little later than the top part.
See below code to understand it better.
/* class and animation for T Top*/
.letter_loader {
fill: none;
stroke: #000;
stroke-width: 8px;
stroke-dasharray: 200px;
stroke-dashoffset: 200px;
animation: move 5s ease forwards;
}
#keyframes move {
100% {
stroke-dashoffset: 0;
}
}
/* class and animation for T Base*/
.letter_loader_later {
fill: none;
stroke: #000;
stroke-width: 8px;
stroke-dasharray: 200px;
stroke-dashoffset: 200px;
animation: move_later 5s ease forwards;
}
#keyframes move_later {
/* until 25% it stills with 200px to have a "later start*/
25% {
stroke-dashoffset: 200px;
}
100% {
stroke-dashoffset: 0;
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="129.204 94.714 359.102 415.224" width="355.1" height="411.22">
<path class="letter_loader" d="M175.2 250.76 L275.2 250.76" /> <!-- T TOP -->
<path class="letter_loader_later" d="M225.2 250.76 L225.2 350.76"/> <!-- T BASE -->
</svg>

Hover over svg does not work when mouse is over text within the svg item

I wanted to apply a simple hover over both the shape and text inside it. It's supposed to change the color (in this case fill), when pointer is over either the shape or the text. I thought that if the text is kind of inside of the svg element, the hover will be applied to it too. Except it isn't.
I'm not good with svg, in fact, this is the first time I'm working with them. Tried to find answers here and in google, but nothing applies to my problem. Please help.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 170">
<path class="a" fill={funnels[funnel].bgColor} d="M378.24,59.8l33.39,88.73a11.82,11.82,0,0,0,11.07,7.66h54.6a11.82,11.82,0,0,0,11.07-7.66L521.76,59.8a11.82,11.82,0,0,0-11.07-16H389.31A11.82,11.82,0,0,0,378.24,59.8Z"></path>
<text x="50%" y="60%" dominant-baseline="middle" text-anchor="middle" font-family="Lato" font-size="2rem" fill="#f2f2f2"> Hello </text>
</svg>
svg {
filter: drop-shadow(0px 0px 10px rgba(0,0,0,0.2));
.a {
transition: all ease-in 0.5s;
&:hover {
fill: darken(red, 10%);
cursor: pointer;
}
}
}
Here's my fiddle: https://jsfiddle.net/Madzik/bkbgf1os/
Apply hover effect to svg not .a.
svg {
filter: drop-shadow(0px 0px 10px rgba(0,0,0,0.2));
.a {
transition: all ease-in 0.5s;
}
&:hover {
fill: darken(red, 10%);
cursor: pointer;
}
}
svg {
filter: drop-shadow(0px 0px 10px rgba(0,0,0,0.2));
}
svg .a {
transition: all ease-in 0.5s;
}
svg:hover {
fill: green;
cursor: pointer;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 170">
<path class="a" fill={funnels[funnel].bgColor} d="M378.24,59.8l33.39,88.73a11.82,11.82,0,0,0,11.07,7.66h54.6a11.82,11.82,0,0,0,11.07-7.66L521.76,59.8a11.82,11.82,0,0,0-11.07-16H389.31A11.82,11.82,0,0,0,378.24,59.8Z"></path>
<text x="50%" y="60%" dominant-baseline="middle" text-anchor="middle" font-family="Lato" font-size="2rem" fill="#f2f2f2"> Hello </text>
</svg>

Why does a rect require width and height attribute in Firefox?

In the following example I created a blinking eyes animation using CSS and an SVG: http://codepen.io/JamesTheHacker/pen/oLZVrY
It works fine in chrome, but on Firefox the eyes do not appear unless I specifically provide a width and height attribute on the <rect>.
Without the attribute the eyes are not visible. If I add the attribute the CSS height animation has no effect.
In SVG 1.1 height and width are attributes i.e. you can't set the height and width via CSS.
In SVG 2 it is proposed width and height should be CSS properties.
Back in 2016 only Chrome had implemented this part of the unfinished SVG 2 specification, since then Firefox has also implemented it so the testcase works as expected.
You have already an excellent answer indicating what the problem is
You can solve it this way
* { box-sizing: border-box; }
body { background-color: rgb(0, 184, 234); }
svg {
display: block;
margin: 90px auto;
width: 380px;
height: 130px;
}
/*
* Keyframes for blink animation
*/
#keyframes blink {
0% { transform: scaleY(1); }
40% { transform: scaleY(0); }
80% { transform: scaleY(1); }
}
.eye {
height: 20px;
width: 20px;
animation-name: blink;
animation-duration: 1s;
animation-iteration-count: infinite;
transform-origin: center 315px;
}
<svg>
<g transform="translate(-108.75 -258.41)">
<path id="specs" fill="#FFF" d="M328.911,258.412v10.29h-19.127v16.192h-19.16v16.249h19.287v-16.191h19v62.169h19.432
v-68.736h123.995v68.761h19.432v-88.709l-18.047,0.001v-0.025h-125.38H328.911z M124.069,258.454v0.001h-15.321v88.709h19.427
v-68.733h123.996l0.008,68.757h19.423v-62.401h19.032v-16.25h-19.032v-10.053h-18.047v-0.026H124.072L124.069,258.454z
M348.294,347.163v17.488h19.4v19.951h85.141v-19.976h-85.109v-17.464H348.294L348.294,347.163z M452.819,347.171v17.439h19.431
v-17.439H452.819z M128.133,347.203v17.487h19.398v19.951h85.149l-0.008-19.975h-85.117l0.001-17.464h-19.427H128.133z
M232.658,347.212v17.439h19.423v-17.439H232.658z"/>
<g id="eyes">
<rect class="eye" x="181.759" y="305.026" width="20" height="20" fill="#FFF" />
<rect class="eye" x="402.759" y="305.026" width="20" height="20" fill="#FFF" />
</g>
</g>
</svg>

3d transforms on SVG element

Is it possible to achieve perspective with 3d transforms on a SVG elements?
I'm talking about something similar with how the Star Wars opening titles look like with 3d perspective. This is a jsfiddle with the desired effect achieved using CSS3 3d transforms:
<section style="transform: perspective(200px) rotateX(-30deg); transform-origin: 50% 100%; text-align: justify; width: 100px;">
<p style="backface-visibility: hidden;">TEXTTEXTTEXT</p>
</section>
Update Nov 2018:
Testing the snipet from the question in latest chrome and Firefox works. Although support for 3d transforms on svg elements isn't very wide, browsers are implementing it more and more.
Origin answer :
3D transforms aren't supported on SVG elements. There are a few workarounds though :
If the svg doesn't contain elements that shouldn't be transformed, you can use CSS 3d transforms on the SVG element itself :
svg {
width: 70%;
margin: 0 auto;
display: block;
-webkit-transform: perspective(300px) rotateX(30deg);
transform: perspective(300px) rotateX(30deg);
}
<svg viewbox="0 0 100 20">
<text x="0" y="20">TEXTEXTEX</text>
</svg>
In case of polygons, you make a 2D polygon look like a 3D polygon. In the following example, the red rectangle is 3D rotated (rotateX(40deg)) and the black rectangle is a 2D SVG polygon made to look like a 3D rotated rectangle:
div{
display:inline-block;
width:200px; height:100px;
background:red;
transform:perspective(500px) rotateX(40deg);
}
svg{
display:inline-block;
width:220px; height:auto;
}
div, svg{
display:inline-block;
margin:0 10px;
}
<div></div>
<svg viewbox="0 0.5 10 4">
<polygon points="9.9 4.1 0.1 4.1 0.7 0.6 9.3 0.6" fill=""/>
</svg>
3D transforms are supported inside <svg> elements (f.e. on <circle>) (at least to some extent, it seems like perspective is isometric only).
For example, here's animation of transform: rotate3d applied to <circle> elements (tested in Chrome only):
body, html {
background: black;
width: 100%; height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
}
svg {
width: 100%;
}
.gAExgp {
transform-origin: 50% 50% 0px;
animation-name: phEs, ipaUyp;
animation-duration: 4s, 7s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.PwswZ {
transform-origin: 50% 50% 0px;
animation-name: gcRPJT, ipaUyp;
animation-duration: 4s, 8s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes phEs {
50% {
transform: rotate3d(0, 2, 1, 180deg);
}
100% {
transform: rotate3d(0, 2, 1, 360deg);
}
}
#keyframes gcRPJT {
50% {
transform: rotate3d(2, 0, 1, 180deg);
}
100% {
transform: rotate3d(2, 0, 1, 360deg);
}
}
#keyframes ipaUyp {
0% {
stroke: magenta;
}
33% {
stroke: cyan;
}
66% {
stroke: yellow;
}
100% {
stroke: magenta;
}
}
<!-- Logo from https://rebassjs.org -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" style="display:block;max-width:100%;margin:0;fill:none;stroke:cyan" vector-effect="non-scaling-stroke" class="sc-htoDjs hCHUAb"><circle cx="32" cy="32" r="32" fill="#000" stroke="none"></circle><circle cx="32" cy="32" r="30" stroke-width="1" vector-effect="non-scaling-stroke" opacity="0.5"></circle><g><circle cx="32" cy="32" r="24" stroke-width="2" vector-effect="non-scaling-stroke" class="sc-dnqmqq gAExgp"></circle><circle cx="32" cy="32" r="24" stroke-width="2" vector-effect="non-scaling-stroke" class="sc-iwsKbI PwswZ"></circle></g><text x="32" y="34" text-anchor="middle" font-family="system-ui, sans-serif" font-weight="bold" font-size="4" stroke="none" fill="white" style="text-transform:uppercase;letter-spacing:0.5em">Rebass</text></svg>
Also available here: https://codepen.io/anon/pen/MPeyEj

Resources