uniapp实现微信小程序端动态生成海报

这篇具有很好参考价值的文章主要介绍了uniapp实现微信小程序端动态生成海报。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. 背景:

基于uniapp实现微信小程序中商品详情的海报生成与保存

  1. 效果图:

uniapp 生成海报,微信小程序,小程序,Powered by 金山文档
  1. 思路:

  1. 首先把海报上需要的内容准备好,比如用户头像,商品信息,二维码等。需要注意的是,因为二维码是动态生成的,所以需要后端传给我们,前端需要把路径和参数传给后端,后端请求微信服务接口,通过配置对应的参数就可以生成一个二维码啦,再将二维码发送给前端。

  1. 图片不能是网络图片,我们需要的是本地图片,如非本地图片,那我们需要对图片进行处理。uniapp可以通过uni.getImageInfo获取图片本地路径(uni.chooseImage(OBJECT) | uni-app官网 (dcloud.net.cn))

  1. 如果通过临时路径来保存图片,canvas一倍会比较模糊,我们可以加到2或3倍,太大安卓机容易出问题,我们可以采用设备的像素作为倍率,uni.getSystemInfo获取设备信息

  1. 创建canvas绘制上下文,在绘制canvas的时候,把两个图片都切割成圆形的图片,然后中间就出现了一条线,在之后对原价进行划线删除状态的时候设置划线的颜色,会影响圆形的边线颜色。然后才知道,canvas绘图是在JS中完成,只能通过JS代码来控制,JS提供了beginPath()和closePath()两个函数来控制canvas中模块的开始和结束,这样就可以避免属性覆盖。

  1. 补充:和后端交互生成二维码,需要传参数,要注意页面路径不能再根目录前不增加/,携带的参数最大为32个可见字符,且字符有要求限制,如果不符合规范就会生成二维码失败

  1. 在页面的onLoad生命周期参数options判断options.scene是否存在,存在就是扫描二维码进入,需要对二维码返回的参数进行处理,先根据官方文档来进行解码,decodeURIComponent会将%3D解码成=,我传参的时候是通过&符号将参数之间隔开的,所以我这里使用split('&'),将解码后的字符串通过&符号分割成数组,循环数组得到键名和键值,代码如下(扫普通链接二维码打开小程序 | 微信开放文档 (qq.com))

  1. 生成、获取二维码代码

onLoad: function(options) {
    if (options.scene) { //二维码进入
                const scene1 = decodeURIComponent(options.scene);
                let a = scene1.toString().split('&');
                let obj = {}
                for (let i = 0; i < a.length; i++) {
                    // 获取=前
                    let index = a[i].indexOf("=")
                    let name = a[i].substring(0, index);//键名
                    // 获取=后
                    let index1 = a[i].indexOf("=")
                    let value = a[i].substring(index1 + 1, a[i].length);
                    obj[name] = value //键值
                }
                this.goodsId = obj.id //获取商品id
                this.getType = obj.type; //获取进入页面的方式
       } else{//非二维码进入

       }
}
//生成二维码向后端传递的参数
let data = {
      page: 'page_my/pages/chengbei-goods-details/index',//路径
     scene: `id=${that.goodsId}&code=${that.userCode}&type=${that.getType}`, //参数
}
  1. HTML代码如下

<!-- 海报 -->
        <canvas :style="{height: pupopHeight + 'px',width: pupopWidth + 'px'}" canvas-id="myCanvas"></canvas>
        <uni-popup ref="popup" type="center">
            <view class="popup-wrap" :style="'width:' + width + 'px'">
                <!-- <view class="popup-head">
                    生成海报
                    <view @click="close_popup()" class="close_icon"></view>
                </view> -->
                <image :src="posterImg" mode="widthFix" style="width: 100%;"></image>
                <view class="popup-footer">
                    <view class="buttons margin_l30 marTop20" @click="saveToLocal()">保存到相册</view>
                    <view class="buttonNo margin_l30 marTop20" @click="closeToLocal()">取消</view>
                    <!-- <view class="tips">保存图片到相册,你就可以分享啦!</view> -->
                </view>
            </view>
        </uni-popup>
        <!-- 海报end -->
  1. JS代码如下文章来源地址https://www.toymoban.com/news/detail-532625.html

