Ant
Ant

Reputation: 271

Make radial-gradient() span two elements

I have a circle made of two semi circular divs. I'd like to use background: radial-gradient() to make the circle appear spherical. How can I do this without overlaying the two semi-circular divs with one circular div?

[The reason for having two semi-circle divs is because of a transition in which the circle splits into two pieces. The reason for not wanting to overlay with a single div is for a similar reason]

.top-semi-circle, .bottom-semi-circle {
  width: 10em;
  height: 5em;
  background: radial-gradient(circle at 100px 100px, red, #000);
}
.top-semi-circle {
  border-radius: 10em 10em 0 0;
}
.bottom-semi-circle {
  border-radius: 0 0 10em 10em;
}
.full-circle {
  width: 10em;
  height: 10em;
  border-radius: 10em;
  background: radial-gradient(circle at 100px 100px, red, #000);
}
Make this:
<div class="top-semi-circle"></div>
<div class="bottom-semi-circle"></div>
Look like this:
<div class="full-circle"></div>

Upvotes: 1

Views: 409

Answers (3)

Temani Afif
Temani Afif

Reputation: 273448

Adjust the second gradient position and most important give a radius to both gradient to avoid having an automatic value that will not be the same since we will have different position and the default value of size is farthest-corner

.top-semi-circle, .bottom-semi-circle {
  width: 10em;
  height: 5em;
}
.top-semi-circle {
  border-radius: 10em 10em 0 0;
  background: radial-gradient(circle 10em at 100px 100px, red, #000);
}
.bottom-semi-circle {
  border-radius: 0 0 10em 10em;
  background: radial-gradient(circle 10em at 100px 20px, red, #000);
}

.bottom-semi-circle:hover {
  transform:translateY(10px);
}
<div class="top-semi-circle"></div>
<div class="bottom-semi-circle"></div>

The radial gradient syntax is:

radial-gradient() = radial-gradient(
  [ <ending-shape> || <size> ]? [ at <position> ]? ,
  <color-stop-list>

<size>

Determines the size of the gradient’s ending shape. If omitted it defaults to farthest-corner. ref

You can also play with background-size/background-position if you want to keep the definition of the gradient the same. Simply give a size equal to the overal shape (top half + bottom half).

.top-semi-circle, .bottom-semi-circle {
  width: 10em;
  height: 5em;
  background-image: radial-gradient(circle at 100px 100px, red, #000);
  background-size:10em 10em;
}
.top-semi-circle {
  border-radius: 10em 10em 0 0;
  background-position:top;
}
.bottom-semi-circle {
  border-radius: 0 0 10em 10em;
  background-position:bottom;
}

.bottom-semi-circle:hover {
  transform:translateY(10px);
}
<div class="top-semi-circle"></div>
<div class="bottom-semi-circle"></div>

Another idea is to consider overlaping with clip-path:

.top-semi-circle, .bottom-semi-circle {
  width: 10em;
  height: 10em;
  border-radius: 10em;
  background: radial-gradient(circle at 100px 100px, red, #000);
}
.top-semi-circle {
  clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
}
.bottom-semi-circle {
  margin-top:-10em;
  clip-path:polygon(0 100%,100% 100%,100% 50%,0 50%);
}
.bottom-semi-circle:hover {
  transform:translateY(10px);
}
<div class="top-semi-circle"></div>
<div class="bottom-semi-circle"></div>

Same logic using mask:

.top-semi-circle, .bottom-semi-circle {
  width: 10em;
  height: 10em;
  border-radius: 10em;
  background: radial-gradient(circle at 100px 100px, red, #000);
}
.top-semi-circle {
  -webkit-mask:linear-gradient(to bottom,white 50%,transparent 0);
  mask:linear-gradient(to bottom,white 50%,transparent 0);
}
.bottom-semi-circle {
  margin-top:-10em;
  -webkit-mask:linear-gradient(to top,white 50%,transparent 0);
  mask:linear-gradient(to top,white 50%,transparent 0);
}
.bottom-semi-circle:hover {
  transform:translateY(10px);
}
<div class="top-semi-circle"></div>
<div class="bottom-semi-circle"></div>

Upvotes: 2

Gildas.Tambo
Gildas.Tambo

Reputation: 22653

Use overflow hidden and pseudo elements

*{box-sizing: border-box}
[class$=circle] {
  width: 10em;
  height: 10em; 
  overflow: hidden;
  position: relative; 
  display: block;
  will-change: transform;
  transition: transform .2s ease
}
[class$=circle]:before {
  content: "";
  position: absolute;
  left: 0;
  width: 10em;
  height: 10em;
  border-radius: 50%;
  background: radial-gradient(circle at 100px 100px, red, #000);
}
[class^=top]:before { 
  top: 50%;
}
[class^=bottom]:before { 
  bottom: 50%;
}
figure{
  width: 10rem;
  height: 10rem;
}
figure:hover [class^=top] {
  transform: translate3d(0, -10px, 0)
}
figure:hover [class^=bottom] {
  transform: translate3d(0, 10px, 0)
}
<figure>
  <div class="top-semi-circle"></div>
  <div class="bottom-semi-circle"></div>
</figure>

Upvotes: 1

Jonas Grumann
Jonas Grumann

Reputation: 10786

This can achieved using calc() to position the radial in the second in the bottom semi-circle. I'm using calc(100px - 5em), because 100px is the offset of the center of the gradient in the top half and 5em is the height of one semi-circle.

EDIT: I also had to specify the size of the gradient to make them match, by default the sizes are different, probably because the distance from the center and the various sides are different.

.top-semi-circle {
  width: 10em;
  height: 5em;
  background: radial-gradient(10em at 100px 100px, red, #000);
}
.bottom-semi-circle {
width: 10em;
  height: 5em;
  background: radial-gradient(10em at 100px calc(100px - 5em), red, #000);
}
.top-semi-circle {
  border-radius: 10em 10em 0 0;
}
.bottom-semi-circle {
  border-radius: 0 0 10em 10em;
}
.full-circle {
  width: 10em;
  height: 10em;
  border-radius: 10em;
  background: radial-gradient(circle at 100px 100px, red, #000);
}
Make this:
<div class="top-semi-circle"></div>
<div class="bottom-semi-circle"></div>
Look like this:
<div class="full-circle"></div>

Upvotes: 1

Related Questions