fix(SharePoster): 严格裁剪封面图避免溢出到信息区
修改封面图裁剪逻辑,根据源图与目标区域的纵横比选择水平或垂直裁剪 添加封面区域样式限制,防止内容溢出
Showing
1 changed file
with
33 additions
and
7 deletions
| ... | @@ -365,15 +365,35 @@ async function compose_poster() { | ... | @@ -365,15 +365,35 @@ async function compose_poster() { |
| 365 | rounded_rect_path(ctx, card_x, card_y, card_w, card_h, card_radius) | 365 | rounded_rect_path(ctx, card_x, card_y, card_w, card_h, card_radius) |
| 366 | ctx.clip() | 366 | ctx.clip() |
| 367 | 367 | ||
| 368 | - // 封面图(对象填充) | 368 | + // 封面图(严格裁剪到封面区域,避免溢出到信息区) |
| 369 | const cover_img = await load_image(cover_src.value) | 369 | const cover_img = await load_image(cover_src.value) |
| 370 | if (cover_img) { | 370 | if (cover_img) { |
| 371 | - const scale = Math.max(card_w / cover_img.width, cover_h / cover_img.height) | 371 | + const sw = cover_img.width |
| 372 | - const dw = cover_img.width * scale | 372 | + const sh = cover_img.height |
| 373 | - const dh = cover_img.height * scale | 373 | + const dest_ar = card_w / cover_h |
| 374 | - const dx = card_x + (card_w - dw) / 2 | 374 | + const src_ar = sw / sh |
| 375 | - const dy = card_y + (cover_h - dh) / 2 | 375 | + let sx = 0, sy = 0, s_w = sw, s_h = sh |
| 376 | - ctx.drawImage(cover_img, dx, dy, dw, dh) | 376 | + // 根据源图与目标区域的纵横比,选择水平或垂直裁剪 |
| 377 | + if (src_ar > dest_ar) { | ||
| 378 | + // 源图更宽:水平裁剪 | ||
| 379 | + s_h = sh | ||
| 380 | + s_w = Math.round(sh * dest_ar) | ||
| 381 | + sx = Math.round((sw - s_w) / 2) | ||
| 382 | + sy = 0 | ||
| 383 | + } else { | ||
| 384 | + // 源图更窄或更高:垂直裁剪 | ||
| 385 | + s_w = sw | ||
| 386 | + s_h = Math.round(sw / dest_ar) | ||
| 387 | + sx = 0 | ||
| 388 | + sy = Math.round((sh - s_h) / 2) | ||
| 389 | + } | ||
| 390 | + // 目标区域严格限定在封面矩形 | ||
| 391 | + ctx.save() | ||
| 392 | + ctx.beginPath() | ||
| 393 | + ctx.rect(card_x, card_y, card_w, cover_h) | ||
| 394 | + ctx.clip() | ||
| 395 | + ctx.drawImage(cover_img, sx, sy, s_w, s_h, card_x, card_y, card_w, cover_h) | ||
| 396 | + ctx.restore() | ||
| 377 | } else { | 397 | } else { |
| 378 | ctx.fillStyle = '#f3f4f6' // gray-100 | 398 | ctx.fillStyle = '#f3f4f6' // gray-100 |
| 379 | ctx.fillRect(card_x, card_y, card_w, cover_h) | 399 | ctx.fillRect(card_x, card_y, card_w, cover_h) |
| ... | @@ -490,6 +510,12 @@ watch(() => [props.course, props.qr_url], () => { | ... | @@ -490,6 +510,12 @@ watch(() => [props.course, props.qr_url], () => { |
| 490 | object-fit: contain; | 510 | object-fit: contain; |
| 491 | display: block; | 511 | display: block; |
| 492 | } | 512 | } |
| 513 | + .PosterCover { | ||
| 514 | + // 固定封面高度为卡片宽度的 2/3,避免溢出到信息区 | ||
| 515 | + aspect-ratio: 3 / 2; | ||
| 516 | + width: 100%; | ||
| 517 | + overflow: hidden; | ||
| 518 | + } | ||
| 493 | .PosterInfo { | 519 | .PosterInfo { |
| 494 | overflow: hidden; | 520 | overflow: hidden; |
| 495 | .PosterQR { | 521 | .PosterQR { | ... | ... |
-
Please register or login to post a comment