为微信小程序生成当前页的二维码并合成适用于朋友圈的宣传海报的方法

我们在使用微信小程序的时候,会发现微信小程序是无法转发到朋友圈的。这也许是腾讯基于业务生态的考虑没有开放这个口子。但作为微信小程序的使用者来说就不是那么方便了。有一些很有趣的小程序我们是很希望将它转发到朋友圈分享给自己的好友看的。

现在,有一种折衷的方法,就是为小程序生成当前页面的二维码,将这个二维码转发到朋友圈,别人扫码是可以进入到小程序里的。虽然有一点点别扭,但总算是变相解决了小程序页面分享到朋友圈的问题。微信可能一直没有明确支持这种做法,所以,用到这个方法的朋友,在提交小程序审核的时候,可能需要低调一点咯。

下面我们来看看前端(小程序端)代码的构成:

	qrCode: function(options){
		var self = this;
		var pages = getCurrentPages();    //获取加载的页面
		var currentPage = pages[pages.length-1];    //获取当前页面的对象
		var url = currentPage.route;   //当前页面url
		var options = currentPage.options;   //如果要获取url中所带的参数可以查看options
		var qrurl = 'pages/detail/detail?id='+ options.id; //拼接当前页面的path地址
		var imgtag = 'prefix_' + options.id; //拼接生成的图片文件名
		wx.request({
			url: 'xxxxxx/QRcode.php', //后端request地址
			method: 'POST', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
			header: {
				'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
			},
			data: {
				path: qrurl, //A、C类接口使用,冒号之前是这个数值在后端的名称,冒号之后是这个数值在前端的名称
				width: 600, //这里可以不设置,在后端设置也可以
				imgname: imgtag, 
				title: '你的title',
			},
			success: function(res) {
				//console.log(qrurl);
				//console.log(imgtag);
				wx.downloadFile({
					url: 'xxxxxx' + imgtag + '.jpg', //这里填写你服务器上保存海报图片的目录网址
					success:function(res){
						wx.saveImageToPhotosAlbum({
							filePath: res.tempFilePath,
							success(result) {
								//console.log(result)
								wx.showModal({
									title: '提示',
									content: '二维码海报已保存在手机相册,请进入手机相册找到这张海报并分享到微信朋友圈。',
									showCancel: false,
									success: function (res) {
										if (res.confirm) {
											console.log('用户点击确定')
										}
									}
								})
							}
						});
					},fail:function(res){
						console.log(res)
					}
				});
			},
			fail:function(res) {
				//console.log('isFail')
			}
		});
	},

代码里都有注释,我简单讲一下,实现这个目的需要进行哪几个步骤:

第一步:获取当前页面的path,为生成页面二维码做准备;
第二步:wx.request对服务器发送POST请求,在后端生成二维码并合成海报;
第三步:wx.downloadFile下载服务器指定目录下生成的海报;
第四步:wx.saveImageToPhotosAlbum将海报保存在手机相册。

现在,我们来看后端代码的构成:

首先,还是取得$access_token,然后调用微信A类二维码接口来获取当前页的小程序码。

http://api.weixin.qq.com/wxa/getwxacode?access_token=$access_token

这里解释一下微信小程序A、B、C三类接口,很多人在这里都被微信的文档搞得头晕。
接口A小程序码(太阳码),总数10万个(永久有效,扫码进入path对应的动态页面);
接口B小程序码(太阳码),不限制数量(永久有效,将统一打开固定页面,可根据scene跟踪推广人员或场景);
接口C小程序二维码(方形码),总数10万个(永久有效,扫码进入path对应的动态页面)。

这里可以很明显地看出来,B类码我们用不到,那是跟踪某一个固定页面的多渠道推广效果的。而C类码是传统的方形二维码,如果是在小程序里看的话,是识别不了的。所以,要生成多个页面的小程序码,我们只能选择A类接口。

页面参数通过前端POST过来,我们在后端生成二维码之后,用file_put_contents()函数将其保存在服务器的指定目录中(如果是linux服务器,请确保目录可写,并且www这个用户对该目录有所有权。 命令行: chown -R www /www/你的目录地址)。

下一步我们需要调用一个类CombineImage.class.php来合成海报,事先我们已经将一张背景图上传到服务器了。这个类是做的图片拼接工作,跟合成还有一点差别,但是原理上大同小异了。

后端处理好之后,回到前端第三步将图片下载保存即可,我们在前端加了一个wx.showModal提示,引导使用者到相册里找到这张海报图,并将其分享到朋友圈。

后端php代码QRCode.php如下:

<?php  

	require_once 'CombineImage.class.php';

	function httpGet($url) {
			$curl = curl_init();
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($curl, CURLOPT_TIMEOUT, 500);
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt($curl, CURLOPT_URL, $url);

			$res = curl_exec($curl);
			curl_close($curl);

			return $res;
	}


	function getAccessToken() {
			$AppId = ""; //小程序APPid
			$AppSecret = ""; //小程序APPSecret
			$data = json_decode(file_get_contents("access_token.json"));
			if ($data->expire_time < time()) {
				$url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$AppId.'&secret='.$AppSecret;
				$res = json_decode(httpGet($url));
				$access_token = $res->access_token;
				if ($access_token) {
					$data->expire_time = time() + 7000;
					$data->access_token = $access_token;
					$fp = fopen("access_token.json", "w");
					fwrite($fp, json_encode($data));
					fclose($fp);
				}
			} else {
				$access_token = $data->access_token;
			}
			return $access_token;
  }


	function get_content_post($url,$post_data=array(),$header=array()){   
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查  
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);  // 从证书中检查SSL加密算法是否存在 
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_AUTOREFERER,true);
        $content = curl_exec($ch);
        $info = curl_getinfo($ch,CURLINFO_EFFECTIVE_URL); 
        $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);       
        curl_close($ch);    
        if($code == "200"){
            return $content;
        }else{
            return "错误码:".$code;
        }
	}


	$ACCESS_TOKEN = getAccessToken($access_token); 
	//接口A小程序码,总数10万个(永久有效,扫码进入path对应的动态页面)
	$url = 'http://api.weixin.qq.com/wxa/getwxacode?access_token='.$ACCESS_TOKEN;
	//接口B小程序码,不限制数量(永久有效,将统一打开首页,可根据scene跟踪推广人员或场景)
	//$url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=".$ACCESS_TOKEN; 
	//接口C小程序二维码,总数10万个(永久有效,扫码进入path对应的动态页面)
	//$url = 'http://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token='.$ACCESS_TOKEN;
	header('content-type:image/png');
	$color = array(  
			"r" => "114",  //这个颜色码自己到Photoshop里设
			"g" => "188",  //这个颜色码自己到Photoshop里设
			"b" => "234",  //这个颜色码自己到Photoshop里设
	);  
	$data = array(
		//$data['scene'] = "scene";//自定义信息,可以填写诸如识别用户身份的字段,注意用中文时的情况  
		//$data['page'] = "pages/index/index";//扫码后对应的path,只能是固定页面
		'path' => $_POST['path'], //前端传过来的页面path
		'width' => intval(600), //设置二维码尺寸
		'auto_color' => false,
		'line_color' => $color,
	);
	$data = json_encode($data);

	$QRCode = get_content_post($url,$data);
	$imgName = $_POST['imgname'];
	$fileName = $imgName.'.png';
	
	//输出二维码
	file_put_contents('QRCode/'.$fileName,$QRCode);

	//合成海报
	$ci = new CombineImage(array('QRCode/'.$fileName, 'background.png'), 'QRCode/'.$imgName.'.jpg');
	$ci->combine();
	$ci->show();


?>

图片合成类CombineImage.class.php :

<?php
/**
 * 拼接多幅图片成为一张图片
 *
 * 参数说明:原图片为文件路径数组,目的图片如果留空,则不保存结果
 *
 * 例子:
 * <code>
 * $ci = new CombineImage(array("D:/Downloads/1.jpg", "D:/Downloads/2.png"), "D:/Downloads/3.png");
 * $ci->combine();
 * $ci->show();
 * </code>
 *
 * @author 张荣杰
 * @version 2012.8.9
 */
class CombineImage {
	/**
	 * 原图地址数组
	 */
	private $srcImages;
	/**
	 * 每张图片缩放到这个宽度
	 */
	private $width;
	/**
	 * 每张图片缩放到这个高度
	 */
	private $height;
	/**
	 * 拼接模式,可以选择水平或垂直
	 */
	private $mode;
	/**
	 * 水平拼接模式常量
	 */
	const COMBINE_MODE_HORIZONTAL = "horizontal";
	/**
	 * 垂直拼接模式常量
	 */
	const COMBINE_MODE_VERTICAL = "vertical";
	/**
	 * 目标图片地址
	 */
	private $destImage;

	/**
	 * 临时画布
	 */
	private $canvas;

	/**
	 * 构造函数,传入原图地址数组和目标图片地址
	 */
	public function __construct($srcImages = '', $desImage = '',
		$width = 600, $height = 600,
		$mode = self::COMBINE_MODE_VERTICAL) {
		$this->srcImages = $srcImages;
		$this->destImage = $desImage;
		$this->width = $width;
		$this->height = $height;
		$this->mode = $mode;
		$this->canvas = NULL;
	}

