⭐ 推荐:社区规则条款 V1.0

CSS 三角形/箭头/气泡框纯 CSS 实现大全

yipeng
yipeng 正式会员正式会员认证极客认证极客
发布于 2026-10-15 17:35 ·3 浏览 ·2 回复
内容摘要

纯 CSS 画三角形、箭头和气泡框的方法包括用零宽高盒子的 border 透明边做出实心三角、用 clip-path 裁剪出可渐变可动画的任意三角、按 √3÷2 计算等边三角形高度、用旋转的双边框正方形做折线箭头,并需注意 border 小数取整、clip-path 不改变点击热区与占位、旋转后位置偏移等坑。

学完这篇,你能不用任何图片和 SVG,纯靠 CSS 画出各种方向、各种大小的三角形、箭头和带尖角的气泡框,并且知道每种写法什么时候该用、坑在哪里。

第一步:先搞懂 border 三角形的原理

一个宽高都是 0 的盒子,四条边框会以 45° 斜接在四个角上,看起来就是四个三角形拼成的方块。把其中三条边的颜色设成 transparent,剩下的那条边框就是一个三角形。

.triangle-up {
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-bottom: 16px solid #333;
}

方向对照:朝上给 border-bottom,朝下给 border-top,朝右给 border-left,朝左给 border-right。左右边框宽度控制底边长度,上下边框宽度控制高。

注意:width: 0; height: 0 不能省。如果宽高不为 0,你得到的是梯形而不是三角形——这本身也是做梯形的好办法,但别指望它出尖角。

第二步:用 clip-path 做更自由的三角形

clip-path: polygon() 可以直接把矩形裁成任意形状,支持渐变背景、背景图,也能做动画。

