I try to create a circle with a border in which I can control the ratio of the red part to the green part. I am using React and styled-components. I created something like this:
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
margin: 50px auto;
width: 150px;
height: 150px;
background: ${({ theme }) => theme.red};
border-radius: 50%;
`;
const Circle = styled.div``;
const MaskFull = styled.div`
width: 150px;
height: 150px;
position: absolute;
border-radius: 50%;
clip: rect(0px, 150px, 150px, 75px);
animation: fill ease-in-out 3s;
transform: rotate(126deg);
`;
const Fill = styled.div`
clip: rect(0px, 75px, 150px, 0px);
background-color: ${({ theme }) => theme.green};
animation: fill ease-in-out 3s;
transform: rotate(126deg);
width: 150px;
height: 150px;
position: absolute;
border-radius: 50%;
`;
const InsideCircle = styled.div`
width: 130px;
height: 130px;
border-radius: 50%;
background: #fff;
line-height: 130px;
text-align: center;
margin-top: 10px;
margin-left: 10px;
position: absolute;
z-index: 100;
font-weight: 700;
font-size: 2em;
`;
const SecondMask = styled.div`
width: 150px;
height: 150px;
position: absolute;
border-radius: 50%;
clip: rect(0px, 150px, 150px, 75px);
`;
const Avatar: React.FC = () => {
return (
<Wrapper>
<Circle>
<MaskFull>
<Fill />
</MaskFull>
<SecondMask>
<Fill />
</SecondMask>
<InsideCircle />
</Circle>
</Wrapper>
);
};
export default Avatar;
but as you can see on the picture, there is an ugly 1px protruding red fragment. I can't find the bug in my code, how can I fix it?
Is don't think it's particularly a bug in your code - it's just that the system is trying to work out how to show part CSS pixels on a screen which uses several screen pixels per CSS pixel. Some can get 'left behind'.
A different way of creating the effect you want is to use background images made up of a conic gradient overlaid with a radial one (to give the 'hole' in the middle).
This is a simple snippet to demonstrate the idea in HTML/CSS. The CSS variable --ratio could be set in JS using setProperty to whatever the ratio of red to green is required.
.ratio {
--ratio: 0.3;
height: 150px;
width: 150px;
border-radius: 50%;
position: relative;
clip-path: circle(50%);
}
.ratio::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: conic-gradient(red 0 calc(var(--ratio) * 360deg), lime calc(var(--ratio) * 360deg) 360deg);
z-index: -2;
}
.ratio::after {
content: '';
position: absolute;
width: 80%;
height: 80%;
top: 10%;
left: 10%;
background-color: white;
border-radius: 50%;
z-index: -1;
}
<div class="ratio"></div>
Note: while it is possible to get the same effect using a simpler set up - just one background-image statement on the actual div with first the radial gradient and then the conic gradient you can run into another 'pixel' problem, a sort of fuzziness around the curves. Hence this snippet tidies the circles up with clip-path to give smooth edges.