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

discord.py开发:使用Pillow实现保留圆角的经验进度条裁剪咨询

保留进度条圆角的动态长度实现方案

问题根因

  • 你当前使用的整体resize方案,会把进度条首尾固定像素宽度的圆角区域一同等比缩放,当经验值较低、整体宽度被压得很小时,圆角的弧度会被拉平变成直角,完全失去原图的圆角效果
  • 之前用crop没生效,基本都是坐标参数错配导致的:Pillow的crop()方法接收的参数格式是(左边界x, 上边界y, 右边界x, 下边界y),必须用相对原图的绝对像素坐标,顺序颠倒或者数值小于边界值时,会返回空的透明图,看起来就像裁剪没生效

正确实现思路

不要对整张进度条图做缩放,而是拆分进度条的固定结构,通过裁剪拼接生成长度适配经验值的进度条,全程保留原图像素,不会出现圆角变形:

  1. 先测量进度条的三段固定边界
    打开你的progress-bar.png,记录三个固定宽度值:
    • 左圆角段宽度:从图片最左侧开始,到圆角完全结束、进入纯填充/纹理段的x坐标,记为LEFT_RADIUS_W
    • 右圆角段宽度:从图片最右侧反向测量,到圆角完全结束、进入纯填充/纹理段的x坐标,记为RIGHT_RADIUS_W
    • 中间填充段:就是左右两个圆角之间的区域,这部分没有圆角,自由裁剪长度不会影响视觉效果
  2. 按经验占比计算目标进度条总宽度
    沿用你原来的计算逻辑:目标总宽度 = 进度条原图总宽度 * 当前经验值 / 升级所需经验值
  3. 按目标宽度裁剪拼接进度条
    经验值极低、总宽度小于两个圆角段宽度和时,只保留左段圆角的裁剪结果;经验值正常时,分别裁出左固定圆角段、对应长度的中间填充段、右固定圆角段,横向拼接成最终的进度条

可直接复用的修正代码

from PIL import Image

# 需根据自己的progress-bar.png测量调整的固定参数
PROGRESS_IMG_PATH = "progress-bar.png"
LEFT_RADIUS_W = 12   # 替换为你实测的左圆角宽度
RIGHT_RADIUS_W = 12  # 替换为你实测的右圆角宽度

# 读取完整进度条原图
full_bar = Image.open(PROGRESS_IMG_PATH)
full_bar_w, full_bar_h = full_bar.size

# 计算当前需要的进度条总宽度
progress_ratio = exp / lev
target_total_w = round(full_bar_w * progress_ratio)

# 生成适配长度的进度条
if target_total_w <= LEFT_RADIUS_W:
    # 经验极低的极端情况:只裁左段圆角,不露出右圆角
    final_bar = full_bar.crop((0, 0, target_total_w, full_bar_h))
else:
    # 分别提取固定的左右圆角段
    left_radius = full_bar.crop((0, 0, LEFT_RADIUS_W, full_bar_h))
    right_radius = full_bar.crop((full_bar_w - RIGHT_RADIUS_W, 0, full_bar_w, full_bar_h))
    
    # 计算中间填充段需要的长度
    middle_need_w = target_total_w - LEFT_RADIUS_W
    # 判断是否能放下完整的右圆角
    can_hold_right = middle_need_w >= (full_bar_w - LEFT_RADIUS_W - RIGHT_RADIUS_W)

    if can_hold_right:
        # 可以放下完整右圆角:裁取全段中间填充部分
        middle_part = full_bar.crop((LEFT_RADIUS_W, 0, full_bar_w - RIGHT_RADIUS_W, full_bar_h))
        # 横向拼接三个部分
        final_bar = Image.new("RGBA", (target_total_w, full_bar_h))
        final_bar.paste(left_radius, (0, 0))
        final_bar.paste(middle_part, (LEFT_RADIUS_W, 0))
        final_bar.paste(right_radius, (target_total_w - RIGHT_RADIUS_W, 0))
    else:
        # 总长度放不下完整右圆角:只裁对应长度的中间填充部分,不贴右圆角
        middle_part = full_bar.crop((LEFT_RADIUS_W, 0, LEFT_RADIUS_W + middle_need_w, full_bar_h))
        final_bar = Image.new("RGBA", (target_total_w, full_bar_h))
        final_bar.paste(left_radius, (0, 0))
        final_bar.paste(middle_part, (LEFT_RADIUS_W, 0))

# 粘贴到等级卡时传入alpha通道作为蒙版,避免圆角边缘出现黑边
levelcard.paste(final_bar, (440, 376), final_bar)
levelcard.save("profile2.png")
await ctx.reply(file=discord.File("profile2.png"))

常见踩坑提醒

  • 粘贴半透明圆角的进度条时,一定要把进度条本身的alpha通道作为第三个参数传给paste()方法,不然半透明边缘会出现难看的黑边
  • 如果进度条中间是渐变纹理,不要拉伸纹理段,直接从原图的纹理段里裁取对应长度的原像素即可,视觉上不会有违和感
  • 如果裁出来的边缘有杂色,可以在测量左右圆角宽度时,向外多取1-2像素的边界,避免把圆角的抗锯齿像素裁掉

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 03:36:05