2022-10-22

使用 CSS 实现 iMessage 气泡

我观察过多个社交软件的聊天气泡,觉得最好看的还是 iMessage 的气泡,端庄且优雅。

本文会简单分析不使用任何素材用 CSS 实现 iMessage 气泡,只会实现基本的外观形状,不会考虑所有的细节。形状方面实现的难点明显在于气泡的箭头部分,在继续之前,先说明几个简单的 CSS 属性。border-radiusborder-bottom-left-radiusborder-bottom-right-radius。第一个属性很常见,作用就是设置元素四个角的圆角;第二个 border-bottom-left-radius 作用也是设置元素圆角,不过仅控制元素左下角的圆角;第三个控制的是右下角的圆角。

根据第二个和第三个 CSS 属性名可以推测可能还存在 border-top-left-radiusborder-top-right-radius 分别控制左上角和右上角的圆角,是的,这两个属性存在,但在本文中不会用到。

简单了解 border-bottom-left-radiusborder-bottom-right-radius 后就可以实现气泡的箭头了。气泡箭头的原理其实很简单,就是两个不同背景颜色,不同圆角的元素使用 position 叠加在一起。

一图胜千言:

下面是气泡箭头的具体实现,你可以直接复制代码到一个新的 HTML 文件中,然后在浏览器预览。

<div class="container">
  <div class="item">
    <div class="before"></div>
    <div class="after border"></div>
  </div>
  <div class="item">
    <div class="before"></div>
    <div class="after"></div>
  </div>
</div>
<style>
  .container {
    display: flex;
    align-items: center;
    justify-content: space-around;
    overflow: hidden;
    height: 176px;
  }

  .item {
    position: relative;
    height: 64px;
    width: 64px;
  }

  .before {
    position: absolute;
    bottom: 0;
    right: 0;
    height: 64px;
    width: 64px;
    border-bottom-left-radius: 75px;
    background-color: #007aff;
  }

  .after {
    position: absolute;
    bottom: -4px;
    right: 0;
    height: 70px;
    width: 32px;
    border-bottom-left-radius: 50px;
    background-color: white;
    z-index: 1;
  }

  .border {
    border: 2px solid black;
  }
</style>

知道了气泡箭头的实现原理剩下的就比较简单了,无非就是一个圆角矩形和气泡箭头拼接在一起。这里使用 beforeafter 伪元素实现气泡箭头部分。

<div class="bubbles">
  <div class="bubble">
    <div class="bubble-content">
      <div class="msg">最最喜欢你,绿子。</div>
    </div>
  </div>
  <div class="bubble left">
    <div class="bubble-content">
      <div class="msg">什么程度?</div>
    </div>
  </div>
  <div class="bubble">
    <div class="bubble-content">
      <div class="msg">像喜欢春天的熊一样。</div>
      <div class="state">已送达</div>
    </div>
  </div>
</div>

<style>
  :root {
    font-size: 16px;
  }

  .bubbles {
    max-width: 448px;
    margin: 0 auto;
  }

  .bubble {
    display: flex;
    justify-content: flex-end;
  }

  .bubble.left {
    justify-content: flex-start;
  }

  .bubble-content {
    margin-bottom: 4px;
    max-width: 75%;
  }

  .msg {
    position: relative;
    border-radius: 20px;
    padding: 6px 12px;
    font-size: 1rem;
    line-height: 1.5rem;
    background-color: #007aff;
    color: white;
  }

  .left .msg {
    background-color: #e6e5e8;
    color: black;
  }

  .msg::before {
    content: "";
    position: absolute;
    bottom: 0;
    height: 20px;
    width: 20px;
    background-color: inherit;
    right: -8px;
    border-bottom-left-radius: 15px;
  }

  .left .msg::before {
    left: -8px;
    right: unset;
    border-bottom-left-radius: unset;
    border-bottom-right-radius: 15px;
  }

  .msg::after {
    content: "";
    position: absolute;
    bottom: -1px;
    z-index: 1;
    height: 21px;
    width: 10px;
    background-color: white;
    right: -10px;
    border-bottom-left-radius: 10px;
  }

  .left .msg::after {
    left: -10px;
    right: unset;
    border-bottom-left-radius: unset;
    border-bottom-right-radius: 10px;
  }

  .state {
    text-align: right;
    font-size: 12px;
    line-height: 16px;
    color: #888;
  }
</style>

此时的效果:

最最喜欢你,绿子。
什么程度?
像喜欢春天的熊一样。
已送达

到此,我们已经大致实现了 iMessage 气泡的外观。

参考

  1. vercel.com (Wayback Machine 归档)

更改日志

日期更改内容
2024-08-19修改文章措辞;更新实例代码;使用元素替换图片。