如何通过CSS创建一个带有倾斜一侧和圆角的盒子?

17

我目前需要做类似这样的东西。

Enter image description here

我最初的想法是使用clip-path,但圆角比较难实现,并且由于按钮内部内容的变化会导致22.5度难以维持。

所以最终我选择将每个按钮制作成两个divs,一个div被倾斜了22.5度并与常规矩形div重叠。然后我给两者都添加了边框半径。

body {
  line-height: 0;
  font-size: 16px;
  background-color: black;
}

.cta-button-group {
  display: flex;
  gap: 2rem;
  align-items: center;
}

.button-angular-wrapper-left {
  display: flex;
  isolation: isolate;
  position: relative;
  height: 40px;
  width: fit-content;
}

.button-angular-wrapper-left .button-angular-main {
  border-radius: 7px 0 0 7px;
  height: 100%;
  display: inline-grid;
  place-items: center;
  padding-inline: 8px 16px;
  margin-right: 13px;
  transition: background-color 50ms;
}

.button-angular-wrapper-left .button-angular-slant {
  border-radius: 0 7px 7px 0;
  height: 100%;
  width: 24px;
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: -1;
  transition: background-color 50ms;
}

.button-angular-wrapper-left .button-angular-slant.back-slash {
  transform: skewX(22.5deg);
}

.button-angular-wrapper-left .button-angular-slant.forward-slash {
  transform: skewX(-22.5deg);
}

.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-main,
.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-slant {
  background: white;
  border: 3px solid white;
  color: blue;
}

.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-main {
  border-right: none;
}

.button-angular-wrapper-left.button-angular-color-solid-white .button-angular-slant {
  border-left: none;
}

.button-angular-wrapper-right {
  display: flex;
  isolation: isolate;
  position: relative;
  height: 40px;
  width: fit-content;
}

.button-angular-wrapper-right .button-angular-main {
  border-radius: 0 7px 7px 0;
  height: 100%;
  display: inline-grid;
  place-items: center;
  padding-inline: 8px 16px;
  margin-left: 13px;
}

.button-angular-wrapper-right .button-angular-slant {
  border-radius: 7px 0 0 7px;
  height: 100%;
  width: 24px;
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: -1;
}

.button-angular-wrapper-right .button-angular-slant.back-slash {
  transform: skewX(22.5deg);
}

.button-angular-wrapper-right .button-angular-slant.forward-slash {
  transform: skewX(-22.5deg);
}

.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main,
.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-slant {
  border: 3px solid white;
}

.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main {
  border-left: none;
}

.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main .icon-call {
  color: white;
}

.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-main .cta-text {
  color: white;
}

.button-angular-wrapper-right.button-angular-color-outline-white .button-angular-slant {
  border-right: none;
}
<div class="cta-button-group">
  <div class="button-angular-wrapper-left button-angular-color-solid-white" href="">
    <div class="button-angular-main">
      <span class="cta-text">
        Learn More Today
      </span>
    </div>
    <div class="button-angular-slant back-slash">
    </div>
  </div>
  <div class="button-angular-wrapper-right button-angular-color-outline-white" href="">
    <div class="button-angular-main">
      <span class="cta-text tel-link-no">
        1800-1-5555
      </span>
    </div>
    <div class="button-angular-slant back-slash">
    </div>
  </div>
</div>

CodePen: https://codepen.io/katylar/pen/yLRjKaO

它能工作,但并不完美。我注意到在某些分辨率下的某些浏览器上会出现明显的伪影和奇怪的角落/边缘。

有没有一个好的解决方案?不需要使用遮罩(我总是很难调整大小)?


我认为可以使用伪元素 :before 来实现这个功能。 - Apodemus
1
@Apodemus 我相信原帖作者已经知道伪元素的存在。问题是如何使用它。 - rishi
1
我现在已经发布了一个答案,并且说明了我如何使用伪元素来完成它。 - Apodemus
我建议将这个问题的名称改为“*如何使用CSS创建一个带有圆角的直角梯形*”,因为“一个带有角度的盒子”有点含糊不清。 - InSync
4个回答

