You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中绘制带尾巴的聊天风格气泡?

实现可定制的带尾巴Canvas聊天气泡

核心实现思路

先绘制带圆角的气泡主体,再根据指定的对齐方向绘制尾巴(支持左/右/上/下四种对齐),最后填充背景色并绘制文本,所有样式参数均可灵活调整。

完整代码示例

function drawChatBubble(ctx, options) {
  // 解构参数,设置默认值
  const {
    x, y, width, height,
    padding = 12,
    tailAlign = 'left',
    tailSize = 10,
    bgColor = '#ECECEC',
    borderRadius = 8,
    text = '',
    textColor = '#000',
    fontSize = 14
  } = options;

  // 计算文本内容区域(扣除内边距)
  const contentX = x + padding;
  const contentY = y + padding;
  const maxTextWidth = width - padding * 2;

  ctx.save();
  ctx.beginPath();

  // 绘制气泡主体:圆角矩形
  ctx.moveTo(x + borderRadius, y);
  ctx.lineTo(x + width - borderRadius, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + borderRadius);
  ctx.lineTo(x + width, y + height - borderRadius);
  ctx.quadraticCurveTo(x + width, y + height, x + width - borderRadius, y + height);
  ctx.lineTo(x + borderRadius, y + height);
  ctx.quadraticCurveTo(x, y + height, x, y + height - borderRadius);
  ctx.lineTo(x, y + borderRadius);
  ctx.quadraticCurveTo(x, y, x + borderRadius, y);

  // 绘制不同对齐方向的尾巴
  if (tailAlign === 'left') {
    ctx.moveTo(x, y + height - borderRadius - tailSize);
    ctx.lineTo(x - tailSize, y + height - borderRadius);
    ctx.lineTo(x, y + height - borderRadius + tailSize);
  } else if (tailAlign === 'right') {
    ctx.moveTo(x + width, y + height - borderRadius - tailSize);
    ctx.lineTo(x + width + tailSize, y + height - borderRadius);
    ctx.lineTo(x + width, y + height - borderRadius + tailSize);
  } else if (tailAlign === 'top') {
    ctx.moveTo(x + borderRadius + tailSize, y);
    ctx.lineTo(x + borderRadius, y - tailSize);
    ctx.lineTo(x + borderRadius - tailSize, y);
  } else if (tailAlign === 'bottom') {
    ctx.moveTo(x + borderRadius + tailSize, y + height);
    ctx.lineTo(x + borderRadius, y + height + tailSize);
    ctx.lineTo(x + borderRadius - tailSize, y + height);
  }

  ctx.closePath();
  ctx.fillStyle = bgColor;
  ctx.fill();

  // 绘制气泡文本
  ctx.fillStyle = textColor;
  ctx.font = `${fontSize}px sans-serif`;
  ctx.textBaseline = 'top';
  ctx.fillText(text, contentX, contentY, maxTextWidth);

  ctx.restore();
}

// 调用示例
const canvas = document.getElementById('chatCanvas');
const ctx = canvas.getContext('2d');

// 左侧尾巴气泡
drawChatBubble(ctx, {
  x: 50,
  y: 50,
  width: 220,
  height: 70,
  tailAlign: 'left',
  bgColor: '#ECECEC',
  text: '这是一条左侧带尾巴的聊天消息'
});

// 右侧尾巴气泡
drawChatBubble(ctx, {
  x: 300,
  y: 150,
  width: 190,
  height: 60,
  tailAlign: 'right',
  bgColor: '#007AFF',
  textColor: '#fff',
  text: '这是一条右侧带尾巴的回复消息'
});

参数说明

  • x/y: 气泡左上角的起始坐标
  • width/height: 气泡整体宽高(包含内边距)
  • padding: 气泡内边距,默认12px
  • tailAlign: 尾巴对齐方向,可选left/right/top/bottom,默认left
  • tailSize: 尾巴的尺寸,默认10px
  • bgColor: 气泡背景色,默认#ECECEC
  • borderRadius: 气泡圆角半径,默认8px
  • text: 气泡内的文本内容
  • textColor: 文本颜色,默认#000
  • fontSize: 文本字号,默认14px

效果优化提示

  • 若需要更圆润的尾巴,可将三角形尾巴替换为贝塞尔曲线绘制,例如左侧尾巴修改为:
    ctx.moveTo(x, y + height - borderRadius - tailSize);
    ctx.quadraticCurveTo(x - tailSize/2, y + height - borderRadius, x, y + height - borderRadius + tailSize);
    
  • 多行文本支持:可通过拆分文本为多行,循环调用fillText实现换行显示。

内容的提问来源于stack exchange,提问作者Yerdaulet Abdurakhman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:08:32