uniapp 小程序 canvas海报---已封装

在这里插入图片描述
已封装好的代码

<template>
    <view class="canvas-content">
        <!-- 海报 -->

        <canvas class="canvas" :style="'width: ' + canvasWith + 'px; height: ' + canvasHeight + 'px;'" canvas-id="myCanvas"></canvas>
        <view class="button-wrapper">
            <!-- 保存海报按钮 -->
            <button class="save-btn" @click="saveCanvasImage" v-if="isPhoto">保存海报</button>
           <button class="save-btn" open-type="openSetting" @opensetting="getPhotoSetting" v-else>保存海报</button>
        </view>
		       <view class="del-icon"><image @click="handleCanvasCancel" src="../../static/canvas/del.png" mode=""></image></view>
    </view>
</template>

<script>
export default {
    
    
    data() {
    
    
        return {
    
    
            bgScale: 0.914, // 头部背景缩放比例 320 / canvasWith(350)
            aScale: 0.2, // 二维码缩放比例 120 / canvasWith(350)
            screenWidth: 750,
            canvasWith: 650,
            canvasHeight: 900,
            bgPic: {
    
    
                // 圆角背景宽高
                w: 320,
                h: 320
            },
            codePic: {
    
    
                // 二维码宽高
                w: 120,
                h: 120
            },
            codeImg: 'https://a.mushihulian.com/group1/M00/00/AE/rBGrr1-Pox-AJ6wLAAGlJGxOb4E997.png',
            isPhoto: true,
            canvasBgLink: '',
            canvasShow: false,
            system: null,
            attrs: {
    
    }
        };
    },
    props: {
    
    
        canvasAttr: {
    
    
            type: Object
        }
    },
    // 监听页面显示
    onShow() {
    
            
    },
    mounted() {
    
    
        this.isSavePic();
    },
    methods: {
    
    
        // 保存图片的授权设置(二次授权情况下触发)
        getPhotoSetting(e) {
    
    
            const isTrue = e.detail.authSetting['scope.writePhotosAlbum'];
            this.isPhoto = isTrue ? true : false;
        },
        // 检测是否有授权保存图片功能,以显示不同按钮
        isSavePic() {
    
    
            uni.getSetting({
    
    
                success: res => {
    
    
                    const isTrue = res.authSetting['scope.writePhotosAlbum'];
                    if (isTrue === undefined) {
    
    
                        this.isPhoto = true;
                    } else {
    
    
                        this.isPhoto = isTrue ? true : false;
                    }
                }
            });
        },
        // 获取设备信息
        getSystem() {
    
    
            const system = uni.getSystemInfoSync();
            // console.log(system, '设备信息');
            const ratio = this.screenWidth / system.windowWidth;
            this.canvasHeight = this.canvasHeight / ratio;
            this.canvasWith = this.canvasWith / ratio;
            this.bgPic = {
    
    
                // 圆角白色背景宽高
                w: this.canvasWith * this.bgScale,
                h: this.canvasWith * this.bgScale
            };
            this.codePic = {
    
    
                // 二维码宽高
                w: this.canvasWith * this.aScale,
                h: this.canvasWith * this.aScale
            };
            
        },
        // 展示海报
        posterShow() {
    
    
            this.getSystem();
            // this.isSavePic();
            console.log(this.codeImg,this.canvasAttr, '咯');
            Object.assign(this.attrs, this.canvasAttr);
            this.creatPoster(this.attrs);
        },
        // 生成canvas海报
        creatPoster(canvasAttr) {
    
    
            const canvasWith = this.canvasWith;
            const canvasHeight = this.canvasHeight;
            const ctx = uni.createCanvasContext('myCanvas', this);
            // console.log(canvasAttr, '娃哈哈');
            ctx.draw(); //清空之前的海报
            ctx.save();

            // 圆角背景图
            ctx.restore();
            this.roundRect(ctx, 0,0, canvasWith, canvasHeight, 10, '#fff', canvasAttr.bgImg, true);
            ctx.save(); 
			 
			 // 白色背景
            ctx.restore();
			// this.roundRect(ctx,this.canvasWith*0.044, canvasHeight-172, this.bgPic.h, 160, 10, '#fff');
			this.roundRect(ctx,12, canvasHeight-172, this.bgPic.h, 160, 10, '#fff');
            ctx.save();
   
			// 画圆形头像
			ctx.restore();
			this.drawCircular(
				ctx,canvasAttr,
				this.canvasWith*0.1,
				this.canvasHeight*0.55,
				40 ,40
			);
			ctx.save();
			// 文字
			ctx.setFillStyle('#000000'); // 文字颜色
			ctx.setFontSize(15); // 文字字号
			ctx.textAlign="start"; 
			ctx.fillText('昵称一样', 76, canvasHeight-135); // 绘制文字
			
			ctx.setFillStyle('#666666'); // 文字颜色
			ctx.setFontSize(15); // 文字字号
			ctx.textAlign="start"; 
			ctx.fillText('2020-09-16', 76, canvasHeight-116); // 绘制文字

			// ctx.beginPath();
			// // 设置线宽
			// ctx.lineWidth = 1;
			// 移动画笔至坐标 x20 y20 的位置
			ctx.moveTo(29, canvasHeight-100);
			// 绘制到坐标 x20 y100 的位置
			ctx.lineTo(this.bgPic.h-6, canvasHeight-100);
			// 填充颜色
			ctx.strokeStyle="#C4C4C4";
			// // 开始填充
			// ctx.stroke();
			// ctx.closePath();
			// ctx.save();
			

			
            // 画二维码
            ctx.restore();
            ctx.drawImage(canvasAttr.codeImg, this.canvasWith*0.69, canvasHeight-164, this.codePic.w, this.codePic.w);
            ctx.save();
            			
			ctx.setFillStyle('#666666'); 
			ctx.setFontSize(12); 
			ctx.textAlign="start"; 
			ctx.fillText('累计打卡', 32, canvasHeight-76);
			
			ctx.setFillStyle('#000000'); // 
			ctx.setFontSize(26);
			ctx.textAlign="end"; 
			ctx.fillText('210', this.bgPic.h-20, canvasHeight-70); 
			
			ctx.setFillStyle('#666666'); // 
			ctx.setFontSize(12);
			ctx.textAlign="end"; 
			ctx.fillText('天', this.bgPic.h-6, canvasHeight-70); 
			
			ctx.strokeStyle="#C4C4C4";//左
			ctx.moveTo(29, canvasHeight-60);
			// 绘制到坐标 x20 y100 的位置
			ctx.lineTo(this.bgPic.h-6, canvasHeight-60);
			ctx.stroke();
			
			ctx.setFillStyle('#666666');
			ctx.setFontSize(12); 
			ctx.textAlign="start"; 
			ctx.fillText('完成一亿部', 32, canvasHeight-40);
			
			ctx.setFillStyle('#666666');
			ctx.setFontSize(12); 
			ctx.textAlign="start"; 
			ctx.fillText('累计打卡', 32, canvasHeight-23);//经书名称
			
			ctx.setFillStyle('#000000');
			ctx.setFontSize(26);
			ctx.textAlign="end"; 
			ctx.fillText('210', this.bgPic.h-20, canvasHeight-28); //经书数量
			
			ctx.setFillStyle('#666666'); 
			ctx.setFontSize(12);
			ctx.textAlign="end"; 
			ctx.fillText('部', this.bgPic.h-6, canvasHeight-28); 
			
			
			
			//左右边距线
			// ctx.strokeStyle="red";//右
			// ctx.moveTo(this.bgPic.h-6,canvasHeight-180);
			// ctx.lineTo(this.bgPic.h-6,canvasHeight);
			// ctx.stroke();
			
			// ctx.strokeStyle="red";//右
			// ctx.moveTo(this.bgPic.h-20,canvasHeight-100);
			// ctx.lineTo(this.bgPic.h-20,canvasHeight);
			// ctx.stroke();
			
			// ctx.strokeStyle="red";//左
			// ctx.moveTo(29,canvasHeight-180);
			// ctx.lineTo(29,canvasHeight);
			// ctx.stroke();
						
			//绘制结束
			ctx.draw(true);
           
        },
        /**
         * 画圆形头像
         * @param { object } ctx : canvas上下文
         * @param { number } x : 圆心 x 轴坐标
         * @param { number } y : 圆心 y 轴坐标
         * @param { number } width : canvas上下文
         * @param { number } ctx:canvas上下文
         * 是否逆时针旋转: false 代表顺时针旋转
         * @author: wudidi
         */
        drawCircular(ctx, canvasAttr, x, y, width, height) {
    
    
            ctx.save();
            let avatar_width = width,
                avatar_heigth = height,
                avatar_x = x,
                avatar_y = y;

            // 开始绘制路径
            ctx.beginPath();
            // 绘制圆的路径   0 即 0°是从三点钟方向开始的
            ctx.arc(avatar_width / 2 + avatar_x, avatar_heigth + avatar_y, avatar_width / 2, 0, Math.PI * 2, false);
            ctx.closePath();
            ctx.clip();
            ctx.setFillStyle('#F1F1F1');
            ctx.fill();
            ctx.drawImage(canvasAttr.handUrl, avatar_x, avatar_heigth / 2 + avatar_y, avatar_width, avatar_heigth);
            ctx.restore();
        },

        /**
         * 圆角背景
         * @param {CanvasContext} ctx canvas上下文
         * @param {number} x 圆角矩形选区的左上角 x坐标
         * @param {number} y 圆角矩形选区的左上角 y坐标
         * @param {number} w 圆角矩形选区的宽度
         * @param {number} h 圆角矩形选区的高度
         * @param {number} r 圆角的半径
         * @param {String} fillColor 填充颜色
         * @author: wudidi
         */
        roundRect(ctx, x, y, w, h, r, fillColor = '#ffffff', url, isBg) {
    
    
            ctx.save();
            // 开始绘制
            ctx.beginPath();
            // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
            ctx.setFillStyle('transparent');

            // 绘制左上角圆弧
            ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);

            // 绘制border-top
            ctx.moveTo(x + r, y);
            ctx.lineTo(x + w - r, y);
            ctx.lineTo(x + w, y + r);
            // 绘制右上角圆弧
            ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);

            // 绘制border-right
            ctx.lineTo(x + w, y + h - r);
            ctx.lineTo(x + w - r, y + h);
            // 绘制右下角圆弧
            ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);

            // 绘制border-bottom
            ctx.lineTo(x + r, y + h);
            ctx.lineTo(x, y + h - r);
            // 绘制左下角圆弧
            ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);

            // 绘制border-left
            ctx.lineTo(x, y + r);
            ctx.lineTo(x + r, y);

            ctx.fill();
            ctx.closePath();
            // 剪切
            ctx.clip();
            ctx.setFillStyle(fillColor);
            ctx.fillRect(x, y, w, h);
            if (isBg) {
    
    
                ctx.drawImage(url, 0,0, this.canvasWith, this.canvasHeight);
            }
            ctx.restore();
        },

        // 保存图片到相册
        async saveCanvasImage() {
    
    
            uni.showLoading({
    
    
                title: '海报保存中'
            });
            
            await this.createCanvasLink();
            console.log(this.canvasBgLink, '图片链接');
            if (this.canvasBgLink) {
    
    
                uni.getSetting({
    
    
                    success: res => {
    
    
                        if (res.authSetting['scope.writePhotosAlbum']) {
    
    
                            this.savaPhotos();
                        } else {
    
    
                            uni.authorize({
    
    
                                scope: 'scope.writePhotosAlbum',
                                success: res => {
    
    
                                    this.savaPhotos();
                                },
                                fail: err => {
    
    
                                    this.isPhoto = false;
                                }
                            });
                        }
                    }
                }); 
            } else {
    
    
                uni.hideLoading();
                uni.showToast({
    
    
                    title: '海报保存失败',
                    duration: 2000,
                    icon: 'none'
                });
            }
            
        },
        // 把画布转化成临时文件
        createCanvasLink() {
    
    
            return new Promise((resolve, reject) => {
    
    
                uni.canvasToTempFilePath({
    
    
                    x: 0,
                    y: 0,
                    width: this.canvasWith, // 画布的宽
                    height: this.canvasHeight, // 画布的高
                    canvasId: 'myCanvas',
                    success: res => {
    
    
                        this.canvasBgLink = res.tempFilePath;
                        resolve({
    
     res });
                    },
                    fail: () => {
    
    
                        // uni.hideLoading();
                        // uni.showToast({
    
    
                        //     title: '海报生成失败 稍后再试',
                        //     duration: 2000,
                        //     icon: 'none'
                        // });
                    }
                },this);
            });
        },
        //保存图片至相册
        savaPhotos() {
    
    
            uni.saveImageToPhotosAlbum({
    
    
                filePath: this.canvasBgLink,
                success: res => {
    
    
                    this.isPhoto = true;
                    uni.hideLoading();
                    this.handleCanvasCancel();
                },
                fail: err => {
    
    
                    this.handleCanvasCancel();
                    uni.hideLoading();
                    uni.showToast({
    
    
                        title: '海报保存失败',
                        duration: 2000,
                        icon: 'none'
                    });
                }
            });
        },
        // 取消海报
        handleCanvasCancel() {
    
    
            this.$emit('canvasCancel', false);
        }
    }
};
</script>

