(转)如何实现 Cut-Out Effect

原文地址:Thinking About The Cut-Out Effect: CSS or SVG?

Cut-Out Effect,就是 月牙效果,或者叫,咬一口效果

 1. User Avatar 月牙效果

1. 1 - Clip Path

Pros
  Cross-browser, works on all major versions of Chrome, Edge, Firefox, and Safari.
  Good for very basic examples. It can get complex with borders or shadows.
Cons
  To remove the cut-out effect, we need to change the path. This can be hard to do in a component with different states.
  It needs some experience in dealing with merging shapes in a design app.

<div class="avatar">
  <img class="item-2" src="https://ishadeed.com/assets/shadeed.jpg" alt="" />
  <span class="badge"></span>
</div>

<svg class="svg">
  <defs>
    <clipPath id="my-clip-path" clipPathUnits="objectBoundingBox">
      <path d="M0.5,0 C0.776,0,1,0.224,1,0.5 C1,0.603,0.969,0.7,0.915,0.779 C0.897,0.767,0.876,0.76,0.853,0.76 C0.794,0.76,0.747,0.808,0.747,0.867 C0.747,0.888,0.753,0.908,0.764,0.925 C0.687,0.972,0.597,1,0.5,1 C0.224,1,0,0.776,0,0.5 C0,0.224,0.224,0,0.5,0"></path>
    </clipPath>
  </defs>
</svg>
.avatar {
    position: relative;
    --size: 175px;
    width: var(--size);
    height: var(--size);
    margin: 1rem auto;

    &:after {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        border-radius: 50%;
        border: 1px solid;
        opacity: 0.2;
        clip-path: url("#my-clip-path");
    }
}
  
.item-2 {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 50%;
    clip-path: url("#my-clip-path");
}

*,
*:before,
*:after {
    box-sizing: border-box;
}

1.2 - CSS Mask

Pros
  Cross-browser, but needs the vendor prefix all browsers except Firefox.
Cons
  Can be limited or complex in other examples

<img class="item" src="https://ishadeed.com/assets/shadeed.jpg" alt="" />
.item {
  display: block;
  width: 200px;
  height: 200px;
  -webkit-mask-image: radial-gradient(
    circle 20px at calc(100% - 30px) calc(100% - 30px),
    transparent 30px,
    #000 0
  );
  object-fit: cover;
  border-radius: 50%;
  margin: 2rem auto;
}

*,
*:before,
*:after {
  box-sizing: border-box;
}

1.3 - SVG Mask

Pros
  A simple solution.
  Great cross-browser support.
  Can be maintained.

Cons
  I can’t think of any cons for this solution except that it can be a bit hard for a person who doesn’t know SVG

<div class="test-2">
  <svg role="none">
    <mask id="test2">
      /* white black 做差集,white white 做并集,类似于css的mask-composite */
      <circle fill="white" cx="100" cy="100" r="100"></circle>
      <circle fill="black" cx="86%" cy="86%" r="18"></circle>
    </mask>
    <g mask="url(#test2)">
      <image x="0" y="0" height="100%" preserveAspectRatio="xMidYMid slice" width="100%" xlink:href="https://ishadeed.com/assets/shadeed.jpg"></image>
      <circle fill="none" cx="100" cy="100" r="100" stroke="rgba(0,0,0,0.1)" stroke-width="2"></circle>
    </g>
  </svg>
</div>
.test-2 {
  width: 200px;
  height: 200px;
  margin: 2rem auto;

  svg {
    width: 100%;
    height: 100%;
  }

  img {
    max-width: 100%;
  }
}

2. Seen Avatars 头像重叠

2.1 - css mask

