加载grafika
use Grafika\Color;
use Grafika\Grafika;
use app\common\library\wechat\WxBase;
use app\common\library\wechat\Qrcode;

  1. 控制器
    $data=[
     'distribut_id'=>$distribut_id,
     'avator'=>$storeInfo['head'],
     'nickname'=>$storeInfo['store_name'],
     'date'=>date('Y_m_d'),
    ];
    //生成打卡审核图片
    $posterModel = new posterModel($data);
    $posters=$posterModel->getImage();
    
  2. 模型
    Poster.php ``` <?php namespace app\api\model; use Grafika\Color; use Grafika\Grafika; use app\common\library\wechat\WxBase; use app\common\library\wechat\Qrcode; use think\Db; /**
    • 生成海报类
    • Class Qrcode
    • @package app\common\service / class Poster extends Base{ / @var array $config 海报设置 / private $config; / @var int $sdata data数据 */ private $sdata; /**
    • 构造方法
    • Poster constructor.
    • @param $data
    • [distribut_id] => 2
    • [avator] => 2020_01_11
    • [nickname] => 4
    • @throws \Exception */ public function __construct($data=[]){ $this->sdata=$data; // 微信设置 $this->config = Db::name(‘wechatconfig’)->where(‘wechat’,’wechat’)->find(); } /**
    • 获取海报
    • @return string
    • @throws \app\common\exception\BaseException
    • @throws \think\exception\DbException
    • @throws \Exception */ public function getImage(){ if (file_exists($this->getPosterPath())) { return $this->getPosterUrl(); } // 小程序id $wxappId = $this->config[‘wxappid’]; // 1. 下载背景图 $backdrop = $this->getBg(); // 2. 下载用户头像 $avatarUrl = $this->saveTempImage($wxappId,$this->sdata[‘avator’],’head’); // 3. 下载小程序码 $qrcode = $this->saveQrcode($wxappId,’referee_id:’.$this->sdata[‘distribut_id’]); // 4. 拼接海报图 return $this->savePoster($backdrop, $avatarUrl, $qrcode); } /**
    • 保存小程序码到文件
    • @param $wxapp_id
    • @param $scene
    • @param null $page
    • @return string
    • @throws \app\common\exception\BaseException
    • @throws \think\exception\DbException */ private function saveQrcode($wxapp_id, $scene, $page = null){ // 文件目录 $dirPath = ROOT_PATH.’public/uploads/qrcode’; !is_dir($dirPath) && mkdir($dirPath, 0755, true); // 文件名称 $fileName = ‘qrcode_‘.md5($wxapp_id.$scene.$page).’.png’; // 文件路径 $savePath = “{$dirPath}/{$fileName}”; if (file_exists($savePath)) return $savePath; // 小程序配置信息 // 请求api获取小程序码 $qrcode = new Qrcode($this->config[‘wxappid’],$this->config[‘wxappsecret’]); $content = $qrcode->getQrcode($scene, $page); // 保存到文件 file_put_contents($savePath, $content); return $savePath; } /**
    • 获取网络图片到临时目录
    • @param $wxapp_id
    • @param $url
    • @param string $mark
    • @return string */ private function saveTempImage($wxapp_id,$url,$mark=’head’,$ssl=false){ $dirPath = ROOT_PATH.’public/uploads/store/head’; !is_dir($dirPath) && mkdir($dirPath, 0755, true); $savePath = $dirPath . ‘/’ . $mark . ‘_’ . $url . ‘.png’; if (file_exists($savePath)) return $savePath; $ch = curl_init($url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); if(!empty($ssl)){ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); $img = curl_exec($ch); curl_close($ch); $fp = fopen($savePath, ‘w’); fwrite($fp, $img); fclose($fp); return $savePath; } /**
    • 拼接海报图
    • @param $backdrop
    • @param $avatarUrl
    • @param $qrcode
    • @return string
    • @throws \Exception */ private function savePoster($backdrop, $avatarUrl, $qrcode){ vendor(‘grafika.src.autoloader’); // 实例化图像编辑器 $editor = Grafika::createEditor([‘Gd’]); // 打开海报背景图 $editor->open($backdropImage, $backdrop); $backdropImage_w=$backdropImage->getWidth(); $backdropImage_h=$backdropImage->getHeight(); // 生成圆形用户头像 $this->circular($avatarUrl, $avatarUrl); // 打开用户头像 $editor->open($avatarImage, $avatarUrl); // 重设用户头像宽高 $avatarWidth = 160; $editor->resizeExact($avatarImage, $avatarWidth, $avatarWidth); // 用户头像添加到背景图 $avatarX = ($backdropImage_w/2)-80; $avatarY = ($backdropImage_h/2)-60; $editor->blend($backdropImage, $avatarImage, ‘normal’, 1.0, ‘top-left’, $avatarX, $avatarY); // 生成圆形小程序码 // $this->circular($qrcode, $qrcode); // 打开小程序码 $editor->open($qrcodeImage, $qrcode); // 重设小程序码宽高 $qrcodeWidth = 180; $editor->resizeExact($qrcodeImage, $qrcodeWidth, $qrcodeWidth); // 小程序码添加到背景图 $qrcodeX = ($backdropImage_w/2)-90; $qrcodeY = ($backdropImage_h/2)+340; $editor->blend($backdropImage, $qrcodeImage, ‘normal’, 1.0, ‘top-left’, $qrcodeX, $qrcodeY); // 写入用户昵称 $Color = new Color(‘#333333’); $fontPath = Grafika::fontsDir() . DS . ‘st-heiti-light.ttc’; $fontBox = imagettfbbox(20,0,$fontPath,$this->sdata[‘nickname’]);//获取文字所需的尺寸大小 $editor->text($backdropImage, $this->sdata[‘nickname’],20,ceil((($backdropImage_w-$fontBox[2])/2)),($backdropImage_h/2)+120,$Color,$fontPath); // 保存图片 $editor->save($backdropImage, $this->getPosterPath()); return $this->getPosterUrl(); } /**
    • 生成圆形图片
    • @param static $imgpath 图片地址
    • @param string $saveName 保存文件名,默认空。 */ private function circular($imgpath, $saveName = ‘’,$alpha=’127’){ // print_r($imgpath);exit; $srcImg = imagecreatefromstring(file_get_contents($imgpath)); $w = imagesx($srcImg); $h = imagesy($srcImg); $w = $h = min($w, $h); $newImg = imagecreatetruecolor($w, $h); // 这一句一定要有 imagesavealpha($newImg, true); // 拾取一个完全透明的颜色,最后一个参数127为全透明 $bg = imagecolorallocatealpha($newImg, 255, 255, 255, $alpha); imagefill($newImg, 0, 0, $bg); $r = $w / 2; //圆半径 for ($x = 0; $x < $w; $x++) { for ($y = 0; $y < $h; $y++) { $rgbColor = imagecolorat($srcImg, $x, $y); if (((($x - $r) * ($x - $r) + ($y - $r) * ($y - $r)) < ($r * $r))) { imagesetpixel($newImg, $x, $y, $rgbColor); } } } // 输出图片到文件 imagepng($newImg, $saveName); // 释放空间 imagedestroy($srcImg); imagedestroy($newImg); } /**
    • 处理文字超出长度自动换行
    • @param integer $fontsize 字体大小
    • @param integer $angle 角度
    • @param string $fontface 字体名称
    • @param string $string 字符串
    • @param integer $width 预设宽度
    • @param null $max_line 最多行数
    • @return string */ private function wrapText($fontsize, $angle, $fontface, $string, $width, $max_line = null){ // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度 $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 ($max_line && $line_count >= $max_line) { $content = mb_substr($content, 0, -1, ‘UTF-8’) . “…”; break; } $content .= “\n”; } $content .= $l; } return $content; } /**
    • 海报图文件路径
    • @return string */ private function getPosterPath(){ // 保存路径 $tempPath =ROOT_PATH.’public/uploads/store/poster/’; !is_dir($tempPath) && mkdir($tempPath, 0755, true); return $tempPath.$this->getPosterName(); } /**
    • 海报图文件名称
    • @return string */ private function getPosterName(){ return ‘poster_‘.md5($this->sdata[‘date’].’_’.$this->sdata[‘distribut_id’]).’.png’; } /**
    • 海报图url
    • @return string */ private function getPosterUrl(){ return \base_url().’uploads/store/poster/’.$this->getPosterName().’?t=’.time(); } /**
    • 获取随机背景图
    • @param $where
    • @return null static
    • @throws \think\exception\DbException */ private function getBg(){ $mt_rand=mt_rand(1,4); $bg=ROOT_PATH.’public/uploads/store/share_bg_’.$mt_rand.’.jpg’; return $bg; } }
      3.前端   
      poster.js
      

      Page({ /**

    • 页面的初始数据 */ data: { modalHidden: false }, posters:function(){ let _this=this; let msg=’临沂市兰山区颐高上海街’; wx.showModal({ title: ‘提示’, content: ‘确认定位在:’ + msg+’?’, success: function (res) { if (res.confirm) { console.log(‘用户点击确定’); App._get(‘Store/posters’, {}, function (result) { if (result.code==1){ _this.setData({ posters: result.data.posters, modalHidden: true }); } console.log(result); }); } else if (res.cancel) { console.log(‘用户点击取消’) } } }) }, onHideCheckPoster:function(){ var _this = this; _this.setData({ modalHidden: false }) }, // 保存图片 saveImg: function (e) { let _this = this; //获取相册授权 wx.getSetting({ success(res) { if (!res.authSetting[‘scope.writePhotosAlbum’]) { wx.authorize({ scope: ‘scope.writePhotosAlbum’, success() { //这里是用户同意授权后的回调 _this.saveImgToLocal(); }, fail() {//这里是用户拒绝授权后的回调 _this.setData({ openSettingBtnHidden: false }) } }) } else {//用户已经授权过了 _this.saveImgToLocal(); } } }) }, saveImgToLocal: function (e) { let _this = this; let imgSrc = _this.data.posters; wx.downloadFile({ url: imgSrc, success: function (res) { //图片保存到本地 wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function (data) { wx.showToast({ title: ‘保存成功’, icon: ‘success’, duration: 2000 }) }, }) } }) }, // 授权 handleSetting: function (e) { let _this = this; // 对用户的设置进行判断,如果没有授权,即使用户返回到保存页面,显示的也是“去授权”按钮;同意授权之后才显示保存按钮 if (!e.detail.authSetting[‘scope.writePhotosAlbum’]) { // wx.showModal({ // title: ‘警告’, // content: ‘若不打开授权,则无法将图片保存在相册中!’, // showCancel: false // }) _this.setData({ openSettingBtnHidden: false }) } else { // wx.showModal({ // title: ‘提示’, // content: ‘您已授权,赶紧将图片保存在相册中吧!’, // showCancel: false // }) _this.setData({ openSettingBtnHidden: true }) } }, });
      poster.wxss
      

      /* 遮罩层 / .mask { width: 100%; height: 100%; position: fixed; top: 0; left: 0; background: #000; z-index: 9000; opacity: 0.5; } / 分销海报 / .modalPoster { width: 90%; height:90vh; position: fixed; top: 30px; left: 0; right: 0; z-index: 99999; margin: 0 auto; display: flex; flex-direction: column; align-items: center; border-radius: 15rpx; background: #fff; } .modalPoster .close{ position: absolute; width: 60rpx; height: 60rpx; top: -30rpx; right: -30rpx; } .modalPoster .close image{ width: 100%; height: 100%; } / 分销海报的图片 */ .modalPoster .check-box,.modalPoster .shimg image { height:80vh; margin: 9rpx; } .saveImg{ width: 40%; height: 65rpx; line-height: 65rpx; font-size: 32rpx; background: #fec627; border: 1rpx solid #fec627; border-radius: 50rpx; color: #fff; text-align: center; margin: 20rpx; }

      poster.wxml
      

保存

``` 附件下载:https://file.alonesky.com/file/poster_tplay_tp5.zip