data(){
    return{
                // 海报
                pupopWidth: 590,
                pupopHeight: 900,
                pixelRatio: 3, //屏幕像数密度
                inviteQR: '', //动态二维码
                posterImg: '', //最后生成的海报
                width: 100, //当前手机宽度
                // 海报end   
    }
},
onLoad(){
            let that = this
            uni.getSystemInfo({
                success: function(res) {
                    that.width = res.windowWidth * 0.8 //获取海报弹出层的宽度
                    that.pixelRatio = res.pixelRatio //获取像素比
                }
            })
            this.pupopWidth = this.pupopWidth * this.pixelRatio
            this.pupopHeight = this.pupopHeight * this.pixelRatio
            console.log("屏幕像素密度", this.pixelRatio, this.pupopWidth, this.pupopHeight)
},
methods:{
            // 生成海报
            shareing() {
                this.isHideSharePopor()
                uni.showLoading({
                    title: "海报生成中...",
                    mask: true
                })

                //#ifdef MP-WEIXIN
                //这里参数是前端和后端商议好,生成二维码需要前端传那些对应的值,这里我传了当前页面的路径和邀请码及当前页面的参数
                
                this.inviteQR = this.ewmImgUrl //这里是获取后端传来的二维码,先给写死了
                this.createPoster();
                //#endif

            },
            //生成海报--微信端
            createPoster() {
                let _this = this
                _this.headImg = uni.getStorageSync('avatarUrl') //去本地缓存获取头像
                uni.getImageInfo({
                    src: _this.headImg,
                    success(image) {
                        const canvasId = "myCanvas"
                        let ctx = uni.createCanvasContext(canvasId, _this) // 自定义组件中 一定要传this 
                        // 填充背景
                        ctx.setFillStyle('#FAFAFA')
                        ctx.fillRect(0, 0, _this.pupopWidth, _this.pupopHeight);
                        ctx.save()

                        // 头像和二维码大小都需要在规定大小的基础上放大像素比的比例后面都会*this.pixelRatio
                        let headerW = 102 * _this.pixelRatio
                        let headerX = 40 * _this.pixelRatio
                        let headerY = 40 * _this.pixelRatio
                        // 控制头像为圆形
                        ctx.beginPath()
                        ctx.setStrokeStyle('rgba(0,0,0,.2)') //设置线条颜色,如果不设置默认是黑色,头像四周会出现黑边框
                        ctx.arc(headerX + headerW / 2, headerY + headerW / 2, headerW / 2, 0, 2 * Math.PI)
                        ctx.stroke() //画出当前路径的边框
                        ctx.clip()//画完之后执行clip()方法,否则不会出现圆形效果
                        ctx.drawImage(image.path, headerX, headerY, headerW, headerW)// 将头像画到画布上
                        ctx.restore()
                        ctx.strokeStyle = '#EEEEEE';
                        ctx.save()
                        ctx.closePath()
                        //绘制小程序名字
                        const uniqueCode = "木之本樱";
                        let invateCode = `${uniqueCode}`
                        let invateCodeX = headerX + headerW + 14 * _this.pixelRatio
                        let invateCodeY = headerY + (40 * _this.pixelRatio)
                        ctx.setFontSize(26 * _this.pixelRatio);
                        ctx.setFillStyle('#333333');
                        ctx.fillText(invateCode, invateCodeX, invateCodeY);
                        ctx.stroke();
                        //绘制广告语
                        let invateCode1 = "广告语"
                        let invateCodeX1 = headerX + headerW + 14 * _this.pixelRatio
                        let invateCodeY1 = headerY + (84 * _this.pixelRatio)
                        ctx.setFontSize(26 * _this.pixelRatio);
                        ctx.setFillStyle('#333333');
                        ctx.fillText(invateCode1, invateCodeX1, invateCodeY1);
                        ctx.stroke();
                        //生成banner图
                        uni.getImageInfo({
                            src: _this.goodsThumbnailUrl, //这里的banner是展示的商品图 
                            success(image) {
                                let bannerW = 510 * _this.pixelRatio
                                let bannerH = 510 * _this.pixelRatio
                                let bannerX = 40 * _this.pixelRatio
                                let bannerY = 40 * _this.pixelRatio + headerW + 24 * _this.pixelRatio
                                // 控制商品图片为圆形
                                ctx.beginPath()
                                ctx.setStrokeStyle('rgba(0,0,0,.2)') //设置线条颜色,如果不设置默认是黑色,头像四周会出现黑边框
                                ctx.arc(bannerX + bannerW / 2, bannerY + bannerW / 2, bannerW / 2, 0, 2 * Math.PI)
                                // ctx.strokeStyle = 'red'; //设置线的颜色状态
                                ctx.stroke()
                                //画完之后执行clip()方法,否则不会出现圆形效果
                                ctx.clip()
                                // 将商品主图画到画布上
                                ctx.drawImage(image.path, bannerX, bannerY, bannerW, bannerH)
                                ctx.restore()
                                ctx.save()
                                ctx.closePath()
                                //现价
                                let bannerTextX = 40 * _this.pixelRatio
                                let bannerTextY = bannerY + bannerH + 20 * _this.pixelRatio + 50 * _this.pixelRatio //这里的y轴起始值是顶上的距离还要特意加上文字的行高
                                let chr = `¥${_this.oldPrice}`; //这个方法是将一个字符串分割成字符串数组
                                ctx.setFontSize(50 * _this.pixelRatio);
                                ctx.setFillStyle('#EA5506');
                                ctx.fillText(chr, bannerTextX, bannerTextY);
                                ctx.stroke();
                                // 测试当前现价的宽度 
                                let metrics = ctx.measureText(`¥${_this.oldPrice}`)
                                // 划线价
                                let bannerTextX1 = 40 * _this.pixelRatio + metrics.width + 20 * _this
                                    .pixelRatio
                                let bannerTextY1 = bannerY + bannerH + 20 * _this.pixelRatio + 50 * _this
                                    .pixelRatio //这里的y轴起始值是顶上的距离还要特意加上文字的行高
                                let chr1 = `¥${_this.newPrice}`; //这个方法是将一个字符串分割成字符串数组
                                ctx.setFontSize(26 * _this.pixelRatio);
                                ctx.setFillStyle('#999999');
                                ctx.fillText(chr1, bannerTextX1, bannerTextY1);
                                ctx.stroke();
                                // 测试原价的宽度
                                let metrics1 = ctx.measureText(`¥${_this.newPrice}`)
                                //画字体删除线
                                ctx.beginPath()
                                ctx.moveTo(bannerTextX1, bannerTextY1 - 10 * _this.pixelRatio); //移动到指定位置 X Y
                                //设置起点状态
                                ctx.lineTo(bannerTextX1 + metrics1.width, bannerTextY1 - 10 * _this
                                    .pixelRatio);
                                //设置末端状态
                                ctx.lineWidth = 1 * _this.pixelRatio; //设置线宽状态
                                ctx.strokeStyle = '#999999'; //设置线的颜色状态
                                // ctx.setStrokeStyle('#999999')
                                ctx.stroke();
                                ctx.closePath()
                                //二维码
                                uni.getImageInfo({
                                    src: _this.inviteQR,
                                    success(res) {
                                        // 画当前页面的二维码 _this.pupopWidth

                                        const img_w = 160 * _this.pixelRatio
                                        const img_x = _this.pupopWidth - 40 * _this.pixelRatio -
                                            img_w
                                        const img_y = bannerY + bannerH + 20 * _this.pixelRatio
                                        ctx.drawImage(res.path, img_x, img_y, img_w, img_w)
                                        // 商品名字 goodsName
                                        //这里会处理多行显示文字,超出显示省略号的效果 
                                        let bannerTextX = 40 * _this.pixelRatio
                                        let bannerTextY = bannerY + bannerH + 20 * _this.pixelRatio + 150 * _this.pixelRatio //这里的y轴起始值是顶上的距离还要特意加上文字的行高
                                        let chr = _this.goodsName.split(""); //这个方法是将一个字符串分割成字符串数组
                                        let temp = "";
                                        let row = [];
                                        ctx.setFontSize(30 * _this.pixelRatio)
                                        ctx.setFillStyle("#333333")
                                        for (var a = 0; a < chr.length; a++) {
                                            if (ctx.measureText(temp).width < 300 * _this.pixelRatio) {
                                                temp += chr[a];
                                            } else {
                                                a--; //这里添加了a-- 是为了防止字符丢失,效果图中有对比
                                                row.push(temp);
                                                temp = "";
                                            }
                                        }
                                        row.push(temp);
                                        if (row.length > 2) {
                                            let rowCut = row.slice(0, 1);
                                            let rowPart = rowCut[0];
                                            let test = "";
                                            let empty = [];
                                            for (var a = 0; a < rowPart.length; a++) {
                                                if (ctx.measureText(test).width < 300 * _this.pixelRatio) {
                                                    test += rowPart[a];
                                                } else {
                                                    break;
                                                }
                                            }
                                            empty.push(test);
                                            var group = empty[0] + "..." //这里只显示1行,超出的用...表示
                                            rowCut.splice(0, 1, group);
                                            row = rowCut;
                                        }
                                        for (var b = 0; b < row.length; b++) {
                                            ctx.fillText(row[b], bannerTextX, bannerTextY + b *50 * _this.pixelRatio, 510 * _this.pixelRatio);
                                        }
                                        ctx.draw(false, () => {
                                            uni.canvasToTempFilePath({
                                                width: _this.pupopWidth,
                                                height: _this.pupopHeight,
                                                destWidth: _this.pupopWidth,
                                                destHeight: _this.pupopHeight,
                                                canvasId: canvasId,
                                                fileType: 'png',
                                                quality: 1,
                                                success: function(res) {
                                                    _this.posterImg = res
                                                        .tempFilePath; //最终将canvas转换为图片
                                                    _this.$refs.popup.open();
                                                    uni.hideLoading()

                                                },
                                                fail(error) {
                                                    console.log('4', error)
                                                    // appEv.arrTips("生成海报失败,请稍后重试!")
                                                    setTimeout(() => {
                                                        uni.hideLoading()
                                                    }, 2000)
                                                }
                                            }, _this)
                                        })
                                    },
                                    fail(error) {
                                        console.log('获取二维码失败', error)
                                        // appEv.arrTips("生成海报失败,获取二维码失败")
                                        setTimeout(() => {
                                            uni.hideLoading()
                                        }, 2000)
                                    }
                                })
                            },
                            fail(error) {
                                console.log('生成商品图失败', error)
                                // appEv.arrTips("生成海报失败,获取商品图失败")
                                setTimeout(() => {
                                    uni.hideLoading()
                                }, 2000)


                            }
                        });

                    },
                    fail(error) {
                        console.log('生成头像失败', error)
                        // appEv.arrTips("生成海报失败,获取头像失败")
                        setTimeout(() => {
                            uni.hideLoading()
                        }, 2000)

                    }
                })
            },
            // 取消保存
            closeToLocal() {
                this.$refs.popup.close()
            },
            //将图片保存到本地相册
            saveToLocal() {
                //#ifdef MP-WEIXIN
                uni.saveImageToPhotosAlbum({
                    filePath: this.posterImg,
                    success: () => {
                        console.log('保存到相册成功')
                        // appEv.arrTips("保存到相册成功")
                        this.$refs.popup.close()
                    },
                    fail: (err) => {
                        console.log("保存到相册失败", err)
                    }
                });
                //#endif 
            },
},

到了这里,关于uniapp实现微信小程序端动态生成海报的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包