/* 椭圆裁切,30.5用于消除锯齿 */
.item {
    -webkit-mask-image: radial-gradient(ellipse 54px 135px at 11px center, #0000 30px, #000 30.5px);
}

/* 椭圆图片,渐变全背景,两个mask进行差集裁切 */
.item {
    -webkit-mask-image: url(oval.svg), linear-gradient(#000, #000);
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-position: -26px 54%, 100% 100%;;
    -webkit-mask-size: 80px 140px, 100% 100%;
    mask-composite: exclude;
    -webkit-mask-composite: destination-out;
}

2.2 - SVG

核心思路,svg两个圆差集,black圆cx用负值,盖住white圆,white圆差去和black圆重叠部分,就形成月牙

<svg role="none" class="avatar-wrapper">
    <mask id="cut">
        <circle cx="50" cy="50" r="50" fill="white"></circle>
        <circle fill="black" cx="-30" cy="50" r="50"></circle>
    </mask>
    <g mask="url(#cut)">
        <image x="0" y="0" height="100%" width="100%" xlink:href="shadeed.jpg"></image>
        <circle fill="none" stroke="rgba(0,0,0,0.1)" stroke-width="2"></circle>
    </g>
</svg>

详细可参考

完整代码

<div class="wrapper">
  <div class="card">
    <div class="card__content">
      <div class="seen-group">
        <svg role="none" class="avatar-wrapper">
          <style>
            <style>.avatar-wrapper circle {
              cx: calc(var(--size) / 2);
              cy: calc(var(--size) / 2);
              r: calc(var(--size) / 2);
            }
          </style>
          </style>
          <mask id="test2">
            <circle fill="white"></circle>
          </mask>
          <g mask="url(#test2)">
            <image x="0" y="0" height="100%" preserveAspectRatio="xMidYMid slice" width="100%" xlink:href="https://ishadeed.com/assets/shadeed.jpg"></image>
            <circle fill="none" stroke="rgba(0,0,0,0.1)" stroke-width="2"></circle>
          </g>
        </svg>
        <svg role="none" class="avatar-wrapper">
          <style>
            <style>.avatar-wrapper circle {
              cx: calc(var(--size) / 2);
              cy: calc(var(--size) / 2);
              r: calc(var(--size) / 2);
            }
          </style>
          </style>
          <mask id="test3">
            <circle fill="white"></circle>
            <circle fill="black" cx="-30"></circle>
          </mask>
          <g mask="url(#test3)">
            <image x="0" y="0" height="100%" preserveAspectRatio="xMidYMid slice" width="100%" xlink:href="https://ishadeed.com/assets/shadeed.jpg"></image>
            <circle fill="none" stroke="rgba(0,0,0,0.1)" stroke-width="2"></circle>
          </g>
        </svg>
      </div>
    </div>
  </div>
.seen-group {
  display: flex;

  > * + * {
    margin-left: calc(var(--size) / 5.5 * -1);

    circle[fill="black"] {
      cx: calc(var(--size) / 4 * -1);
      cy: calc(var(--size) / 2);
      r: calc(var(--size) / 2);
    }
  }
}

.wrapper {
  max-width: 344px;
  margin: 2rem auto;
}

.card {
  --primary-text: #050505;
  --secondary-text: #65676b;
  --bg-color: #fff;

  position: relative;
  background-color: var(--bg-color);
  padding: 8px 12px 8px 8px;
  border-radius: 7px;
  box-shadow: 0 3px 15px 0 rgba(#000, 0.05);
}

.card__content {
  display: flex;
  justify-content: center;
}

.avatar-wrapper {
  --size: 100px;
  width: var(--size);
  height: var(--size);
}

body {
  font-family: "Helvetica";
  line-height: 1.4;
  padding: 1rem;
  background-color: #f7f7f7;
}

*,
*:before,
*:after {
  box-sizing: border-box;
}

3. Website Header

3.1 - CSS Radial Gradient

background用radial-gradient挖洞,logo盖在上面即可

:root {
    --header-height: 100px;
    --radius: clamp(48px, 4vw, 60px);
    --logo-size: calc(calc(var(--radius) * 2) - 8px);
}

.logo {
    display: block;
    position: relative;
    top: calc(var(--header-height) * 0.7 - var(--radius) + 2px);
    width: var(--logo-size);
    margin-left: auto;
    margin-right: auto;
}

.site-header {
  background: radial-gradient(
    circle at 50% 70%,
    rgba(0, 0, 0, 0) var(--radius),
    #95a57f var(--radius),
    #95a57f 100%
  );
}

详细可参考

3.2 - SVG Mask

svg挖个洞,作为mask,盖住header即可

<header class="site-header">
    <img src="assets/logo.svg" alt="" />
    <svg role="none" height="80">
        <defs>
            <mask id="hole">
                <rect width="100%" height="100%" fill="white" />
                <circle cx="50%" cy="80%" r="58" fill="black"></circle>
            </mask>
        </defs>

        <rect width="100%" height="100%" mask="url(#hole)" />
    </svg>
</header>
.site-header {
    position: relative;
}

.site-header svg {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}

详细可参考

posted @ 2023-06-29 19:10  全玉  阅读(24)  评论(0编辑  收藏  举报