<style lang="scss">
.content {
    
    
    text-align: center;
    height: 100%;
}
.canvas-content {
    
    
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .del-icon {
    
    
        display: flex;
        width: 100%;
		height: 50rpx;
		justify-content: center;
		margin-top: 20rpx;
        > image {
    
    
            width: 50rpx;
            height: 50rpx;
            margin-right: 16rpx;
            margin-bottom: 10rpx;
            border-radius: 50%;
        }
    }
    .button-wrapper {
    
    
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 72rpx;
        margin-top: 15px;
        z-index: 16;
    }

    .save-btn {
    
    
        width: 40%;
        height: 100%;
        font-size: 30rpx;
        line-height: 72rpx;
        color: #fff;
        background: #ffbe1a;
        text-align: center;
        border-radius: 45rpx;
        border-radius: 36rpx;
        z-index: 10;
    }
    .cancel-btn {
    
    
        background: #fff;
    }
    .canvas-close-btn {
    
    
        position: fixed;
        width: 60rpx;
        height: 60rpx;
        padding: 20rpx;
        top: 30rpx;
        right: 0;
        z-index: 12;
    }
    .wisdom-dialog {
    
    
        .wisdom-dialog-text {
    
    
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 600rpx;
            height: 400rpx;
            padding: 10px 15px;
            border-radius: 10px;
            background-color: #ffffff;
            border: 1px solid #f1f1f1;
            > text {
    
    
                text-align: center;
                font-size: 14px;
                margin-bottom: 30px;
            }
            .wisdom-text-title {
    
    
                margin-top: 15px;
                font-size: 16px;
                color: #000000;
                font-weight: 700;
            }
            > button {
    
    
                width: 80%;
                font-size: 15px;
                color: #000000;
                font-weight: 600;
                border-radius: 30px;
                background-color: #ffbe1a;
            }
        }
    }
}
</style>

