Draw double curved item with beveled edges

2020-03-24 08:57发布

I'm creating a website, with a header / menu on top. In the middle, there is a logo. To accentuate this, I've absolute positioned an ellips below the logo: So underneath, you see the ellips, which blends in perfectly with the rest of the menu.

.ellips {
        border-radius: 50%;
        height: 130%;
        background: white;
        left: 0px;
        right: 0px;
        z-index: 1;
        position: absolute;
        top: 6%;
        border: 2px solid #dfe0e4;
    }

Absolutely positioned ellips

Now the client wants to see the left and right 'corner' also curved:

enter image description here

I know that the 'shove an ellips beneath the menu' approach won't suffice. I was thinking about 2 possible solutions:

  1. Create an SVG with the curve, and place it beneath the menu. I've tried that solution, but I'm no SVG expert, so I went to try a CSS approach:

  2. Try to create CSS item (div) with such curve: above it's white, below it's transparent. Put dat item below the menu.

I prefer a css solution, as I'm no big hero in SVG. But if it can be achieved much easier with SVG. Then I'd take that solution off course.

Edit: The blue-ish background you see, is an underlaying image. So this needs to be transparant.

The ellips needs to be colored white.

enter image description here

标签: html css svg
4条回答
欢心
2楼-- · 2020-03-24 09:09

I created 2 SVG examples so you can choose where to apply the background

Codepen demo


The outer container of each SVG element keeps a specific aspect ratio so the whole element can be responsive (but, of course, you can also specify a fixed width or height).

The basic idea is to create a path that overflows the size of the SVG element, so you can define a closed shape on the top area or on the bottom area, in order to fill it with a colour (if you enlarged the viewbox e.g. to -10 -10 610 130 you could see how the path is actually defined).

The applied background is a gradient but you can also specify a single color-stop (white, in your specific scenario). The background on the body element shows the transparent parts of the SVG.

Fine tuning and adjustment of curves, viewbox, colours is left to the reader.

For any change to the shape you can read the path documentation on MDN

Markup

<div class="doublecurve">
  <svg viewBox="0 0 600 120" xmlns="http://www.w3.org/2000/svg">

    <defs>
      <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
        <stop offset="0%"   stop-color="#d8e5f1" />
        <stop offset="100%" stop-color="#91b4d3" />
      </linearGradient>
    </defs>

    <path class="concave" fill="url(#gradient)" d="M-2 2 
            L75 2 A75 75 0 0 1 110 20 C200 100 400 100 480 20
            A75 75 0 0 1 525 2 L602 2 L602 122 L-2 122 z"/>
  </svg>
</div>



<div class="doublecurve">
  <svg viewBox="0 0 600 120" xmlns="http://www.w3.org/2000/svg">

    <defs>
      <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
        <stop offset="0%"   stop-color="#d8e5f1"/>
        <stop offset="100%" stop-color="#91b4d3"/>
      </linearGradient>
    </defs>

    <path class="concave" fill="url(#gradient)" d="M-2 2 
            L75 2 A75 75 0 0 1 110 20 C200 100 400 100 480 20
            A75 75 0 0 1 525 2 L602 2 L602 -2 L-2-2"/>
  </svg>
</div>

CSS

.doublecurve {
  width: 100%;
  height: 0;
  margin: 20px 0;
  border: 1px dashed #bc9;
  padding-bottom: 20%;
  position: relative; }

.doublecurve svg { 
  position: absolute;
  width: 100%; height: 100%;}

.doublecurve path.concave { 
  stroke: #d0d0d0; 
  stroke-width: 4px;}

Final Result

enter image description here

查看更多
Emotional °昔
3楼-- · 2020-03-24 09:09

SVG would be the way to go for such things but for a CSS solution I would probably use multiple background with linear/radial-gradient BUT the drawback is that it can be difficult to calculate the different values and make the whole shape responsive.

Here is an example that can help you get some idea:

body {
  background:grey;
}

.header {
  border: 5px solid red;
  border-top: none;
  height: 100px;
  width: 600px;
  background: 
  radial-gradient(ellipse at -7px 26px, transparent 50%, red 50%, red calc(50% + 11px), white 0%) 101px -3px/7% 19% no-repeat, 
  radial-gradient(ellipse at 60px 26px, transparent 50%, red 50%, red calc(50% + 11px), white 0%) 454px -4px/7% 19% no-repeat, 
  radial-gradient(ellipse at top, white 20%, red 20%, red calc(21% + 2px), transparent 0%) 50% 0/200% 200% no-repeat,
  linear-gradient(red 50%,transparent 0%) 0 0/100% 10px no-repeat, 
  linear-gradient(to right, gray, blue);
}
.header-tr {
  border: 5px solid red;
  margin-top:20px;
  border-top: none;
  height: 100px;
  width: 600px;
  background: 
  radial-gradient(ellipse at -7px 26px, transparent 50%, red 50%, red calc(50% + 11px), white 0%) 101px -3px/7% 19% no-repeat, 
  radial-gradient(ellipse at 60px 26px, transparent 50%, red 50%, red calc(50% + 11px), white 0%) 454px -4px/7% 19% no-repeat, 
  radial-gradient(ellipse at top, white 20%, red 20%, red calc(21% + 2px), transparent 0%) 50% 0/200% 200% no-repeat,
  linear-gradient(red 50%,transparent 0%) 0 0/110px 10px no-repeat,
  linear-gradient(red 50%,transparent 0%) 100% 0/110px 10px no-repeat;
}
<div class="header">