.triangle {
  width: 20px;
  height: 16px;
  background: linear-gradient(#4a9, #1a6);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

想调角度就改坐标,比如 polygon(30% 0, 100% 100%, 0 100%) 就是一个偏斜的三角形。

注意:clip-path 只是视觉裁剪,元素的实际盒子还是原尺寸,点击热区和布局占位都按矩形算。如果外面的空白碍事,把宽高设成紧贴三角形的最小尺寸。

第三步:算准等边三角形

等边三角形的高 = 边长 × √3 ÷ 2 ≈ 边长 × 0.866。边长 20px 时,就是左右边框各 10px、下边框 17.32px:

.tri {
  width: 0; height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-bottom: 17.32px solid #333;
}

注意:小数边框宽度在不同设备像素比下可能被取整,看起来会差 1px。对精度要求高(比如做图形化指示器)时,改用 clip-path 或直接上 SVG。

第四步:箭头(折线型尖角)

箭头不是实心三角,而是两条边拼出的「>」。做法是画一个只有上边框和右边框的正方形,再旋转 45°:

.arrow-right {
  width: 10px; height: 10px;
  border-top: 2px solid #333;
  border-right: 2px solid #333;
  transform: rotate(45deg);
}

旋转角度对应:右 45°、下 135°、左 225°、上 315°。border-width 是线粗,宽高是箭头大小。

注意:旋转以元素中心为原点,转完位置会偏移,通常配合 transform-origin 或负 margin 微调;批量使用时建议写四个方向修饰类,别在现场手调。

第五步:气泡框的两种主流写法

做法 A:border 三角形伪元素(最轻量,推荐)

.bubble {
  position: relative;
  background: #333; color: #fff;
  padding: 8px 12px; border-radius: 6px;
}
.bubble::after {
  content: "";
  position: absolute; left: 50%; top: 100%;
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: #333;
  border-bottom: 0;
}

做法 B:正方形旋转 45°(尖角能带边框、能跟圆角气泡对齐)

.bubble::after {
  content: "";
  position: absolute; left: 50%; bottom: -5px;
  width: 10px; height: 10px;
  background: inherit;
  transform: translateX(-50%) rotate(45deg);
}

注意:做法 B 的小方块只露出两个角,如果主框有边框,你得单独给方块的下边和右边加边框,并且让它盖住主框那条底边,否则交界处会露出一条横线。做法 A 反过来——它天生无法带边框,只能靠颜色匹配。

第六步:气泡要有阴影,用 drop-shadow 不要用 box-shadow

box-shadow 是跟着矩形盒子走的,实心三角后面会莫名其妙多一条阴影边。改用 filter: drop-shadow(),它会沿元素真实轮廓(含伪元素)投影:

.bubble { filter: drop-shadow(0 2px 4px rgba(0,0,0,.2)); }

注意:filter 会创建新的包含块,气泡内部的 position: fixed 元素会定位错乱;同时 filter 会让内部 backdrop-filter 失效。两者都用到时,把投影挪到外层包装元素上。

第七步:让箭头颜色和尺寸好维护

写死颜色后期改起来很痛。三角颜色用 currentColor 或 CSS 变量,尺寸用 em 跟着字号缩放:

.bubble {
  --bubble-bg: #333;
  --bubble-size: .5em;
  background: var(--bubble-bg);
}
.bubble::after {
  border-width: var(--bubble-size);
  border-top-color: var(--bubble-bg);
}

这样气泡字号一变,箭头自动跟着变,移动端不用再写一套。

第八步:常见坑排查清单

  • 三角形边缘发虚/有锯齿:多半是半像素渲染,尽量用整数尺寸,避免 .5px 的偏移。
  • 气泡箭头和主框之间有 0.5px 缝隙:让伪元素往主框内叠 1px(例如 bottom: -5px 改成 -4.5px)。
  • 箭头在深色背景上看不见:检查是不是用了 transparent 以外的颜色,或者父元素 overflow: hidden 把箭头裁掉了。
  • 三角位置对不齐:优先用 left: 50% + translateX(-50%),不要用 margin-left 手算一半宽度(宽度一变就废)。

小结

  • 实心三角首选 border 三边透明法,原理是宽高为 0 时四条边框 45° 斜接。
  • 需要渐变、任意角度、精准等边时改用 clip-path: polygon()。
  • 箭头 = 两条相邻边框 + rotate(45deg),四个方向靠角度区分。
  • 气泡尖角两种做法:border 三角(轻量、不能带边框)与旋转小方块(能带边框和圆角、要注意遮盖底边线)。
  • 带阴影的气泡必须用 filter: drop-shadow(),box-shadow 会跟着矩形盒子跑。
  • 颜色和尺寸尽量走 CSS 变量与 em,改主题、做响应式都省事。
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-839.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 2

zjlxcf
zjlxcf 正式会员正式会员认证极客认证极客 1楼 2026-10-15 17:45

总结得挺系统,border 原理那块讲得没问题,补几个实战里最容易翻车的点。

带边框的气泡框必须用双伪元素叠三角。 单三角只能配无边框气泡,一旦气泡本身有 border,尖角那截边框就断了。做法是外层三角用边框色、内层三角用背景色,错开 1px 盖上去:

.bubble { position:relative; background:#fff; border:1px solid #ccc; }
.bubble::before, .bubble::after {
  content:""; position:absolute; top:20px; border:8px solid transparent;
}
.bubble::before { left:-16px; border-right-color:#ccc; }  /* 深色做边 */
.bubble::after  { left:-15px; border-right-color:#fff; }  /* 浅色盖 1px */

clip-path 有个坑:box-shadow 会被一起裁掉,因为裁切在绘制之后生效。想要阴影就换 filter: drop-shadow(0 2px 4px rgba(0,0,0,.2)),它是按最终轮廓算的,能贴住尖角。另外 border 三角形没法做圆角尖角,要圆角只能上 clip-path 或 SVG,这点第三步里也提到了。

第三个坑是抗锯齿。 border 三角的斜边在低 DPI 屏幕上容易有锯齿,transparent 在某些叠层/阴影场景还会透出一点灰边,这时可以试试写 rgba(0,0,0,0),或者干脆改 clip-path——它的边缘平滑度明显更好,代价是刚才说的阴影问题,两者算是各有取舍。

最后提一句,帖子末尾好像被截断了(折线箭头那段停在 bor),135°/225°/315° 那几个对应关系没写完,方便的话补一下,这块正好和 border-top+border-right 的组合方向容易记混。

做个坏人啦
做个坏人啦 正式会员正式会员认证极客认证极客 #786 2楼 2026-10-15 17:52
zjlxcf:总结得挺系统,border 原理那块讲得没问题,补几个实战里最容易翻车的点。 **带边框的气泡框必须用双伪元素叠三角。** 单三角只能配无边框气泡,一旦气泡本…

先把你要的那部分补齐——折线箭头的旋转角度对照是:右 45deg、下 135deg、左 225deg、上 315deg,即从右开始顺时针每档 +90°。基础形状是「上边框 + 右边框」的正方形,它天然指向右上(也就是 45° 方向的尖角),所以往右只需要再补 45°。

.arrow-down  { transform: rotate(135deg); }
.arrow-left  { transform: rotate(225deg); }
.arrow-up    { transform: rotate(315deg); }

记忆上别硬背角度,记「基础形=右上,右=+45,顺时针累加」就够了。顺带两个实战坑:一是旋转后的元素盒子会外扩到原尺寸的 √2 倍,父级若有 overflow: hidden 或固定高度,尖角会被削掉一块,记得留 padding 或放行 overflow;二是 45° 斜线在非整数像素上容易发虚,加个 translateZ(0) 提到合成层会干净一些。

你提的三点基本都认同,补两条细节。rgba(0,0,0,0) 和 transparent 规范上其实完全等价(都是透明黑),真正起作用的是把透明端写成与实色同色相的值,比如三角是 #333 就写 rgba(51,51,51,0),这样斜边抗锯齿混出来的半透明像素仍落在同一色系里,灰边就没了。另外双伪元素那套,注意父级别带 overflow: hidden,否则外挂的三角直接消失——这是气泡框最常见的「代码没错但不显示」。

最后一个延伸:如果箭头要渐变或半透明,别用 border-top + border-right 旋转,直接 clip-path: polygon(0 0, 50% 50%, 0 100%, 25% 100%, 75% 50%, 25% 0) 做「>」形,配 filter: drop-shadow() 一样能出阴影。