PHP使用Grafika合成图片,生成海报图

需求背景:
在小程序上生成海报图,但在保存图片时,只能保存其中的小程序码图片,保存下来的图片过于单调,且无法确认该图片的作用性,所以需要调整为保存一整张海报图。

海报效果图:
PHP使用Grafika合成图片,生成海报图_第1张图片

需求分析:
在海报图中,背景图、头像、文字、还有小程序码都是各自独立的部分,我们需要把这些图片合并起来,然后输出为一整张图片,这样就可以直接长按保存图片了。

合并图片可以用以下方法:

  1. 在前端合成,使用画布canvas,把各个元素“画”进去;
  2. 在后端合成,使用插件Grafika,把各个组成部分合并在一起;

因为当前是在小程序开发这个需求,考虑到未来可能会替换海报上的一些文字、图片等情况,而每次修改小程序代码都需要提交版本审核,耗时耗力,所以决定在后端进行图片合成,输出图片到前端。

注意:若背景图片有透明的部分,处理过后,透明部分会变成黑色,在Grafika中暂未找到能合并透明图片的方法,感觉有点坑,不过有搜到说可以直接用gd来合成,有空再研究研究吧。(目前的解决方法是换张白色背景的图片)

使用Grafika

安装:

composer require kosinix/grafika:dev-master --prefer-dist

生成方法
function create($slogan, $avatar, $qr){
    // 背景图片
    $bg_img = 'images/qr_code_bg.png';

    // 实例化图像编辑器
    $editor = Grafika::createEditor(['Gd']);
    // 打开海报背景图
    $editor->open($backdropImage, $bg_img);
    $bgWidth = $backdropImage->getWidth();
    // 生成圆形用户头像
    $avatarUrlName = 'temp/avatar.png';
    $this->circular($avatar_url, $avatarUrlName);
    // 打开用户头像
    $editor->open($avatarImage, $avatarUrlName);
    // 重设用户头像宽高
    $avatarWidth = 310;
    $editor->resizeExact($avatarImage, $avatarWidth, $avatarWidth);
    // 用户头像添加到背景图
    $avatarX = 401;
    $avatarY = 36;
    $editor->blend($backdropImage, $avatarImage, 'normal', 1.0, 'top-left', $avatarX, $avatarY);
    // 打开小程序码
    $editor->open($qrcodeImage, $qr);
    // 重设小程序码宽高
    $qrcodeWidth = 430;
    $editor->resizeExact($qrcodeImage, $qrcodeWidth, $qrcodeWidth);
    // 小程序码添加到背景图
    $qrcodeX = 340;
    $qrcodeY = 720;
    $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', $qrcodeX, $qrcodeY);

    // 处理文字
    $color = new Color('#FFFFFF');
    $fontPath = Grafika::fontsDir() . '/st-heiti-light.ttc';
    $fontSize = 50;
    // 处理用户昵称
    $nicknameBox = $this->get_text_box($nickname, $fontSize, $fontPath);
    $fontY = 410;
    $fontX = ($bgWidth / 2) - ($nicknameBox[0] / 2);
    $editor->text($backdropImage, $nickname, $fontSize, $fontX, $fontY, $color, $fontPath);

    $str1 = "邀请您注册成为分销员";
    $strBox1 = $this->get_text_box($str1, $fontSize, $fontPath);
    $str1x = ($bgWidth / 2) - ($strBox1[0] / 2);
    $editor->text($backdropImage, "邀请您注册成为分销员", $fontSize, $str1x, $fontY + 100, $color, $fontPath);

    $str2 = "一起赚佣金";
    $strBox2 = $this->get_text_box($str2, $fontSize, $fontPath);
    $str2x = ($bgWidth / 2) - ($strBox2[0] / 2);
    $editor->text($backdropImage,'一起赚佣金', $fontSize, $str2x, 1220, $color, $fontPath);

    // 保存图片
    $editor->save($backdropImage, $qr);
}
  • 使用open()打开图片并获得该图片对象,使用blend()依次合并图片。
  • 在处理头像图片时,需要先指定临时的头像文件,便于后续使用。
  • 添加文字到图片上时text(),Grafika默认的字体不兼容中文,所以需要指定字体文件路径。
// 生成圆形用户头像
function circular($imgpath, $saveName = ''){
    $ext = pathinfo($imgpath);
    $srcImg = null;
    switch ($ext['extension']) {
        case 'jpg':
        case 'jpeg':
            $srcImg = imagecreatefromjpeg($imgpath);
            break;
        case 'png':
            $srcImg = imagecreatefrompng($imgpath);
            break;
    }
    // 获取图片尺寸
    $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, 127);
    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);
}
  • 方法的大概思路是:创建一个正方形的透明图片,通过循环,把透明图片上的圆形部分的像素点画上(替换)头像图片对应的像素点,此时得到的图片就是一个圆形的头像图片。
  • 方法中使用了imagecreatetruecolor(),创建真彩图像。此时需要保证使用的图片位数>24(图片文件>属性>详细信息>位深度),否则无法渲染图像成功,结果得到的图片是黑色的。
  • 除了使用php内置函数实现圆形图片外,还可以安装Imagick扩展,更方便。
// 获取字符串宽高
function get_text_box($text, $size, $font){
    $point = imagettfbbox($size, 0, $font, $text);
    $width = $point[4] - $point[6];
    $height = $point[1] - $point[7];
    return [$width, $height];
}
  • 使用imagettfbbox()来获取字符串所在的坐标轴,从而可以拿到字符串文本框的宽高,把字符串居中合并到背景图上。

因为目前无法生成用于前端展示的透明背景的图片,所以改成前端展示的还是一个弹窗,弹窗上添加一个长按事件,触发下载合成海报图片到用户相册

// 长按点击事件
downloadQrCode(){
    wx.showLoading({
        title: "加载中",
        mask: true
    });
    if(this.data.download_qr_code){
        this.downloadSaveImage(this.data.download_qr_code);
    }else{        
        App._get("user/download_agent_qr_code", {}, (result) => {
            if(!result.res){
                wx.showToast({ icon: 'none', title: result.msg });
                return false;
            }
            this.setData({download_qr_code: result.data.url});
            this.downloadSaveImage(result.data.url);
        }, null, (res) => {
            wx.hideLoading();
        });
    }
},
/**
 * 下载图片,并保存到用户相册
 * @param {string} url 图片地址
 */
downloadSaveImage(url){
    wx.getImageInfo({
        src: url,
        success: function (ret) {
            var path = ret.path;
            wx.saveImageToPhotosAlbum({
                filePath: path,
                success(result) {
                    wx.hideLoading();
                    wx.showToast({ icon: 'none', title: "已保存图片到相册" });
                },
                fail(result) {
                    wx.hideLoading();
                    if(result.errMsg.indexOf("saveImageToPhotosAlbum:fail auth deny") !== -1){
                        wx.showToast({ icon: 'none', title: "请允许小程序“保存图片到相册”" });
                    }
                    console.log(result)
                }
            });
        },
        fail: function(result){
            wx.hideLoading();
            console.log(result)
        }
    });
},
  • App._get() 为发送get请求公共方法

参考:
Grafika官方文档
PHP 将图片切成圆角

你可能感兴趣的:(微信小程序,PHP,php,前端,开发语言,微信小程序)