// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\common\service\qrcode; use Exception; use Grafika\Color; use Grafika\Grafika; use app\common\model\Goods as GoodsModel; /** * 商品海报 * Class Goods * @package app\common\service\qrcode */ class Goods extends BaseQRcode { // 当前客户端 (默认H5) private string $channel; // 商品信息 private GoodsModel $goods; // 用户ID private ?int $userId; /** * 构造方法 * Goods constructor. * @param $goods * @param int|null $userId * @param string $channel 二维码渠道(小程序码、h5码) */ public function __construct(GoodsModel $goods, ?int $userId = null, string $channel = 'H5') { parent::__construct(); // 商品信息 $this->goods = $goods; // 当前用户id $this->userId = $userId; // 当前客户端 $this->channel = $channel; } /** * 拼接海报图 * @return string * @throws \cores\exception\BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @throws Exception */ public function getImage(): string { // 判断海报图文件存在则直接返回url if (file_exists($this->getPosterPath())) { return $this->getPosterUrl(); } // 商城ID $storeId = $this->goods['store_id']; // 商品海报背景图 $backdrop = __DIR__ . '/resource/goods_bg.png'; // 下载商品首图 $goodsUrl = $this->saveTempImage($storeId, $this->goods['goods_image'], 'goods'); // 小程序码参数 $scene = "gid:{$this->goods['goods_id']},uid:" . ($this->userId ?: ''); // 下载小程序码 $qrcode = $this->getQrcode($storeId, $scene, 'pages/goods/detail', $this->channel); // 拼接海报图 return $this->savePoster($backdrop, $goodsUrl, $qrcode); } /** * 拼接海报图 * @param string $backdrop 背景图路径 * @param string $goodsUrl 商品图路径 * @param string $qrcode 二维码图路径 * @return string * @throws \Exception */ private function savePoster(string $backdrop, string $goodsUrl, string $qrcode): string { // 实例化图像编辑器 $editor = Grafika::createEditor(['Gd']); // 字体文件路径 $fontPath = Grafika::fontsDir() . '/' . 'st-heiti-light.ttc'; // 打开海报背景图 $editor->open($backdropImage, $backdrop); // 打开商品图片 $editor->open($goodsImage, $goodsUrl); // 重设商品图片宽高 $editor->resizeExact($goodsImage, 690, 690); // 商品图片添加到背景图 $editor->blend($backdropImage, $goodsImage, 'normal', 1.0, 'top-left', 30, 30); // 商品名称处理换行 $fontSize = 30; $goodsName = $this->wrapText($fontSize, 0, $fontPath, $this->goods['goods_name'], 680, 2); // 写入商品名称 $editor->text($backdropImage, $goodsName, $fontSize, 30, 750, new Color('#333333'), $fontPath); // 写入商品价格 $editor->text($backdropImage, $this->goods['goods_price_min'], 38, 62, 964, new Color('#ff4444')); // 打开小程序码 $editor->open($qrcodeImage, $qrcode); // 重设小程序码宽高 $editor->resizeExact($qrcodeImage, 140, 140); // 小程序码添加到背景图 $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', 570, 914); // 保存图片 $editor->save($backdropImage, $this->getPosterPath()); return $this->getPosterUrl(); } /** * 处理文字超出长度自动换行 * @param integer $fontsize 字体大小 * @param integer $angle 角度 * @param string $fontface 字体名称 * @param string $string 字符串 * @param integer $width 预设宽度 * @param int|null $maxLine 最多行数 * @return string */ private function wrapText(int $fontsize, int $angle, string $fontface, string $string, int $width, int $maxLine = null): string { // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度 $content = ""; // 将字符串拆分成一个个单字 保存到数组 letter 中 $letter = []; for ($i = 0; $i < mb_strlen($string, 'UTF-8'); $i++) { $letter[] = mb_substr($string, $i, 1, 'UTF-8'); } $line_count = 0; foreach ($letter as $l) { $testbox = imagettfbbox($fontsize, $angle, $fontface, $content . ' ' . $l); // 判断拼接后的字符串是否超过预设的宽度 if (($testbox[2] > $width) && ($content !== "")) { $line_count++; if ($maxLine && $line_count >= $maxLine) { $content = mb_substr($content, 0, -1, 'UTF-8') . "..."; break; } $content .= "\n"; } $content .= $l; } return $content; } /** * 海报图文件路径 * @return string */ private function getPosterPath(): string { // 保存路径 $tempPath = web_path() . "temp/{$this->goods['store_id']}/"; !is_dir($tempPath) && mkdir($tempPath, 0755, true); return $tempPath . $this->getPosterName(); } /** * 海报图文件名称 * @return string */ private function getPosterName(): string { return 'goods_' . md5("{$this->userId}_{$this->goods['goods_id']}_{$this->channel}") . '.png'; } /** * 海报图url * @return string */ private function getPosterUrl(): string { return \base_url() . 'temp/' . $this->goods['store_id'] . '/' . $this->getPosterName() . '?t=' . time(); } }