微信小程序绘制图片,发送朋友圈

这种生成图片的效果是很常见的,实现起来也不难,跟原生js的差不多。需要注意的就是canvas标签上不要加太多的css,后果呢就是导致canvas不显示,还有呢就是canvas组件的优先级是最高的,所以会覆盖掉下面的所有内容,解决方法呢就是使用:

使用上面这两个组件是可以盖在canvas上面的。

注意:canvas绘制不支持网络图片,需要将网络图片保存成本地图片

 onLoad: function(options) {
        var grade = options.grade;
        this.setData({
            grade: grade
        })
        this.loading();

    },
    //检测,网络图片是否下载完成
    loading: function() {
        var _this = this;
        wx.showLoading({
            title: '生成中...',
        })
        timer = setInterval(function() {
            var avatarUrl = _this.data.avatarUrl;
            var qc_code = _this.data.qc_code;
            if (avatarUrl != null && qc_code != null) {
                wx.hideLoading();
                clearInterval(timer);
                _this.draw();
            }
        }, 500)
    },
    //保存到相册
    saveImage: function() {
        var imagePath = this.data.imagePath;
        wx.saveImageToPhotosAlbum({
            filePath: imagePath,
            success: function(res) {
                console.log(res)
            },
            fail: function(res) {
                console.log(res)
            }
        })

    },
    //将用户头像下载为本地路径
    downImage: function(img) {
        var _this = this;
        wx.getImageInfo({
            src: img,
            success: function(res) {
                console.log(res.path)
                _this.setData({
                    avatarUrl: res.path
                })

            }
        })
    },
    //下载小程序二维码
    downImage2: function (img) {
        var _this = this;
        wx.getImageInfo({
            src: img,
            success: function (res) {
                console.log(res.path)
                _this.setData({
                    qc_code: res.path
                })

            }
        })
    },
    //生成canvas图片
    draw: function() {
        var _this = this;
        var context = wx.createCanvasContext('firstCanvas');
        var userInfo = wx.getStorageSync('userInfo');
        var award ;
        // 性别
        var gender = userInfo.gender;
        //背景图片
        var bg = '../../images/icon-cj.png';
        //得分
        var grade = 0 ^ _this.data.grade;
        var width;
        var height;
        if (grade >= 0 && grade <= 30) {
            if(gender == 2){
                award = '../../images/zbzxlp.png';
            }else{
                award = '../../images/zbzxlg.png';
            }
        }else if(grade >= 31 && grade <= 60){
            if (gender == 2) {
                award = '../../images/zklp.png';
            } else {
                award = '../../images/zklg.png';
            }
        } else if (grade >= 61 && grade <= 80){
            if (gender == 2) {
                award = '../../images/zmlp.png';
            } else {
                award = '../../images/whlg.png';
            }
        }else{
            if (gender == 2) {
                award = '../../images/wmlp.png';
            } else {
                award = '../../images/wmlg.png';
            }
        }   
        if (award == '../../images/zbzxlp.png' || award == '../../images/zbzxlg.png'){
            width = 156;
            height= 25;
        }else{
            width = 103;
            height = 25;
        }   
        //二维码
        var qc_code = _this.data.qc_code;
        // 用户头像
        var avatarUrl = _this.data.avatarUrl;
        //获取设备的基本信息
        wx.getSystemInfo({
            success: function(res) {
                //绘制背景图
                context.drawImage(bg, 0, 0, 350, 468);
                // 绘制奖项
                context.drawImage(award,180 - (width / 2),212 - (height / 2),width,height);
                //绘制二维码
                context.drawImage(qc_code, 175 - (92 / 2), 385 - (108 / 2), 92, 107);
                //绘制得分
                context.setFontSize(28); //字体大小
                context.fillStyle = '#4fc089';
                context.setTextAlign('center')
                context.fillText(grade, 177, 48)
                // 绘制姓名
                context.setFontSize(16);
                context.fillStyle = '#000000';
                context.setTextAlign('center')
                context.fillText(userInfo.nickName, 167, 180);
                // 绘制头像
                context.drawImage(avatarUrl, 72, 157, 33, 33);

                context.draw(false, function() {
                    setTimeout(function() {
                        wx.canvasToTempFilePath({
                            width: 350,
                            height: 468,
                            destWidth: 700,
                            destHeight: 936,
                            canvasId: 'firstCanvas',
                            success: function(res) {
                                var tempFilePath = res.tempFilePath;
                                console.log("图片"+tempFilePath);
                                _this.setData({
                                    imagePath: tempFilePath,
                                    isCanvas: true
                                });
                                _this.upload(tempFilePath);

                            },
                            fail: function(res) {
                                console.log(res);
                            }
                        });
                    }, 1000);
                });
            },
        })
    },

