• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

微信小程序开发——生成分享图片

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

做小程序开发,大都会遇到这么一个需求:生成分享图片。

需求的复杂度各不相同,不外乎 背景图+微信头像+昵称+小程序码+其他。本文作者入坑较深,使用了在前端canvas画布来实现。

大概的总结了下遇到的一些知识点:

1、创建一个通用的图片分享组件components

2、drawImage绘制自适应图片;

3、获取小程序码接口真的很坑;

4、保存base64图片到本地临时文件;

5、Promise.all解决获取图片数据过程中的异步;

 

废话不说了 直接贴代码

组件:

canvas-share.wxml

 1 <view catchtap="handleClose" class="share {{ visible ? \'show\' : \'\' }}">
 2 
 3 <canvas class="canvas-hide" canvas-id="share" style="width:{{canvasWidth*2}}rpx;height:{{canvasHeight*2}}rpx" />
 4 
 5 <view class="content" style="transform:scale({{responsiveScale}});-webkit-transform:scale({{responsiveScale}});">
 6 
 7 <image class="canvas" catchtap="zuzhimaopao" src="{{imageFile}}" style="width:{{canvasWidth/3*2}}rpx;height:{{canvasHeight/3*2}}rpx" />
 8 
 9 <view class="save" catchtap="handleSave"><text>保存图片</text></view>
10 
11 </view>
12 
13 </view>

 

