hookehuyr

fix(SharePoster): 严格裁剪封面图避免溢出到信息区

修改封面图裁剪逻辑,根据源图与目标区域的纵横比选择水平或垂直裁剪
添加封面区域样式限制,防止内容溢出
...@@ -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 {
......