使用

图片地址记得替换一下

<template>
    <view class="mine">
		<view @click="isSavePic">显示</view>
        <view class="canvas-box" v-if="isCanvas">
            <hchPoster ref="hchPoster" @canvasCancel="canvasCancel" :canvasAttr.sync="posterData" />         
        </view>   
    </view>

</template>

<script>
import hchPoster from '../hch-poster/hch-poster.vue';
export default {
    
    
    components: {
    
    
        hchPoster
    },
    data() {
    
    
        return {
    
    
            isCanvas: false, // 显示canvas
			posterData: {
    
    
			    nickName: '兔子',
			    handUrl: '../../static/scriptures/tim2.jpg',
			    bgImg: '../../static/scriptures/timg.jpg',
			    codeImg: '../../static/scriptures/qr_code.png',
			},
        };
    },

	onShow(){
    
    
		this.isSavePic();
		this.getShare()
	},
    methods: {
    
    
        // 检测是否有授权保存图片功能,以显示不同按钮
        isSavePic(){
    
    
			this.getShare()
			this.isCanvas =true;
			this.$nextTick(() => {
    
    
				this.$refs.hchPoster.posterShow();
			});
        },
       
        // 关闭海报生成弹窗
        canvasCancel(val) {
    
    
            this.isCanvas = val;
        },
		getShare(){
    
    
			let par = {
    
    
			        method: 'POST',
			        url: '接口url',
					status: true,
			        show: false
			    };
			this.$request(par)
				.then(res => {
    
    
					if (res.data.code === '200') {
    
    
					   this.posterData=res.data.content;
					}
				})
				.catch(err => {
    
    
					console.log(err, '获取失败');
				});
		},
    }
};
</script>

<style lang="scss">
	.canvas-box {
    
    
	    position: fixed;
	    left: 0;
	    right: 0;
	    top: 0;
	    bottom: 0;
	    width: 100%;
	    display: flex;
	    justify-content: flex-start;
	    align-items: center;
	    background-color: rgba(0, 0, 0, 0.7);
	    z-index: 99999;
	}
</style>

使用时以下新建为2个文件,放在同一目录下,运行看看

猜你喜欢

转载自blog.csdn.net/qq_21113235/article/details/109649013
今日推荐