	public function __destruct() {
		if ($this->canvas != NULL) {
			imagedestroy($this->canvas);
		}
	}

	/**
	 * 合并图片
	 */
	public function combine() {
		if (empty($this->srcImages)	|| $this->width==0 || $this->height==0) {
			return;
		}
		$this->createCanvas();
		for($i=0; $i<count($this->srcImages); $i++) {
			$srcImage = $this->srcImages[$i];
			$srcImageInfo = getimagesize($srcImage);
			// 如果能够正确的获取原图的基本信息
			if ($srcImageInfo) {
				$srcWidth = $srcImageInfo[0];
				$srcHeight = $srcImageInfo[1];
				$fileType = $srcImageInfo[2];
				if ($fileType == 2) {
					// 原图是 jpg 类型
					$srcImage = imagecreatefromjpeg($srcImage);
				} else if ($fileType == 3) {
					// 原图是 png 类型
					$srcImage = imagecreatefrompng($srcImage);
				} else {
					// 无法识别的类型
					continue;
				}

				// 计算当前原图片应该位于画布的哪个位置
				if ($this->mode == self::COMBINE_MODE_HORIZONTAL) {
					$destX = $i * $this->width;
					$desyY = 0;
				} elseif ($this->mode == self::COMBINE_MODE_VERTICAL) {
					$destX = 0;
					$desyY = $i * $this->height;
				}

				imagecopyresampled($this->canvas, $srcImage, $destX, $desyY,
							0, 0, $this->width, $this->height, $srcWidth, $srcHeight);
			}
		}

		// 如果有指定目标地址,则输出到文件
		if ( ! empty($this->destImage)) {
			$this->output();
		}
	}

	/**
	 * 输出结果到浏览器
	 */
	public function show() {
		if ($this->canvas == NULL) {
			return;
		}
		header("Content-type: image/jpeg");
		imagejpeg($this->canvas);
	}

	/**
	 * 私有函数,创建画布
	 */
	private function createCanvas() {
		$totalImage = count($this->srcImages);
		if ($this->mode == self::COMBINE_MODE_HORIZONTAL) {
			$width = $totalImage * $this->width;
			$height = $this->height;
		} else if ($this->mode == self::COMBINE_MODE_VERTICAL) {
			$width = $this->width;
			$height = $totalImage * $this->height;
		}
		$this->canvas = imagecreatetruecolor($width, $height);

		// 使画布透明
		$white = imagecolorallocate($this->canvas, 255, 255, 255);
		imagefill($this->canvas, 0, 0, $white);
		imagecolortransparent($this->canvas, $white);
	}

	/**
	 * 私有函数,保存结果到文件
	 */
	private function output() {
		// 获取目标文件的后缀
		$fileType = substr(strrchr($this->destImage, '.'), 1);
		if ($fileType=='jpg' || $fileType=='jpeg') {
			imagejpeg($this->canvas, $this->destImage);
		} else {
			// 默认输出 png 图片
			imagepng($this->canvas, $this->destImage);
		}

	}

	/**
	 * @return the $srcImages
	 */
	public function getSrcImages() {
		return $this->srcImages;
	}

	/**
	 * @param Array $srcImages
	 */
	public function setSrcImages($srcImages) {
		$this->srcImages = $srcImages;
	}

	/**
	 * @return the $width
	 */
	public function getWidth() {
		return $this->width;
	}

	/**
	 * @param int $width
	 */
	public function setWidth($width) {
		$this->width = $width;
	}

	/**
	 * @return the $height
	 */
	public function getHeight() {
		return $this->height;
	}

	/**
	 * @param int $height
	 */
	public function setHeight($height) {
		$this->height = $height;
	}

	/**
	 * @return the $mode
	 */
	public function getMode() {
		return $this->mode;
	}

	/**
	 * @param const $mode
	 */
	public function setMode($mode) {
		$this->mode = $mode;
	}

	/**
	 * @return the $destImage
	 */
	public function getDestImage() {
		return $this->destImage;
	}

	/**
	 * @param String $destImage
	 */
	public function setDestImage($destImage) {
		$this->destImage = $destImage;
	}
}
?>

将这两个文件保存在服务器某个目录下,并在同目录下添加一个QRCode子目录(请确保整个目录及子目录都有写入权限)。然后拿文件QRcode.php和目录QRcode对应的网址分别填写进前端代码的对应位置即可。

现在,打开小程序调试工具开始调试吧!

对本教程有任何建议或者希望与我探讨的,欢迎扫描下方二维码添加我微信为好友与我交流。

发表评论

电子邮件地址不会被公开。 必填项已用*标注