14

我尝试过使用伪元素的方法来实现这个形状。这个形状的左侧是一个::before元素,为了实现悬停效果,我将按钮和伪元素的特定边缘设置为不可见,并且还改变了特定角落的边框半径。

.button {
  color: white;
  background-color: black;
  text-align: center;
  text-transform: uppercase;
  padding: 5px 10px;
  margin: 11px;
  display: inline-block;
  border-radius: 4px;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border: 2px solid black;
  -ms-transform: skewX(-20deg);
  -webkit-transform: skewX(-20deg);
  transform: skewX(-20deg);
}

.button-left::before {
  content: " ";
  display: block;
  position: absolute;
  top: -2px;
  left: -12px;
  z-index: -10;
  background-color: black;
  width: 20px;
  height: 100%;
  -ms-transform: skewX(20deg);
  -webkit-transform: skewX(20deg);
  transform: skewX(20deg);
  border-radius: 4px;
  border: 2px solid black;
}

.button-left:hover {
  background: rgba(0,0,0,0);
  box-sizing: border-box;
  border: 2px solid black;
  border-left: 2px solid rgba(0,0,0,0);
  color: black;
  border-top-left-radius: 0;
}

.button-left:hover::before {
  border-right: 2px solid rgba(0,0,0,0);
  background: rgba(0,0,0,0);
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.button-content {
  -ms-transform: skewX(20deg);
  -webkit-transform: skewX(20deg);
  transform: skewX(20deg);  
  display: inline-block;
}
<div class="button button-left">
  <span class="button-content">Slanted Button</span>
</div>


1
不是与答案无关,但是你现在不需要再使用rgba了,rgb可以接受相同的参数(包括alpha值)。 - rishi
1
哦,那很酷,并且它有广泛的支持(除了IE,但现在使用IE相当罕见)。 - Apodemus
4
从技术上讲,伪元素是::before,而不是:before。规范说明:是伪类,::是伪元素。不幸的是,最初的规范没有做出这个区别,因此大多数浏览器都接受:::——用于原始伪元素。但为了与后来的伪元素保持一致(通常必须使用::),我们应该遵循规范,除非您特别需要支持旧版浏览器(我不知道IE是否支持::)。 - KRyan
2
不错!为了明确起见,我已经点赞了,但我很感谢更新。 - KRyan
1
您可以通过在 .button 上设置左上角和左下角半径为零,然后调整 ::before 元素的位置来移除此伪类。 - Arthur Shlain
显示剩余3条评论

9

如果你完全反对口罩,一个建议是使用 perspective 并将一个 div 在 3D 中旋转以给出一个倾斜的元素。

使用一个容器并使其 display: inline-block,这样倾斜的 div 总是在右侧。当您在 X 轴上旋转它时,它会向右后方倾斜。我们在容器中创建一个新的堆叠上下文来隐藏父级下面的最后一个 div。使用 CSS 自定义属性确保最终的 div 与父级的宽度匹配。个人而言,我会使用口罩,但这也可以用于具有实心背景的元素,特别是对于不喜欢口罩的人。我不知道如何处理边框,因为边框宽度也会随着 transform: rotateX() 属性而改变。对于不同高度和宽度的元素,它也有点脆弱,但这可能是一个有用的起点。

无论如何,请看看您的想法。

body {
  background: black;
  color: black;
}

.container {
  --height: 4rem;
  perspective: 100px;
  isolation: isolate;
  display: inline-flex;
}

.main,
.angle {
  display: inline-flex;
  align-items: center;
  padding-left: 1rem;
  background-color: white;
  border-radius: 0.5rem;
}

.main {
  height: var(--height);
  width: 200px;
}

.angle {
  position: relative;
  right: 30px;
  width: 30px;
  transform-origin: top center;
  transform: rotateX(20deg);
  height: calc(var(--height) * 0.96);
  z-index: -1;
}
<div class="container">
  <div class='main'>Learn more today!</div><div class="angle"></div>
</div>


5

这个版本不需要任何额外的HTML标记;它会自动应用适当的斜线效果,使用伪元素。它还可以与任意数量的按钮一起使用,并且仅在元素之间添加斜线,使第一个元素没有左斜线,最后一个元素没有右斜线。

它还使用了几个CSS变量,以允许根据给定的用例进行自定义。

.slant-between:not(:first-child) {
  margin-top: 2rem;
}

.slant-between {
  white-space: nowrap;
  --border-width: 0.25rem;
  --border-radius: 0.5rem;
  --bg-color: black;
  --fg-color: white;
  --skew-angle: 25deg;
  --height: 2rem;
}

.slant-between>* {
  box-sizing: border-box;
  position: relative;
  z-index: 0;
  border: transparent;
  background: transparent;
  color: var(--fg-color);
  padding: 0 1rem;
  height: var(--height);
  line-height: var(--height);
  font-weight: bold;
  cursor: pointer;
}
.slant-between>:hover {
  color: var(--bg-color);
}
.slant-between>::before {
  content: ' ';
  position: absolute;
  z-index: -1;
  background: var(--bg-color);
  border: var(--border-width) solid var(--bg-color);
  border-radius: var(--border-radius);
  transform: skew(var(--skew-angle));
  top: 0;
  bottom: 0;
}
.slant-between>:not(:first-child)::before {
  left: 0;
}
.slant-between>:not(:last-child)::before {
  right: 0;
}
.slant-between>:hover::before {
  background: var(--fg-color);
}

.slant-between>:first-child::before,
.slant-between>:first-child::after,
.slant-between>:last-child::before,
.slant-between>:last-child::after {
  --skew-tangent: tan(var(--skew-angle));
  --abs-skew-tangent: max(var(--skew-tangent), -1 * var(--skew-tangent));
  --safe-overlap-width: calc(var(--height)/2 * var(--abs-skew-tangent) + var(--border-radius))
}
.slant-between>:first-child::before {
  left: var(--safe-overlap-width);
}
.slant-between>:last-child::before {
  right: var(--safe-overlap-width);
}
.slant-between>:first-child::before {
  border-left: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.slant-between>:last-child::before {
  border-right: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.slant-between>:first-child::after, .slant-between>:last-child::after {
  content: ' ';
  position: absolute;
  z-index: -2;
  top: 0;
  bottom: 0;
  background: var(--bg-color);
  border: var(--border-width) solid var(--bg-color);
}
.slant-between>:first-child:hover::after, .slant-between>:last-child:hover::after {
  background: var(--fg-color);
}
.slant-between>:first-child::after {
  left: 0;
  right: var(--safe-overlap-width);
  border-right: 0;
  border-top-left-radius: var(--border-radius);
  border-bottom-left-radius: var(--border-radius);
}
.slant-between>:last-child::after {
  right: 0;
  left: var(--safe-overlap-width);
  border-left: 0;
  border-top-right-radius: var(--border-radius);
  border-bottom-right-radius: var(--border-radius);
}
<div class="slant-between">
  <button>Foo</button>
  <button>Bar</button>
  <button>Baz</button>
</div>

<div class="slant-between" style="--skew-angle: 15deg; --bg-color: green; --fg-color: blue; padding: 0.5rem; background: blue">
  <button></button>
  <button></button>
  <button></button>
  <button></button>
  <button></button>
</div>

<div class="slant-between" style="--skew-angle: -45deg">
  <button style="--bg-color: pink">Hearts</button>
  <button style="--bg-color: yellow; --fg-color: black">Stars</button>
  <button style="--bg-color: purple">Horseshoes</button>
  <button style="--bg-color: green">Clovers</button>
  <button style="--bg-color: blue">Blue Moons</button>
  <button style="--bg-color: gold">Pots of Gold</button>
  <button style="--bg-color: linear-gradient(
        90deg,
        rgba(255, 0, 0, 1) 0%,
        rgba(255, 154, 0, 1) 10%,
        rgba(208, 222, 33, 1) 20%,
        rgba(79, 220, 74, 1) 30%,
        rgba(63, 218, 216, 1) 40%,
        rgba(47, 201, 226, 1) 50%,
        rgba(28, 127, 238, 1) 60%,
        rgba(95, 21, 242, 1) 70%,
        rgba(186, 12, 248, 1) 80%,
        rgba(251, 7, 217, 1) 90%,
        rgba(255, 0, 0, 1) 100%
    )">Rainbows</button>
  <button style="--bg-color: red">A Red Balloon</button>
</div>

它可以处理各种倾斜角度、颜色方案、边框宽度和半径等。

这段代码的主要限制是你需要在 CSS 中定义按钮的高度,使用 --height 变量。在很多情况下,关于 --height 的三角函数可以用估计值直接硬编码处理——有很大的活动余地。

同时,它实际上不适用于除了纯色以外的背景,正如你在“Rainbows”示例中看到的那样,因为你不能使用渐变或图像等作为文本或边框颜色。但我仍然觉得尝试一下很有趣。我从 @Bartek's answer 上得到了彩虹渐变。


请注意,如果浏览器缩放不是100%,该方法会导致伪影。 - Brian
由于操作系统级别的缩放,在4K显示器上也会导致图像伪影。 - RiverTam
@Brian 很好的意见;我改进了方法来解决这个问题,并添加了一堆变量进行调整。 - KRyan
@RiverTam 不错的观点;已经彻底改进了方法来解决这个问题,并添加了一堆变量以进行调整。 - KRyan
在不同的缩放级别下,现在在 Edge 上看起来很不错。 - Chris Akridge

4
这个问题让我想起了20年前Eric Meyercss/edge中的slantastic
无论如何,当我试图解决导致在KRyan原始答案上缩放时出现"伪像"的故障时,我创建了这种方法。我最初使用了嵌套的div,然后是span,但由于结果相同,我又回到了伪元素。
我希望使用相对长度而不是绝对长度,并使用逻辑属性而不是物理属性。在Chrome/Edge中,我取得了一些进展,但在Firefox中没有。
虽然我认为这并不比KRyan重新设计的答案更好,但目前获得更多赞的其他答案未能显示所请求的框的第二部分,这并不像简单地颠倒第一部分那样(根据我的测试)。

.slanted {
  --border-width: .25em;
  display: flex;
  justify-content: center;
  padding: 1em;
  font-family: sans-serif;
  background-color: #2a63b2;
  white-space: nowrap;
}

.slanted div {
  position: relative;
  margin-inline: 1em;
  padding: .5em;
  border: var(--border-width) solid white;
}

.slanted .first {
  color: #2a63b2;
  background-color: white;
  border-radius: .4em 1em 0 .4em;
  z-index: 0;
}

.slanted .second {
  padding-inline-start: 0;
  color: white;
  background-color: #2a63b2;
  border-inline-start-width: 0;
  border-radius: 0 .4em .4em .3em;
}

.slanted .first::after,
.slanted .second::before {
  content: ' ';
  position: absolute;
  top: calc(-1 * var(--border-width));
  bottom: calc(-1 * var(--border-width));
  width: 2em;
  border: var(--border-width) solid white;
  transform: skew(25deg);
}

.slanted .first::after {
  right: -.5em;
  border-radius: 0 .4em .4em 0;
  background-color: white;
  z-index: -1;
}

.slanted .second::before {
  left: -.7em;
  border-inline-end-width: 0;
  border-radius: .4em 0 0 .4em;
}

.slanted .second:hover {
  color: #2a63b2;
  background-color: white;
}

.slanted .second:hover::before {
  background-color: white;
  clip-path: polygon(0 0, 60% 0, 35% 50%, 35% 100%, 0 100%);
}
</style>
<div class="slanted">
  <div class="first">Learn More Today!</div>
  <div class="second">1800-1-5555</div>
</div>


Eric Meyer:不要与Erik Meijer混淆(函数式编程专家,他有很多演讲,在演讲中他穿着一件奇怪的彩色衬衫,批评敏捷开发)。 - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接