</div>

<div class="header-tr">

</div>

And if you are open to use multiple element you can rely on pseudo element and some border-radius but you have also to manage a lot of element:

body {
  background: gray;
}

.header {
  margin-top: 30px;
  border: 5px solid red;
  border-top: none;
  height: 100px;
  position: relative;
  overflow: auto;
}

.top {
  position: absolute;
  top: -40px;
  right: 80px;
  left: 80px;
  height: 80px;
  border: 5px solid red;
  border-top: none;
  border-radius: 0 0 50% 50%;
  background: #fff;
}

.top:before {
  content: "";
  position: absolute;
  top: 23px;
  right: calc(100% - 11px);
  left: -80px;
  border-top: 18px solid #fff;
  border-radius: 0 50% 0 0;
  border-bottom: 0;
  border-left: 0;
  height: 52px;
  z-index: 0;
}

.top:after {
  content: "";
  position: absolute;
  top: 23px;
  left: calc(100% - 11px);
  right: -80px;
  border-top: 18px solid #fff;
  border-radius: 50% 0 0 0;
  border-bottom: 0;
  border-right: 0;
  height: 52px;
  z-index: 0;
}

.header:before {
  content: "";
  position: absolute;
  top: 0;
  right: calc(100% - 88px);
  left: 0;
  border-top: 5px solid red;
  border-radius: 0 50% 0 0;
  border-bottom: 0;
  border-left: 0;
  height: 25px;
  z-index: 99;
}

.header:after {
  content: "";
  position: absolute;
  top: 0;
  left: calc(100% - 88px);
  right: 0;
  border-top: 5px solid red;
  border-radius: 50% 0 0 0;
  border-bottom: 0;
  border-right: 0;
  height: 25px;
  z-index: 99;
}
<div class="header">
  <div class="top"></div>
</div>

And here is an SVG solution:

<svg
  xmlns='http://www.w3.org/2000/svg'
  viewBox='0 0 75 50'
  width='600' height='300'
  fill='transparent'>
  <path d='M0 24 L64 24 L64 2 L58 2 C36 2 46 10 32 10 C18 10 26 2 4 2 L0 2 Z' stroke="red" stroke-width="1" />
</svg>

查看更多
我命由我不由天
4楼-- · 2020-03-24 09:31

Here is an inline-SVG solution with a quadratic bezier curve.
The svg image has 500x50 pixels dimensions and is absolute positioned into the header.

.your-header {
  border-top: 2px solid #dfe0e4;
  position: relative;

  /* These two are just for demo */
  background-color: lightblue;
  height: 75px;
}

svg.ellipse {
  height: 50px;
  width: 500px;
  /* Centers the svg horizontal */
  left: 50%;
  margin-left: -250px;
  position: absolute;
  /* 2px up due to the parents border */
  top: -2px;
}

svg.ellipse > .border-clear{
  /* A white line to cover up the parents border */
  stroke: white;
  stroke-width: 3px;
}

svg.ellipse>.wave {
  stroke: #dfe0e4;
  stroke-width: 2px;
  fill: white;
}
<div class="your-header">
  <svg class="ellipse">
    <path d="M0 0 H499" class="border-clear" />
    <path d="M0 1 Q 62.5 1, 125 26 T 250 49 T 375 26 T 500 1" class="wave"/>
  </svg>
</div>

查看更多
淡お忘
5楼-- · 2020-03-24 09:33

Almost there ... it could get you in the way.

I used ::before and ::after on the header to add two curve.

Play a little with the values of the radius to get what you want.

The ellips has a fixed width, so this is as responsive as possible. When resizing the screen, the corners won't break but the ellip's size won't change.

html,
body {
  margin: 0;
  padding: 0;
}

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

body {
  background: linear-gradient(to right, #d2e1f1, #86acd0);
}

header {
  height: 100px;
  background: white;
  position: relative;
}
header::before, header::after {
  content: "";
  display: block;
  width: calc(50% - 80px);
  position: absolute;
  top: 95px;
  height: 80px;
  border: 6px solid #dadbe0;
  border-right-color: transparent;
  border-left-color: transparent;
  border-bottom: 0;
  z-index: 3;
}
header::before {
  left: 0px;
  border-radius: 0px 140px 0px 0px/0px 60px 0px 0px;
}
header::after {
  right: 0px;
  border-radius: 140px 0px 0px 0px/60px 0px 0px 0px;
}

.ellips {
  background: white;
  height: 120px;
  width: 300px;
  position: absolute;
  bottom: -40px;
  z-index: -1;
  border-radius: 0px 0px 90% 90%/0px 0px 90px 90px;
  left: 50%;
  transform: translate(-50%);
  border: 6px solid #dadbe0;
}

.masks {
  background: white;
  height: 9px;
  width: 330px;
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  border-radius: 0px 0px 90% 90%/0px 0px 90px 90px;
  bottom: -8px;
}
<header>
  <div class="ellips">
  </div>
  <div class="masks"></div>
</header>

查看更多
登录 后发表回答