声明:本站文章均为作者个人原创,图片均为实际截图。如有需要请收藏网站,禁止转载,谢谢配合!!!

本篇博客已配套视频讲解教程, 点击查看 Bilibili 视频教程



1.封装组件

<template>
    <view class="content" v-if="isShow" @click.stop="isShow=false">
        <canvas @click.stop="" :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="my-canvas"></canvas>
        <view class="save-btn" @click.stop="saveImage">保存图片</view>
    </view>
</template>

<script>
    export default{
        props:{
            headerImg:{
                type: String,
                default: 'https://zhaixiandaojia.com/public/uploads/20210123/f87969714ba1d865f2366b997cbd9fd8.jpg'
            },
            title:{
                type: String,
                default: '草莓千层蛋糕'
            },
            subTitle:{
                type: String,
                default: '鲜嫩多汁的草莓融合香甜奶油'
            },
            price:{
                type: Number,
                default: 36.89
            },
            content:{
                type: String,
                default: ""
            },
            abImg:{
                type: String,
                default: 'https://zhaixiandaojia.com/public/uploads/20210123/f87969714ba1d865f2366b997cbd9fd8.jpg'
            }
        },
        data(){
            return {
                canvasW: 0,
                canvasH: 0,
                ctx: null,
                isShow: false,
                qrcode: ''
            }
        },
        methods:{
            //显示
            showCanvas(headerImg, qrcode){
                this.isShow = true
                this.qrcode = qrcode
                this.headerImg = headerImg
                this.__init()
            },
            //初始化画布
            async __init(){
                uni.showLoading({
                    title: '加载中...',
                    mask: true
                })
                this.ctx = uni.createCanvasContext('my-canvas',this)
                this.canvasW = uni.upx2px(550);
                this.canvasH = uni.upx2px(900);
                //设置画布背景透明
                this.ctx.setFillStyle('rgba(255, 255, 255, 0)')
                //设置画布大小
                this.ctx.fillRect(0,0,this.canvasW,this.canvasH)
                //绘制圆角背景
                this.drawRoundRect(this.ctx, 0, 0, this.canvasW, this.canvasH,uni.upx2px(18),'#FFFFFF')
                //获取标题图片
                 let headerImg = await this.getImageInfo(this.headerImg)
                let hW = uni.upx2px(500);
                let hH = uni.upx2px(500);
                //绘制标题图
                this.drawRoundImg(this.ctx,headerImg.path,((this.canvasW-hW) / 2),((this.canvasW-hW) / 2),hW,hH,uni.upx2px(16)) 

            /*     let headerImg = await this.getImageInfo(this.headerImg)
                let hW = uni.upx2px(500);
                let hH = uni.upx2px(500); */
                //绘制标题图
                /* this.drawRoundImg(this.ctx,headerImg.path,((this.canvasW-hW) / 2),((this.canvasW-hW) / 2),hW,hH,uni.upx2px(16)) */

                //绘制标题
                this.ctx.setFontSize(14); //设置标题字体大小
                this.ctx.setFillStyle('#333'); //设置标题文本颜色
                this.ctx.fillText(this.title,((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + uni.upx2px(60)))
                //绘制副标题
                // this.ctx.setFontSize(14);
                // this.ctx.setFillStyle('#858585');
                // let sWidth = this.ctx.measureText(this.subTitle).width
                // if(sWidth > hW){
                //     this.ctx.fillText(this.subTitle.slice(0,17) + '...',((this.canvasW-hW) / 2),(
                //     ((this.canvasW-hW) / 2) + hH + uni.upx2px(110)))
                // }else{
                //     this.ctx.fillText(this.subTitle,((this.canvasW-hW) / 2),(
                //     ((this.canvasW-hW) / 2) + hH + uni.upx2px(110)))
                // }
                //绘制价格
                if(this.content){        
                    //console.log(this.content)
                    this.ctx.setFontSize(14);
                    this.ctx.setFillStyle('#999');
                    /* this.ctx.fillText('¥',((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + uni.upx2px(120))) */
                    this.ctx.setFontSize(12);
                    this.ctx.fillText(this.content,(((this.canvasW-hW) / 2) + uni.upx2px(36)),(((this.canvasW-hW) / 2) + hH + uni.upx2px(120)))
                }
                //绘制虚线
                this.drawDashLine(this.ctx,uni.upx2px(20),(((this.canvasW-hW) / 2) + hH + uni.upx2px(150)),(this.canvasW-uni.upx2px(20)),(((this.canvasW-hW) / 2) + hH + uni.upx2px(150)),5)
                //左边实心圆
                this.drawEmptyRound(this.ctx,0,(((this.canvasW-hW) / 2) + hH + uni.upx2px(150)),uni.upx2px(20))
                //右边实心圆
                this.drawEmptyRound(this.ctx,this.canvasW,(((this.canvasW-hW) / 2) + hH + uni.upx2px(150)),uni.upx2px(20))
                //提示文案
                this.ctx.setFontSize(12);
                this.ctx.setFillStyle('#858585');
                this.ctx.fillText('测试文字',(((this.canvasW-hW) / 2) + uni.upx2px(20)),(((this.canvasW-hW) / 2) + hH + uni.upx2px(200)))
                //底部广告
                // let BottomAdImg = await this.getImageInfo(this.abImg)
                // this.ctx.drawImage(BottomAdImg.path,(((this.canvasW-hW) / 2)),(((this.canvasW-hW) / 2) + hH + uni.upx2px(310)),uni.upx2px(350),uni.upx2px(110))
                 let BottomAdImg = await this.getImageInfo('https://ab.com/logo.png')
                 this.ctx.drawImage(BottomAdImg.path, 50, 380, 50, 50)
                //小程序码
                let qrcodeImg = await this.getImageInfo('https://ab.com/mp_qrcode.jpg')
                this.ctx.drawImage(qrcodeImg.path,uni.upx2px(384),(((this.canvasW-hW) / 2) + hH + uni.upx2px(200)), uni.upx2px(156), uni.upx2px(156))
                //延迟渲染
                setTimeout(()=>{
                    this.ctx.draw(true,()=>{
                        uni.hideLoading()
                    })
                },500)
            },
            //绘制实心圆
            drawEmptyRound(ctx,x,y,radius){
                ctx.save()
                ctx.beginPath();
                ctx.arc(x, y, radius, 0, 2 * Math.PI,true);
                ctx.setFillStyle('rgba(0, 0, 0, .4)')
                ctx.fill();
                ctx.restore()
                ctx.closePath()
            },
            //绘制虚线
            drawDashLine(ctx,x1,y1,x2,y2,dashLength){
                ctx.setStrokeStyle("#cccccc")//设置线条的颜色
                ctx.setLineWidth(1)//设置线条宽度
                var dashLen = dashLength,
                xpos = x2 - x1, //得到横向的宽度;
                ypos = y2 - y1, //得到纵向的高度;
                numDashes = Math.floor(Math.sqrt(xpos * xpos + ypos * ypos) / dashLen); 
                //利用正切获取斜边的长度除以虚线长度,得到要分为多少段;
                for(var i=0; i<numDashes; i++){
                 if(i % 2 === 0){
                     ctx.moveTo(x1 + (xpos/numDashes) * i, y1 + (ypos/numDashes) * i); 
                     //有了横向宽度和多少段,得出每一段是多长,起点 + 每段长度 * i = 要绘制的起点;
                  }else{
                      ctx.lineTo(x1 + (xpos/numDashes) * i, y1 + (ypos/numDashes) * i);
                  }
                }
                ctx.stroke();
            },
            //带圆角图片
            drawRoundImg(ctx, img, x, y, width, height, radius){
                ctx.beginPath()
                ctx.save()
                // 左上角
                ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5)
                // 右上角
                ctx.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2)
                // 右下角
                ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5)
                // 左下角
                ctx.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI)
                ctx.stroke()
                ctx.clip()
                ctx.drawImage(img, x, y, width, height);
                ctx.restore()
                ctx.closePath()
            },
            //圆角矩形
            drawRoundRect(ctx, x, y, width, height, radius, color){
                ctx.save();
                ctx.beginPath();
                ctx.setFillStyle(color); 
                ctx.setStrokeStyle(color)
                ctx.setLineJoin('round');  //交点设置成圆角
                ctx.setLineWidth(radius);
                ctx.strokeRect(x + radius/2, y + radius/2, width - radius , height - radius );
                ctx.fillRect(x + radius, y + radius, width - radius * 2, height - radius * 2);
                ctx.stroke();
                ctx.closePath();
            },
            //获取图片
            getImageInfo(imgSrc){
                return new Promise((resolve, reject) => {
                    console.log("图片链接",imgSrc)
                    console.log("图片类型",typeof imgSrc)
                    uni.getImageInfo({
                        src: imgSrc,
                        success: (image) => {
                            resolve(image);
                            console.log('获取图片成功',image)
                        },
                        fail: (err) => {
                            reject(err);
                            console.log('获取图片失败',err)
                        }
                    });
                });
            },
            //保存图片到相册
            saveImage(){
                //判断用户授权
                // uni.getSetting({
                //    success(res) {
                //       console.log('获取用户权限',res.authSetting)
                //       if(Object.keys(res.authSetting).length>0){
                //           //判断是否有相册权限
                //           if(res.authSetting['scope.writePhotosAlbum']==undefined){
                //               //打开设置权限
                //               uni.openSetting({
                //                 success(res) {
                //                   console.log('设置权限',res.authSetting)
                //                 }
                //               })
                //           }else{
                //               if(!res.authSetting['scope.writePhotosAlbum']){
                //                   //打开设置权限
                //                   uni.openSetting({
                //                     success(res) {
                //                       console.log('设置权限',res.authSetting)
                //                     }
                //                   })
                //               }
                //           }
                //       }else{
                //           return
                //       }
                //    }
                // })
                var that = this
                uni.showLoading({
                    title: '正在加载',
                    mask: false
                });
                uni.canvasToTempFilePath({
                    canvasId: 'my-canvas',
                    quality: 1,
                    complete: (res) => {
                        console.log('保存到相册',res);
                        uni.saveImageToPhotosAlbum({
                            filePath: res.tempFilePath,
                            success(res) {
                                uni.hideLoading();
                                uni.showToast({
                                    title: '已保存到相册',
                                    icon: 'success',
                                    duration: 2000
                                })
                                setTimeout(()=>{
                                    that.isShow = false
                                },2000)
                            }
                        })
                    }
                },this);
            }
        }
    }
</script>

<style scoped lang="scss">
.content{
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9999999;
    background: rgba(0,0,0,.4);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .save-btn{
        margin-top: 35rpx;
        color: #FFFFFF;
        background: linear-gradient(to right, #FE726B , #FE956B);
        padding: 15rpx 40rpx;
        border-radius: 50rpx;
    }
}
</style>

2.使用组件

<qrcode-poster ref="poster" :title="title" :subTitle="subTitle" :headerImg="headerImg"
            :content="content"></qrcode-poster>
import QrcodePoster from '@/components/poster/poster.vue'

export default{
    components:{
        QrcodePoster
    },
    data(){
        return{
            headerImg: '',
            title: '测试标题',
            subTitle: '测试标题',
            content: ""
        }
    },
    methods: {
        test(){
            this.$refs.poster.showCanvas(this.headerImg, item.logo)
        }
    }
}