canvas-share.js

  1 const app = getApp();
  2 
  3 var base64src = require("../../utils/base64src.js");
  4 
  5 var ajax = require("../../common/commonAjax.js");
  6 
  7 var util = require("../../utils/util.js");
  8 
  9 function getImageInfo(url) {
 10 
 11 return new Promise((resolve, reject) => {
 12 
 13 wx.getImageInfo({
 14 
 15 src: url,
 16 
 17 success: resolve,
 18 
 19 fail: reject,
 20 
 21 })
 22 
 23 })
 24 
 25 }
 26 
 27 function getImageInfo2(search){
 28 
 29 let url="";
 30 
 31 return new Promise((resolve, reject) => {
 32 
 33 ajax.getAccessToken(search, function (e) {
 34 
 35 if (e.ErrorCode == 0) {
 36 
 37 let base64data = e.Data;
 38 
 39 base64src.base64src(base64data, res => {
 40 
 41 //resolve(res);
 42 
 43 url = res;
 44 
 45 wx.getImageInfo({
 46 
 47 src: url,
 48 
 49 success: resolve,
 50 
 51 fail: reject,
 52 
 53 })
 54 
 55 });
 56 
 57 }
 58 
 59 })
 60 
 61 })
 62 
 63  
 64 
 65 }
 66 
 67 function createRpx2px() {
 68 
 69 const { windowWidth } = wx.getSystemInfoSync()
 70 
 71  
 72 
 73 return function(rpx) {
 74 
 75 return windowWidth / 750 * rpx
 76 
 77 }
 78 
 79 }
 80 
 81  
 82 
 83 const rpx2px = createRpx2px()
 84 
 85  
 86 
 87 function canvasToTempFilePath(option, context) {
 88 
 89 return new Promise((resolve, reject) => {
 90 
 91 wx.canvasToTempFilePath({
 92 
 93 ...option,
 94 
 95 success: resolve,
 96 
 97 fail: reject,
 98 
 99 }, context)
100 
101 })
102 
103 }
104 
105  
106 
107 function saveImageToPhotosAlbum(option) {
108 
109 return new Promise((resolve, reject) => {
110 
111 wx.saveImageToPhotosAlbum({
112 
113 ...option,
114 
115 success: resolve,
116 
117 fail: reject,
118 
119 })
120 
121 })
122 
123 }
124 
125  
126 
127 Component({
128 
129 properties: {
130 
131 visible: {
132 
133 type: Boolean,
134 
135 value: false,
136 
137 observer(visible) {
138 
139 if (visible && !this.beginDraw) {
140 
141 this.draw()
142 
143 this.beginDraw = true
144 
145 }
146 
147 }
148 
149 },
150 
151 userInfo: {
152 
153 type: Object,
154 
155 value: null,
156 
157 observer: function (e) {
158 
159 this.setData({
160 
161 userInfo: e
162 
163 })
164 
165 }
166 
167 },
168 
169 courseName: {
170 
171 type: String,
172 
173 value: "",
174 
175 observer: function (e) {
176 
177 this.setData({
178 
179 courseName: e
180 
181 })
182 
183 }
184 
185 },
186 
187 page: {
188 
189 type: String,
190 
191 value: "",
192 
193 observer: function (e) {
194 
195 this.setData({
196 
197 page: e
198 
199 })
200 
201 }
202 
203 },
204 
205 scene: {
206 
207 type: String,
208 
209 value: "",
210 
211 observer: function (e) {
212 
213 this.setData({
214 
215 scene: e
216 
217 })
218 
219 }
220 
221 }
222 
223 },
224 
225  
226 
227 data: {
228 
229 beginDraw: false,
230 
231 isDraw: false,
232 
233  
234 
235 canvasWidth: 1000,
236 
237 canvasHeight: 1200,
238 
239  
240 
241 imageFile: \'\',
242 
243  
244 
245 responsiveScale: 1,
246 
247 userInfo: app.globalData.userInfo,
248 
249 courseName:"",
250 
251 page:"",
252 
253 scene:"",
254 
255 },
256 
257  
258 
259 lifetimes: {
260 
261 ready() {
262 
263 const designWidth = 375
264 
265 const designHeight = 603 // 这是在顶部位置定义,底部无tabbar情况下的设计稿高度
266 
267  
268 
269 // 以iphone6为设计稿,计算相应的缩放比例
270 
271 const { windowWidth, windowHeight } = wx.getSystemInfoSync()
272 
273 const responsiveScale =
274 
275 windowHeight / ((windowWidth / designWidth) * designHeight)
276 
277 if (responsiveScale < 1) {
278 
279 this.setData({
280 
281 responsiveScale,
282 
283 })
284 
285 }
286 
287 },
288 
289 },
290 
291 methods: {
292 
293 handleClose() {
294 
295 this.triggerEvent(\'close\')
296 
297 },
298 
299 zuzhimaopao(){
300 
301  
302 
303 },
304 
305  
306 
307 handleSave() {
308 
309 const { imageFile } = this.data
310 
311  
312 
313 if (imageFile) {
314 
315 saveImageToPhotosAlbum({
316 
317 filePath: imageFile,
318 
319 }).then(() => {
320 
321 wx.showToast({
322 
323 icon: \'none\',
324 
325 title: \'分享图片已保存至相册\',
326 
327 duration: 2000,
328 
329 })
330 
331 })
332 
333 }
334 
335 },
336 
337 draw() {
338 
339 wx.showLoading()
340 
341 let cpage=this;
342 
343 console.log(this.data);
344 
345 const { userInfo, canvasWidth, canvasHeight,courseName } = this.data;
346 
347 const { avatarUrl, nickName } = userInfo;
348 
349 const avatarPromise = getImageInfo(avatarUrl);
350 
351 const backgroundPromise = \'/images/share.png\';
352 
353 let search={
354 
355 page:this.data.page,
356 
357 scene:this.data.scene
358 
359 }
360 
361 const filePath = getImageInfo2(search);
362 
363  
364 
365 Promise.all([avatarPromise, filePath])
366 
367 .then(([avatar, filePathxcx]) => {
368 
369 const ctx = wx.createCanvasContext(\'share\', this)
370 
371  
372 
373 const canvasW = rpx2px(canvasWidth * 2)
374 
375 const canvasH = rpx2px(canvasHeight * 2)
376 
377  
378 
379 // 绘制背景
380 
381 ctx.drawImage(
382 
383 backgroundPromise,
384 
385 //background,
386 
387 1,
388 
389 1,
390 
391 canvasW,
392 
393 canvasH
394 
395 )
396 
397  
398 
399 // 绘制头像
400 
401 const radius = rpx2px(80 * 2)
402 
403 const y = rpx2px(1030 * 2)
404 
405 // ctx.arc(canvasW / 2 - radius * 4, y, radius, 0, 2 * Math.PI)
406 
407 // ctx.clip()
408 
409 ctx.drawImage(
410 
411 avatar.path,
412 
413 canvasW / 2 - radius * 5 - 10,
414 
415 y - radius + 15,
416 
417 radius * 2,
418 
419 radius * 2,
420 
421 )
422 
423 // 绘制小程序码
424 
425 if (!util.isNullObj(filePathxcx)){
426 
427 ctx.drawImage(
428 
429 filePathxcx.path,
430 
431 // \'http://usr/tmp_base64src.png\',
432 
433 canvasW / 2 + radius * 2.5,
434 
435 y - radius * 1.3,
436 
437 radius * 3,
438 
439 radius * 3,
440 
441 )
442 
443  
444 
445 }
446 
447  
448 
449 // 绘制用户名
450 
451 ctx.setFontSize(36)
452 
453 ctx.setTextAlign(\'center\')
454 
455 ctx.setFillStyle(\'#000000\')
456 
457 ctx.fillText(
458 
459 nickName,
460 
461 canvasW / 2 - radius * 1.5 - 25,
462 
463 // y + rpx2px(150 * 2),
464 
465 y - radius * 0.5,
466 
467 )
468 
469 ctx.stroke()
470 
471  
472 
473 // 绘制课程名称
474 
475 ctx.setFontSize(48)
476 
477 ctx.setTextAlign(\'center\')
478 
479 ctx.setFillStyle(\'#434999\')
480 
481 ctx.fillText(
482 
483 "《" + courseName + "》",
484 
485 canvasW / 2,
486 
487 y - radius * 3.5,
488 
489 )
490 
491  
492 
493 ctx.draw(false, () => {
494 
495 canvasToTempFilePath({
496 
497 canvasId: \'share\',
498 
499 }, cpage).then(({ tempFilePath }) => cpage.setData({ imageFile: tempFilePath }))
500 
501 })
502 
503  
504 
505 wx.hideLoading()
506 
507 cpage.setData({ isDraw: true })
508 
509  
510 
511 })
512 
513 .catch(() => {
514 
515 cpage.setData({ beginDraw: false })
516 
517 wx.hideLoading()
518 
519 })
520 
521  
522 
523 }
524 
525 }
526 
527 })

 

前端保存base64图片的引用

base64src.js

 1 const fsm = wx.getFileSystemManager();
 2 
 3 const FILE_BASE_NAME = \'tmp_base64src\'; //自定义文件名
 4 
 5  
 6 
 7 function base64src(base64data, cb) {
 8 
 9 const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
10 
11 if (!format) {
12 
13 return (new Error(\'ERROR_BASE64SRC_PARSE\'));
14 
15 }
16 
17 const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`;
18 
19 const buffer = wx.base64ToArrayBuffer(bodyData);
20 
21 fsm.writeFile({
22 
23 filePath,
24 
25 data: buffer,
26 
27 encoding: \'binary\',
28 
29 success() {
30 
31 cb(filePath);
32 
33 },
34 
35 fail() {
36 
37 return (new Error(\'ERROR_BASE64SRC_WRITE\'));
38 
39 },
40 
41 });
42 
43 };
44 
45  
46 
47 export { base64src };
View Code

 

调用组件:

<canvas-share bindclose="close" userInfo="{{userInfo}}" visible="{{visible}}" courseName="{{aColumn.sName}}" page="/pages/course/PDFDetail" scene="iAutoID={{iAutoID}}" />

参数:

userInfowx.getUserInfo 获取的用户信息

visible:是否显示

courseName:可忽略,特殊需求 分享图上的显示课程名称

page:用于生成小程序码的参数 扫码跳转的页面

scene:用于生成小程序码的参数 扫码跳转的页面参数

bindclose:隐藏组件visiblefalse

 

Php后台 获取小程序码涉及的接口和方法

 

 1     public function GetAccessTokenAction(){
 2         $formVals= json_decode($this->getParam(\'formVals\'));
 3         $page="";
 4         $scene="";
 5         if ($formVals){
 6             $page=$formVals->page;
 7             $scene=$formVals->scene;
 8         }
 9 
10 //        $page="pages/course/oneVideoDetail";
11 //        $scene="iAutoID=60";
12         $appid=Yaf_G::getConf(\'appid\', \'dcr\');
13         $secret=Yaf_G::getConf(\'appsecret\', \'dcr\');
14         $rwm_contents="";
15 
16         $url1="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
17         $result = self::curl_file_get_contents($url1);
18         $access_token= isset($result)?json_decode($result)->access_token:\'\';
19 
20         if($access_token!=""){
21 //            $url2="https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={$access_token}";
22 //            $rwm_contents = self::curl_file_get_contents($url2,$page,$scene);
23             $url2="https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=".$access_token;
24             //dump($access_token);
25             $post_data = array(
26                 "page"=>$page,
27                 "scene"=>$scene,
28                 "width"=>50
29             );
30             $post_data=json_encode($post_data);
31             $rwm_contents=self::send_post($url2,$post_data);
32             $result=self::data_uri($rwm_contents,\'image/png\');
33         }
34         return $this->showMgApiMsg($result);
35     }
36 
37  
38 
39  
40 
41 protected function send_post( $url, $post_data ) {
42     $options = array(
43         \'http\' => array(
44             \'method\'  => \'POST\',
45             \'header\'  => \'Content-type:application/json\',
 
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap