|
|
@@ -16,18 +16,28 @@ class PosterUtil
|
|
|
|
|
|
const SLOT_W = 700;
|
|
|
|
|
|
+ /** 底部价格/标题/太阳码固定区 */
|
|
|
const FOOTER_H = 360;
|
|
|
|
|
|
- const IMG_H_MIN = 480;
|
|
|
+ /** 主图区温和下限,避免扁图海报过矮 */
|
|
|
+ const IMG_H_MIN = 240;
|
|
|
|
|
|
const IMG_H_MAX = 880;
|
|
|
|
|
|
- const TOTAL_H_MIN = 840;
|
|
|
-
|
|
|
const TOTAL_H_MAX = 1600;
|
|
|
|
|
|
const CANVAS_COLOR = 'FFFFFF';
|
|
|
|
|
|
+ const QR_SIZE = 220;
|
|
|
+
|
|
|
+ const QR_PAD_RIGHT = 36;
|
|
|
+
|
|
|
+ const QR_PAD_BOTTOM = 32;
|
|
|
+
|
|
|
+ const QR_HINT_GAP = 14;
|
|
|
+
|
|
|
+ const SCAN_HINT = '微信扫码下单';
|
|
|
+
|
|
|
/**
|
|
|
* 海报标题过长时截断,避免压住底部文案区
|
|
|
*/
|
|
|
@@ -44,38 +54,75 @@ class PosterUtil
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 按商品图比例计算主图区高度与画布总高
|
|
|
+ * 按商品图比例计算主图区高度与画布总高(能多矮就多矮,仅保留温和下限)
|
|
|
*
|
|
|
- * @return array{imgH:int,totalH:int,posterW:int}
|
|
|
+ * @return array{imgH:int,totalH:int,scaledH:int,posterW:int}
|
|
|
*/
|
|
|
public static function calcPosterLayout($coverWidth, $coverHeight)
|
|
|
{
|
|
|
$coverWidth = (int)$coverWidth;
|
|
|
$coverHeight = (int)$coverHeight;
|
|
|
+ $scaledH = 0;
|
|
|
|
|
|
if ($coverWidth > 0 && $coverHeight > 0) {
|
|
|
$scaledH = (int)round($coverHeight * (self::SLOT_W / $coverWidth));
|
|
|
$imgH = max(self::IMG_H_MIN, min(self::IMG_H_MAX, $scaledH));
|
|
|
} else {
|
|
|
- $imgH = 720;
|
|
|
+ $imgH = self::IMG_H_MIN;
|
|
|
+ $scaledH = $imgH;
|
|
|
}
|
|
|
|
|
|
$totalH = $imgH + self::FOOTER_H;
|
|
|
- $totalH = max(self::TOTAL_H_MIN, min(self::TOTAL_H_MAX, $totalH));
|
|
|
+ $absoluteMin = self::FOOTER_H + 120;
|
|
|
+ $totalH = max($absoluteMin, min(self::TOTAL_H_MAX, $totalH));
|
|
|
|
|
|
return [
|
|
|
'imgH' => $imgH,
|
|
|
'totalH' => $totalH,
|
|
|
+ 'scaledH' => $scaledH,
|
|
|
'posterW' => self::POSTER_W,
|
|
|
];
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 短图/扁图:lfit 后 pad;长图:m_fill 裁切进槽位
|
|
|
+ */
|
|
|
+ private static function buildCoverWatermarkPath($cover, $imgH, $scaledH, $coverWidth, $coverHeight)
|
|
|
+ {
|
|
|
+ $wide = $coverWidth > 0 && $coverHeight > 0 && ($coverWidth / $coverHeight) > 1.15;
|
|
|
+ $shortSlot = $scaledH > 0 && ($scaledH < $imgH || $wide);
|
|
|
+
|
|
|
+ if ($shortSlot) {
|
|
|
+ return $cover . '?x-oss-process=image/resize,m_lfit,h_' . $imgH . ',w_' . self::SLOT_W . ',limit_0'
|
|
|
+ . '/resize,m_pad,h_' . $imgH . ',w_' . self::SLOT_W . ',color_' . self::CANVAS_COLOR;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $cover . '?x-oss-process=image/resize,m_fill,h_' . $imgH . ',w_' . self::SLOT_W;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 太阳码水平中心 X(用于扫码文案 g_south 居中)
|
|
|
+ */
|
|
|
+ private static function qrCenterX()
|
|
|
+ {
|
|
|
+ return self::POSTER_W - self::QR_PAD_RIGHT - (int)(self::QR_SIZE / 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 扫码提示文案:位于太阳码正上方居中(g_south = 文字下边中点对齐)
|
|
|
+ */
|
|
|
+ private static function scanHintYFromBottom()
|
|
|
+ {
|
|
|
+ return self::QR_PAD_BOTTOM + self::QR_SIZE + self::QR_HINT_GAP;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 动态白底画布(blank.png → 750 × totalH)
|
|
|
*/
|
|
|
public static function buildCanvasBaseUrl($prefix, $totalH)
|
|
|
{
|
|
|
- $totalH = max(self::TOTAL_H_MIN, min(self::TOTAL_H_MAX, (int)$totalH));
|
|
|
+ $absoluteMin = self::FOOTER_H + 120;
|
|
|
+ $totalH = max($absoluteMin, min(self::TOTAL_H_MAX, (int)$totalH));
|
|
|
$process = '?x-oss-process=image/resize,m_pad,w_' . self::POSTER_W . ',h_' . $totalH
|
|
|
. ',color_' . self::CANVAS_COLOR;
|
|
|
|
|
|
@@ -121,14 +168,11 @@ class PosterUtil
|
|
|
*/
|
|
|
public static function buildGhsHdItemPosterUrl($prefix, array $options)
|
|
|
{
|
|
|
- if (!isset($options['scanHint'])) {
|
|
|
- $options['scanHint'] = '微信扫码采购';
|
|
|
- }
|
|
|
return self::buildMallSharePosterUrl($prefix, $options);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 散客/花束分享海报:动态画布 + 主图固定槽位 + 底部信息区
|
|
|
+ * 散客/花束分享海报:动态画布 + 主图槽位 + 底部信息区
|
|
|
*/
|
|
|
private static function buildMallSharePosterUrl($prefix, array $options)
|
|
|
{
|
|
|
@@ -140,7 +184,10 @@ class PosterUtil
|
|
|
$mainId = (int)($options['mainId'] ?? 0);
|
|
|
$flowerNum = (int)($options['flowerNum'] ?? 0);
|
|
|
$subtitle = trim((string)($options['subtitle'] ?? ''));
|
|
|
- $scanHint = trim((string)($options['scanHint'] ?? '微信扫码下单'));
|
|
|
+ $scanHint = trim((string)($options['scanHint'] ?? self::SCAN_HINT));
|
|
|
+ if ($scanHint === '微信扫码采购') {
|
|
|
+ $scanHint = self::SCAN_HINT;
|
|
|
+ }
|
|
|
|
|
|
$coverW = (int)($options['coverWidth'] ?? 0);
|
|
|
$coverH = (int)($options['coverHeight'] ?? 0);
|
|
|
@@ -151,6 +198,7 @@ class PosterUtil
|
|
|
$layout = self::calcPosterLayout($coverW, $coverH);
|
|
|
$imgH = $layout['imgH'];
|
|
|
$totalH = $layout['totalH'];
|
|
|
+ $scaledH = $layout['scaledH'];
|
|
|
|
|
|
$displayName = self::truncateText($name, 14);
|
|
|
if ($flowerNum > 0 && $displayName !== '') {
|
|
|
@@ -159,12 +207,12 @@ class PosterUtil
|
|
|
$displayName .= ' · ' . self::truncateText($subtitle, 8);
|
|
|
}
|
|
|
|
|
|
- $coverWatermark = $cover . '?x-oss-process=image/resize,m_fill,h_' . $imgH . ',w_' . self::SLOT_W;
|
|
|
- $qrWatermark = $miniCode . '?x-oss-process=image/resize,m_fill,h_220,w_220';
|
|
|
+ $coverWatermark = self::buildCoverWatermarkPath($cover, $imgH, $scaledH, $coverW, $coverH);
|
|
|
+ $qrWatermark = $miniCode . '?x-oss-process=image/resize,m_fill,h_' . self::QR_SIZE . ',w_' . self::QR_SIZE;
|
|
|
|
|
|
$url = self::buildCanvasBaseUrl($prefix, $totalH);
|
|
|
$url .= '/watermark,image_' . stringUtil::ossBase64($coverWatermark) . ',g_north,x_25,y_8';
|
|
|
- $url .= '/watermark,image_' . stringUtil::ossBase64($qrWatermark) . ',g_se,x_36,y_32';
|
|
|
+ $url .= '/watermark,image_' . stringUtil::ossBase64($qrWatermark) . ',g_se,x_' . self::QR_PAD_RIGHT . ',y_' . self::QR_PAD_BOTTOM;
|
|
|
|
|
|
$logoB64 = self::buildHyxhLogoWatermark($mainId);
|
|
|
if ($logoB64 !== '') {
|
|
|
@@ -174,7 +222,9 @@ class PosterUtil
|
|
|
$url .= '/watermark,text_' . stringUtil::ossBase64($priceText) . ',g_sw,x_24,y_32,color_EE2C2C,type_' . self::FONT . ',size_56';
|
|
|
$url .= '/watermark,text_' . stringUtil::ossBase64($displayName) . ',g_sw,x_24,y_102,color_333333,type_' . self::FONT . ',size_36';
|
|
|
$url .= '/watermark,text_' . stringUtil::ossBase64($staffText) . ',g_sw,x_24,y_148,color_999999,type_' . self::FONT . ',size_26';
|
|
|
- $url .= '/watermark,text_' . stringUtil::ossBase64($scanHint) . ',g_se,x_44,y_292,color_888888,type_' . self::FONT . ',size_24';
|
|
|
+ $url .= '/watermark,text_' . stringUtil::ossBase64($scanHint)
|
|
|
+ . ',g_south,x_' . self::qrCenterX() . ',y_' . self::scanHintYFromBottom()
|
|
|
+ . ',color_888888,type_' . self::FONT . ',size_24';
|
|
|
|
|
|
return $url;
|
|
|
}
|