1

I have unsuccessfully been trying to animate a linear gradient border on a div. I took inspiration from this image. I want the border to be transparent between the two gradients, like in the picture. But I also want the gradient to look like it is moving in a circle around the circle div.

I tried making the circle rotate in an animation, which works but I cannot add a gradient to a border-color property so instead I used an ::after property to add a circle behind the main div so that it looks like a circle. All of this works with a on the background but the main problem from here is that I cannot get the gaps between 2 different gradient borders on opposite sides of the circle.

scss:

#home-page {
    i {
        position: absolute;
        color: $medium-red;
        top: 50%;
        left: 50%;
        font-size: 400px;
        // border-width: 2px;
        // border-style: solid;
        animation: spin 4s linear infinite;
        // border-color: transparent $beige;
        border-radius: 50%;

        &::after {
            content: "";
            background: linear-gradient(60deg, $beige, $light-red);
            position: absolute;
            top: -3px;
            left: -3px;
            right: -3px;
            bottom: -3px;
            border-radius: 50%;
            z-index: -1;
        }
    }
}

@keyframes spin {
    0% {
        transform: translate(-50%, -50%);
    }

    100% {
        transform: translate(-50%, -50%) rotate(360deg);
    }
}

html:

<div id="homepage">
    <i>(font awesome icon of a circle)</i>
</div>
Tices
  • 13
  • 5

2 Answers2

0

HTML

<div id="homepage">
    <div class="circle-gradient">
    </div>
</div>

CSS:

.circle-gradient {
    color: $medium-red;
    animation: spin 4s linear infinite;
    border-radius: 50%;
    position: relative;
    height: 300px;
    width: 300px;
    background: linear-gradient(60deg, $beige, $light-red);
    border-radius: 50%;
    z-index: -1;
    position: relative;
    &:after{
      content: '';
      position: absolute;
      width: 100%;
      height: 100%;
      background-color: white;
      border-radius: 50%;
      z-index: 999;
      border-inline: 5px solid rgba(0,0,0,0.02);
    }
}

@keyframes spin {
  0% {
    transform: translate(1%, 1%);
  }

  100% {
    transform: translate(1%, 1%) rotate(360deg);
  }
}

test how it works HERE

monny
  • 95
  • 1
  • 7
0

You could use SVG for the gradient arcs like:

* { margin: 0; }

body { background: #1a0a0a; }

.circle {
  position: relative;
  width: 10rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: url("https://i.stack.imgur.com/p2rd3.png") no-repeat center / 60%;
}

.circle svg {
  position: absolute;
  animation: arcRotation 2s linear infinite ;
}

@keyframes arcRotation {
  100% { rotate: 360deg; }
}
<div class="circle">
  <svg id="arc1" viewBox="0 0 1 1">
    <defs>
      <linearGradient id="gradient1" gradientUnits="userSpaceOnUse" x1="1" y1="1" x2="0" y2="1">
        <stop offset=".42" stop-color="#fff" />
        <stop offset=".58" stop-color="#f00" />
      </linearGradient>
    </defs>
    <path stroke="url(#gradient1)" stroke-width="0.01" fill="none" d="M 0.74 0.0843078061834695 A 0.48 0.48 0 0 0 0.2600000000000001 0.08430780618346945"></path>
  </svg>
  <svg id="arc2" viewBox="0 0 1 1">
    <defs>
      <linearGradient id="gradient2" gradientUnits="userSpaceOnUse" x1="1" y1="1" x2="0" y2="1">
        <stop offset=".42" stop-color="#f00" />
        <stop offset=".58" stop-color="#fff" />
      </linearGradient>
    </defs>
    <path stroke="url(#gradient2)" stroke-width="0.01" fill="none" d="M 0.37576685835079 0.9636443966187528 A 0.48 0.48 0 0 0 0.62423314164921 0.9636443966187528"></path>
  </svg>
</div>

PS, the <path> elements d attribute was generated thanks to this snippet:

// https://stackoverflow.com/a/18473154/383904
const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
  const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
};

const describeArc = (x, y, radius, startAngle, endAngle) => {
  const start = polarToCartesian(x, y, radius, endAngle);
  const end = polarToCartesian(x, y, radius, startAngle);
  const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
  const d = [
    "M", start.x, start.y,
    "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
  ].join(" ");
  return d;
};

document.querySelector("#arc1 path").setAttribute("d", describeArc(0.5, 0.5, 0.48, -30, 30));
document.querySelector("#arc2 path").setAttribute("d", describeArc(0.5, 0.5, 0.48, 180 - 15, 180 + 15));
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • thanks this was helpful for adding the outline that is closest to the picture in the middle – Tices Jul 03 '23 at 16:52
  • @Tices glad you found this answer helpful. Just out of curiosity, you accepted the other answer because of the simplicity? – Roko C. Buljan Jul 03 '23 at 20:57
  • No because it solved the problem I was trying to solve which I presented in the question but your answer solved a later problem that I would have to solve . So I accepted it incase someone else has the same question and wants an answer that will solve that specific problem – Tices Jul 03 '23 at 21:20
  • @Tices that's exactly why I'm asking. Try to add to the answer you accepted: `body { background: #1a0a0a; }` or an image if you will... How is that answer fulfilling any of your requirements? 1: There's no gap between the two arc-lines. 2: there's no transparency at all. 3: The two arcs do not look like the suggested image. 4: wobbles while rotating. 5: there's no *opposite* arc. See for yourself: https://jsfiddle.net/v6uks91z/ . I'm just trying to understand what was my answer missing from your requirements. Thanks for any additional comment. – Roko C. Buljan Jul 03 '23 at 21:52