因为我的项目需要,我上面做个很多判断,那些东西不需要管,重点就是,绘制图片drawimage方法 和绘制文字的方法,

我的绘制方法是让他们根据canvas上的一个坐标点居中绘制的,这个可以看一下。

还有就是,生成图片的尺寸要比画的尺寸大一倍,这样图片不会失真,比较清楚,也就是这个方法:

wx.canvasToTempFilePath() 前两个参数是canvas的大小,然后是生成图片的大小,canvas的ID

参加比赛的作品,开周期一个月,使用了 Wafer2 框架,后台采用腾讯云提供的 Node.js SDK 接入对象存储 API ,前端核心代码实现了类似于图片编辑器的功能,支持图片和文字的移动、旋转、缩放、生成预览图以及编辑状态的保存,动画部分采用 CSS 动画实现小程序中的模态输入框部分使用了自己封装的 InputBox 组件代码已移除 AppId 等敏感信息,可自行添加自己的 AppId 和 AppSecret 以配置后台环境,实现登录测试,详细添加方法见下文「使用方法」,若本地运行可通过修改 app.json 文件中 page 字段的顺序来查看不同页面微信小程序定制需求请联系作者微信:aweawds (注明来意)效果展示      使用方法首先点击右上角 Star ʕ •ᴥ•ʔ获取Demo代码执行 git clone https://github.com/goolhanrry/Weapp-Demo-LemonJournal.git或 点击此处 下载最新版本的代码解压后在微信开者工具中打开 Weapp-Demo-LemonJournal 文件夹即可如需进行登录测试,还要执行以下步骤准备好自己的 AppId 和 AppSecret(可在微信公众平台注册后获取)在 project.config.json 的 appid 字段中填入 AppId在 /client/utils/util.js 中相应位置填入 AppId 和 AppSecret在微信开者工具中重新导入整个项目,上传后台代码后编译运行即可核心代码组件的移动、旋转和缩放主要思路是把  标签(对应图片)和  标签(对应文字)封装在同一个自定义组件  中,通过对外暴露的 text 变量是否为空来进行条件渲染,然后绑定 onTouchStart() 、onTouchEnd() 和 onTouchMove() 三个事件来对整个组件的位置、角度、大小、层级以及 “旋转” 和 “移除” 两个按钮的行为进行操作onTouchStart: function (e) {     // 若未选中则直接返回     if (!this.data.selected) {         return     }     switch (e.target.id) {         case 'sticker': {             this.touch_target = e.target.id             this.start_x = e.touches[0].clientX * 2             this.start_y = e.touches[0].clientY * 2             break         }         case 'handle': {             // 隐藏移除按钮             this.setData({                 hideRemove: true             })             this.touch_target = e.target.id             this.start_x = e.touches[0].clientX * 2             this.start_y = e.touches[0].clientY * 2             this.sticker_center_x = this.data.stickerCenterX;             this.sticker_center_y = this.data.stickerCenterY;             this.remove_center_x = this.data.removeCenterX;             this.remove_center_y = this.data.removeCenterY;             this.handle_center_x = this.data.handleCenterX;             this.handle_center_y = this.data.handleCenterY;             this.scale = this.data.scale;             this.rotate = this.data.rotate;             break         }     } }, onTouchEnd: function (e) {     this.active()     this.touch_target = ''     // 显示移除按钮     this.setData({         removeCenterX: 2 * this.data.stickerCenterX - this.data.handleCenterX,         removeCenterY: 2 * this.data.stickerCenterY - this.data.handleCenterY,         hideRemove: false     })     // 若点击移除按钮则触移除事件,否则触刷新数据事件     if (e.target.id === 'remove') {         this.triggerEvent('removeSticker', this.data.sticker_id)     } else {         this.triggerEvent('refreshData', this.data)     } }, onTouchMove: function (e) {     // 若无选中目标则返回     if (!this.touch_target) {         return     }     var current_x = e.touches[0].clientX * 2     var current_y = e.touches[0].clientY * 2     var diff_x = current_x - this.start_x     var diff_y = current_y - this.start_y     switch (e.target.id) {         case 'sticker': {             // 拖动组件则所有控件同时移动             this.setData({                 stickerCenterX: this.data.stickerCenterX   diff_x,                 stickerCenterY: this.data.stickerCenterY   diff_y,                 removeCenterX: this.data.removeCenterX   diff_x,                 removeCenterY: this.data.removeCenterY   diff_y,                 handleCenterX: this.data.handleCenterX   diff_x,                 handleCenterY: this.data.handleCenterY   diff_y             })             break         }         case 'handle': {             // 拖动操作按钮则原地旋转缩放             this.setData({                 handleCenterX: this.data.handleCenterX   diff_x,                 handleCenterY: this.data.handleCenterY   diff_y             })             var diff_x_before = this.handle_center_x - this.sticker_center_x;             var diff_y_before = this.handle_center_y - this.sticker_center_y;             var diff_x_after = this.data.handleCenterX - this.sticker_center_x;             var diff_y_after = this.data.handleCenterY - this.sticker_center_y;             var distance_before = Math.sqrt(diff_x_before * diff_x_before   diff_y_before * diff_y_before);             var distance_after = Math.sqrt(diff_x_after * diff_x_after   diff_y_after * diff_y_after);             var angle_before = Math.atan2(diff_y_before, diff_x_before) / Math.PI * 180;             var angle_after = Math.atan2(diff_y_after, diff_x_after) / Math.PI * 180;             this.setData({                 scale: distance_after / distance_before * this.scale,                 rotate: angle_after - angle_before   this.rotate             })             break         }     }     this.start_x = current_x;     this.start_y = current_y; }编辑状态的保存一篇手帐包含的组件类型包括 sticker(软件自带的贴纸)、image(用户上传的图片)和 text(自定义文字)三种,全部保存在一个如下格式的 json 对象中,每个独立组件都包含了一个不重复的 id 以及相关的信息,保存时由客户端生成该对象并编码成 json 字符串存储在数据库,恢复编辑状态时通过解析 json 字符串获得对象,再由编辑页面渲染{     "backgroundId": "5",                                        背景图id     "assemblies": [         {             "id": "jhjg",                                       组件id             "component_type": "image",                          组件类型(自定义图片)             "image_url": "https://example.com/jhjg.png",        图片地址             "stickerCenterX": 269,                              中心横坐标             "stickerCenterY": 664,                              中心纵坐标             "scale": 1.7123667831396403,                        缩放比例             "rotate": -3.0127875041833434,                      旋转角度             "wh_scale": 1,                                      图片宽高比             "z_index": 19                                       组件层级         },         {             "id": "gs47",             "component_type": "text",                           组件类型(文字)             "text": "test",                                     文字内容             "stickerCenterX": 479,             "stickerCenterY": 546,             "scale": 1.808535318980528,             "rotate": 29.11614626607893,             "z_index": 10         },         {             "id": "chjn",             "component_type": "sticker",                        组件类型(贴纸)             "sticker_type": "food",                             贴纸类型             "sticker_id": "1",                                  贴纸id             "image_url": "https://example.com/weapp/stickers/food/1.png",             "stickerCenterX": 277,             "stickerCenterY": 260,             "scale": 1.3984276885130673,             "rotate": -16.620756913892055,             "z_index": 5         }     ] }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值