CSS - How to change background of intersection of two objects?

Viewed 1179

I'm trying to replicate the styling of this animation but I don't know how to "fill in" the background color of the intersection of these two shapes. In the animation, the intersection is conveniently stepwise and stops where the edge of the square intersections with the origin of the circle; I can imagine using a clipping-mask to fill in that quadrant of the circle. However, is it possible to do the same more dynamically? Can you fill in the background of two intersecting shapes (while still having a transparent background otherwhere)?

.shape-interconnected {
     width: 400px;
     height: 300px;
     position: relative;
     background-color: black;
     color: white;
     margin: 1rem;
     border-radius: 4px;
}
 .shape-interconnected > .square, .shape-interconnected > .circle {
     width: 50px;
     height: 50px;
     position: absolute;
     border: 5px solid white;
     transform: translate(-50%, -50%);
}
 .shape-interconnected > .square {
     border-radius: 4px;
     top: 45%;
     left: 55%;
}
 .shape-interconnected > .circle {
     border-radius: 50%;
     top: 55%;
     left: 45%;
}
 
<div class="shape-interconnected">
  <div class="square"></div>
  <div class="circle"></div>
</div>

2 Answers

You can recreate the dribble using html with a little css pseudo and animation magic.

This example below works at any set css variable set size border defined in the root css vars.

:root {
  --size: 250px;
  --border: 5px;
}

The trick in my example is by using positioning as percentages, meaning the parent .shape-interconnected controlled by the css var size, dictates all the child and child pseudo element position.

There is a lot of css to explain here, I've added comments in css, see if this inspires you to get you where you need to go...

Here is a fiddle... https://jsfiddle.net/joshmoto/378Lcgp0/

/* our root css vars */
:root {
  --size: 250px;
  --border: 5px;
}

BODY {
  background: black;
  min-height: 100%;
}

/* reset our box sizing on psuedo elems */
*, ::after, ::before {
    box-sizing: border-box;
}

/* our shape intersect container positioned center of window */
/* this can be positioned where ever you want */
.shape-interconnected {
  background: black;
  width: var(--size);
  height: var(--size);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  animation: shape-interconnected 2s infinite;
}

/* animate height and width equally  */
@keyframes shape-interconnected {
  0% {
    width: var(--size);
    height: var(--size);
  }
  50% {
    width: calc(var(--size) * 0.6);
    height: calc(var(--size) * 0.6);
  }
  100% {
    width: var(--size);
    height: var(--size);
  }
}

/* our square calculated at 40% of parent */
/* position and overflow hidden are key, hiding pseudo child elems */
.shape-interconnected > .square {
  width: calc(var(--size) * 0.4);
  height: calc(var(--size) * 0.4);
  background: transparent;
  position: absolute;
  overflow: hidden;
  right: 0;
  top: 0;
}

/* our square before pseudo elem emulating inner white filled circle */
/* position absolute with animation keyframes */
.shape-interconnected > .square::before {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: #fff;
  border-radius: 50%;
  position: absolute;
  animation: circle-interconnected 2s infinite;
}

/* start top/right 150% away, overflowing out of view */
/* 50% keyframe top/right 50% away, in view */
@keyframes circle-interconnected {
  0% {
    top: 150%;
    right: 150%;
  }
  50% {
    top: 50%;
    right: 50%;
  }
  100% {
    top: 150%;
    right: 150%;
  }
}

/* our square after pseudo elem emulating white border */
.shape-interconnected > .square::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: transparent;
  border: var(--border) solid white;
  position: relative;
}

/* our circle calculated at 40% of parent */
.shape-interconnected > .circle {
  width: calc(var(--size) * 0.4);
  height: calc(var(--size) * 0.4);
  position: absolute;
  bottom: 0;
  left: 0;
}

/* our circle after pseudo elem emulating white border */
.shape-interconnected > .circle::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: transparent;
  border: var(--border) solid white;
  border-radius: 50%;
  position: relative;
}
<div class="shape-interconnected">
  <div class="square"></div>
  <div class="circle"></div>
</div>


Here is another example using the same code above but with these css root var settings...

:root {
  --size: 500px;
  --border: 2px;
}

Live example below...

/* our root css vars */
:root {
  --size: 500px;
  --border: 2px;
}

BODY {
  background: black;
  min-height: 100%;
}

/* reset our box sizing on psuedo elems */
*, ::after, ::before {
    box-sizing: border-box;
}

/* our shape intersect container positioned center of window */
/* this can be positioned where ever you want */
.shape-interconnected {
  background: black;
  width: var(--size);
  height: var(--size);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  animation: shape-interconnected 2s infinite;
}

/* animate height and width equally  */
@keyframes shape-interconnected {
  0% {
    width: var(--size);
    height: var(--size);
  }
  50% {
    width: calc(var(--size) * 0.6);
    height: calc(var(--size) * 0.6);
  }
  100% {
    width: var(--size);
    height: var(--size);
  }
}

/* our square calculated at 40% of parent */
/* position and overflow hidden are key, hiding pseudo child elems */
.shape-interconnected > .square {
  width: calc(var(--size) * 0.4);
  height: calc(var(--size) * 0.4);
  background: transparent;
  position: absolute;
  overflow: hidden;
  right: 0;
  top: 0;
}

/* our square before pseudo elem emulating inner white filled circle */
/* position absolute with animation keyframes */
.shape-interconnected > .square::before {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: #fff;
  border-radius: 50%;
  position: absolute;
  animation: circle-interconnected 2s infinite;
}

/* start top/right 150% away, overflowing out of view */
/* 50% keyframe top/right 50% away, in view */
@keyframes circle-interconnected {
  0% {
    top: 150%;
    right: 150%;
  }
  50% {
    top: 50%;
    right: 50%;
  }
  100% {
    top: 150%;
    right: 150%;
  }
}

/* our square after pseudo elem emulating white border */
.shape-interconnected > .square::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: transparent;
  border: var(--border) solid white;
  position: relative;
}

/* our circle calculated at 40% of parent */
.shape-interconnected > .circle {
  width: calc(var(--size) * 0.4);
  height: calc(var(--size) * 0.4);
  position: absolute;
  bottom: 0;
  left: 0;
}

/* our circle after pseudo elem emulating white border */
.shape-interconnected > .circle::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: transparent;
  border: var(--border) solid white;
  border-radius: 50%;
  position: relative;
}
<div class="shape-interconnected">
  <div class="square"></div>
  <div class="circle"></div>
</div>

You might add a white circle inside the square and position it to the same coords the transparent one has.
Set overflow: hidden to the square to hide the outside part of the white circle:

.shape-interconnected {
  width: 400px;
  height: 300px;
  position: relative;
  background-color: black;
  color: white;
  margin: 1rem;
  border-radius: 4px;
  --animation-props: 1s alternate linear infinite;
}

.shape-interconnected>.square,
.shape-interconnected>.square:before,
.shape-interconnected>.circle {
  width: 50px;
  height: 50px;
  position: absolute;
  border: 5px solid white;
  transform: translate(-50%, -50%)
}

.shape-interconnected>.square {
  top: 35%;
  left: 65%;
  border-radius: 4px;
  overflow: hidden;
  animation: for_square var(--animation-props);
}

.shape-interconnected>.circle {
  top: 65%;
  left: 35%;
  border-radius: 50%;
  animation: for_transparent_circle var(--animation-props);
}

.shape-interconnected>.square:before {
  content: '';
  border-radius: 50%;
  background: #fff;
  top: 230%;
  left: -190%;
  animation: for_white_circle var(--animation-props);
}

@keyframes for_square {
  to {
    top: 50%;
    left: 55%;
  }
}

@keyframes for_transparent_circle {
  to {
    top: 55%;
    left: 50%;
  }
}

@keyframes for_white_circle {
  to {
    top: 80%;
    left: 10%;
  }
}
<div class="shape-interconnected">
  <div class="square"></div>
  <div class="circle"></div>
</div>

Related