学完这篇,你能不用任何图片和 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,改主题、做响应式都省事。