CSS 的 S 形曲线形状

Sigmoid Curve Shape with CSS

我想为全屏布局创建一个类似 sigmoid curve 的形状,在一侧显示装饰图案背景,在另一侧显示纯色背景,以便文本显示放在它上面。

目标是拥有一个全屏页面,其左上角填充有图案,页面的其余部分只有白色背景。

JSFiddle:Unfinished sigmoid curve

#container {
  padding-top: 10%;
  padding-bottom: 10%;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 35%;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-radius: 100px;
  -webkit-border-radius: 100px;
  border-radius: 100px;
  -moz-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
<div id="container">
  <div id="parallelogram">
  </div>
</div>

我不知道如何在左下角附近创建(或模拟)倒圆角。

或者可能有概念上不同(更好)的解决方案可用?

更新: 我想出了如何完全用 CSS 创建 shape I need

#container {
  padding-top: 100px;
  background: red;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 400px;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-top-left-radius: 100px;
  -webkit-top-left-border-radius: 100px;
  border-top-left-radius: 100px;
}
#bottom {
  height: 200px;
  width: 100%;
  background: white;
}
#bottom-corner {
  height: 100px;
  width: 300px;
  margin-left: -34px;
  background: red;
  -moz-border-bottom-right-radius: 100px;
  -webkit-bottom-right-border-radius: 100px;
  border-bottom-right-radius: 100px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
}
<div id="container">
  <div id="parallelogram">
  </div>

  <div id="bottom">
    <div id="bottom-corner">
    </div>
  </div>
</div>

然而,这仍然不是最终的解决方案,因为形状不允许我使用我想要的那种背景效果。这是我尝试时发生的情况:fiddle.

#container {
  padding-top: 100px;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) bottom left / 26px 32px repeat;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 400px;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-top-left-radius: 100px;
  -webkit-top-left-border-radius: 100px;
  border-top-left-radius: 100px;
  -moz-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
#bottom {
  height: 200px;
  width: 100%;
  background: white;
  -moz-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
#bottom-corner {
  height: 100px;
  width: 300px;
  margin-left: -34px;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  -moz-border-bottom-right-radius: 100px;
  -webkit-bottom-right-border-radius: 100px;
  border-bottom-right-radius: 100px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  -moz-box-shadow: 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: 0 0 15px rgba(0, 0, 0, .4);
}
<div id="container">
  <div id="parallelogram">
  </div>

  <div id="bottom">
    <div id="bottom-corner">
    </div>
  </div>
</div>

稍后更新: 经过反复试验,我最终得到了一个 ridiculously crude hack solution,达到了我需要的视觉效果:

#container {
  padding-top: 100px;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 385px;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-top-left-radius: 100px;
  -webkit-top-left-border-radius: 100px;
  border-top-left-radius: 100px;
  -moz-box-shadow: inset 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
#bottom-rounded-corner {
  height: 122px;
  position: relative;
  width: 200%;
  z-index: 1000;
  margin-top: -80px;
  margin-left: -185px;
  background: url(http://famok.com/wp-content/uploads/2016/11/CornerAndMask.png) top left no-repeat;
}
#bottom-white {
  height: 100px;
  width: 100%;
  background: white;
}
<div id="container">
  <div id="parallelogram">
  </div>
  <div id="bottom-rounded-corner">

  </div>
  <div id="bottom-white">
  </div>
</div>

尽我所能尝试实现下面 Harry 建议的概念上更好的替代方案,但我无法使用它来创建我想要的效果。如果有人可以提供帮助,无论是通过展示如何去做,还是通过对我的解决方案提出优化建议,我仍然会很感激。

提前致谢!

对复杂形状使用 SVG 而不是 CSS:

正如我在评论中提到的,请不要使用 CSS 来创建如此复杂的形状。 SVG 是此类复杂内容的推荐工具。 SVG 易于创建和维护,默认情况下它们也是响应式的(可扩展的),因此它有很多优点。


创建 S 形:

使用 SVG 本身创建 S 形曲线非常简单,只需要一个路径元素:

  • M0,750 将虚构的笔移动到靠近 SVG 元素的左下角(坐标设置为略低于 SVG 的高度,以便在底部有一个间隙,阴影将可见)。
  • L250,750 从点 (0,768) 到 (250,768)
  • 生成水平 L 直线
  • C650,730 500,154 1024,154 创建实际曲线。这里前两个坐标是曲线的控制点((650,730),(500,154)),第三个坐标是终点(1024,154)。可以通过修改控制点来调整曲线的曲率
  • L1024,0 0,0 0,750 用于完成形状。形状需要完整才能进行填充。

body {
  margin: 0;
}
svg {
  width: 100%;
  height: 100vh;
}
<svg viewBox='0 0 1024 768' preserveAspectRatio='none'>

  <!-- For the shadow -->
  <defs>
    <filter id="dropShadow">
      <feGaussianBlur in="SourceAlpha" stdDeviation="6" />
      <feOffset dx="3" dy="3" result="offsetBlur" />
      <feFlood flood-color="#AAA" flood-opacity="1" result="offsetColor" />
      <feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur" />
      <feMerge>
        <feMergeNode />
        <feMergeNode in="SourceGraphic" />
      </feMerge>
    </filter>
  </defs>
  <!-- End of shadow -->

  <!-- For filling the top-left with pattern -->
  <pattern id='dots' patternUnits='userSpaceOnUse' width='25' height='25'>
    <polygon points='0,0 0,25 25,25 25,0' fill='yellowgreen' />
    <circle cx='12.5' cy='12.5' r='4' fill='rebeccapurple' />
  </pattern>
  <!-- End of pattern -->

  <!-- Actual sigmoid curve -->
  <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,0 0,0 0,750' fill='url(#dots)' filter='url(#dropShadow)' />

</svg>


将图案应用于形状:

在上面的演示中,我使用 polygoncircle 元素创建了一个圆点图案,但在 SVG 本身中创建它不是强制性的,我们也可以使用 image 元素并用图像图案填充形状。

如果您想将背景图片(图案)更改为您选择的另一张图片,只需在 image 标签的 xlink:href 属性中指定您图片的 URL就像下面的代码片段一样。根据您的需要和图像,您可能需要更改 patternimageheightwidth

<pattern id='dots' patternUnits='userSpaceOnUse' width='25' height='25'>
  <image xlink:href='https://yourwebsite.com/yourpath' x='0' y='0' width='15' height='15' />
</pattern>

body {
  margin: 0;
}
svg {
  width: 100%;
  height: 100vh;
}
<svg viewBox='0 0 1024 768' preserveAspectRatio='none'>
  <defs>
    <filter id="dropShadow">
      <feGaussianBlur in="SourceAlpha" stdDeviation="6" />
      <feOffset dx="3" dy="3" result="offsetBlur" />
      <feFlood flood-color="#AAA" flood-opacity="1" result="offsetColor" />
      <feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur" />
      <feMerge>
        <feMergeNode />
        <feMergeNode in="SourceGraphic" />
      </feMerge>
    </filter>
    <pattern id='dots' patternUnits='userSpaceOnUse' width='36.6' height='46'>
      <image xlink:href='http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg' x='0' y='0' width='36.6' height='46' />
    </pattern>
  </defs>
  <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,0 0,0 0,750' fill='url(#dots)' filter='url(#dropShadow)' />
</svg>

注意:以上演示中使用的图片不是我自己的。转自网络


阴影:

阴影效果是使用 SVG filter 元素以及 feGaussianBlurfeOffsetfeMerge 元素创建的。 feGaussianBlur element blurs the source graphic (our sigmoid) by the specified standard deviation value and the feOffset offsets the resulting image by the dx, dy values. The original image and the blurred one are the merged using feMerge. The feFlood and the feComposite are added just in case you want to give the shadow a different color. The colors can be specified using the flood-color and flood-opacity attributes. (The method for changing the SVG drop-shadow's color was taken from this answer 作者:Joe W.)


添加文本:

现在这是整个事情中真正棘手的部分。如果您只需要将文本放置在页面的纯色区域,那么您需要谨慎使用定位属性。如果文本很小或只有一行文本,那么我们可以像我之前链接的演示中那样使用 SVG text 元素本身。如果不是,那么您必须确保文本的容器框不会重叠到 S 形区域。

body {
  margin: 0;
}
div.container {
  position: relative;
  width: 100%;
  height: 100vh;
}
svg {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
}
.container div {
  position: absolute;
  top: 50%;
  right: 0px;
  height: 30vh;
  width: 33.33%;
  font-size: 20px;
}
<div class='container'>
  <svg viewBox='0 0 1024 768' preserveAspectRatio='none'>

    <defs>
      <!-- For the shadow -->
      <filter id="dropShadow">
        <feGaussianBlur in="SourceAlpha" stdDeviation="6" />
        <feOffset dx="3" dy="3" result="offsetBlur" />
        <feFlood flood-color="#AAA" flood-opacity="1" result="offsetColor" />
        <feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur" />
        <feMerge>
          <feMergeNode />
          <feMergeNode in="SourceGraphic" />
        </feMerge>
      </filter>
      <!-- End of shadow -->

      <!-- For filling the top-left with pattern -->
      <pattern id='dots' patternUnits='userSpaceOnUse' width='25' height='25'>
        <polygon points='0,0 0,25 25,25 25,0' fill='yellowgreen' />
        <circle cx='12.5' cy='12.5' r='4' fill='rebeccapurple' />
      </pattern>
      <!-- End of pattern -->
    </defs>

    <!-- Actual sigmoid curve -->
    <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,0 0,0 0,750' fill='url(#dots)' filter='url(#dropShadow)' />

  </svg>
  <div>Hello! Here is some text that is placed on the solid colored area of the page.</div>
</div>

我们可以尝试使用 CSS shape-outside 属性 但是 browser support for that is pretty poor at the moment. Here is a demo using this shape-outside property. Could not host it in-site because it needed creating a separate SVG file. The demo is an adapted version of the one provided in the W3C CSS Shapes Spec.


替代方法:(将图案应用于容器而不是 SVG)

由于您不希望图像被压扁或拉伸,另一种方法是执行以下操作:

  • 创建 SVG 形状,使其只有纯色部分(而不是图案区域)
  • 将模式应用到 div.container,然后将 SVG 绝对放在元素的顶部。 SVG 形状(具有白色填充)将防止图案在另一侧可见。
  • 将 SVG 上的阴影从普通投影更改为嵌入阴影。 (Inset Shadow的代码完全取自here.

body {
  margin: 0;
}
div.container {
  position: relative;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  width: 100%;
  height: 100vh;
}
svg {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
}
.container div {
  position: absolute;
  top: 50%;
  right: 0px;
  height: 30vh;
  width: 33.33%;
  font-size: 20px;
}
<div class='container'>
  <svg viewBox='0 0 1024 768' preserveAspectRatio='none'>
    <defs>
      <filter id="dropShadow" x="-50%" y="-50%" width="200%" height="200%">
        <feComponentTransfer in=SourceAlpha>
          <feFuncA type="table" tableValues="1 0" />
        </feComponentTransfer>
        <feGaussianBlur stdDeviation="6" />
        <feOffset dx="2" dy="2" result="offsetblur" />
        <feFlood flood-color="#AAA" result="color" />
        <feComposite in2="offsetblur" operator="in" />
        <feComposite in2="SourceAlpha" operator="in" />
        <feMerge>
          <feMergeNode in="SourceGraphic" />
          <feMergeNode />
        </feMerge>
      </filter>
    </defs>
    <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,768 0,768 0,750' fill='white' filter='url(#dropShadow)' />
  </svg>
  <div>Hello! Here is some text that is placed on the solid colored area of the page.</div>
</div>

这里是 Plunker Demo 的替代方法。这比前一个稍微复杂一些,因为这里我们需要一个 SVG 来生成纯色区域(用作 img),另一个 SVG 是用于 [=46] 的反转(图案区域) =].