Uniapp绘制海报终极解决方案(微信小程序同样适用)

Uniapp绘制海报终极解决方案(微信小程序同样适用)

问题原因与分析

背景介绍

在微信小程序开发过程中,我们发现分享只能发送给好友,并没有“分享到朋友圈的功能”

以下是任意小程序点开分享界面的截图

在这里插入图片描述

所以我们通常的做法是绘制一个带小程序二维码的海报,然后保存到本地图库

最后引导用户把该张图片分享至朋友圈,朋友看到后扫码即可进入小程序

以下是腾讯新闻小程序解决方案——绘制海报

在这里插入图片描述

在这里插入图片描述

今天,我们就要实现将指定图片(网络路径)绘制成为海报并保存到用户本地的功能

坐稳,我们现在出发:

我们首先分析下程序中存在的难点痛点

  1. 网络图片需要做本地存储后才能开始绘画
  2. 【重点】网络存在不稳定性,如何保证绘画顺序和期望一致(因为uniapp对大部分接口都做了Promise封装)
  3. 绘制好的图片如何保存至用户本地
  4. 如何处理设备适配问题

针对以上几点,画出设计思路图

设计思路图示

在这里插入图片描述

解决方案

万事俱备,撸起袖子开始编码!(以下为编码讲解,文末有完整代码)

  1. 首先编写模板代码

    <template>
    	<view class="content">
    		<canvas canvas-id="myCanvas"></canvas>
    		<button @click="generateImg">点我</button>
    	</view>
    </template>
    

    我们首先定义了一个id为myCanvas的画布,用于绘制我们的海报

  2. 设计绘图队列以及数据结构

    data() {
        return {
            drawQueue: //画图队列
            [
                {
                    path: 'https://zftsw-oss.oss-cn-hangzhou.aliyuncs.com/5c354e4a710461580958674035.jpg',
                    x: 0,
                    y: 0,
                    width: 600,
                    height: 300,
                    type: 'image',
                },
                {
                    path: 'https://zftsw-oss.oss-cn-hangzhou.aliyuncs.com/tmp_cb726c3d5765754dc08ba01c6fa8fa522dff4b08c3aad6d4.jpg',
                    x: 520,
                    y: 20,
                    width: 60,
                    height: 60,
                    type: 'image',
                },
                {
                    text: '我是文章标题',
                    size: 32,
                    x: 50,
                    y: 365,
                    type: 'text',
                },
                {
                    text: '扫码关注我哟',
                    size: 24,
                    x: 300,
                    y: 365,
                    type: 'text',
                },
    
            ],
            ctx: null, //画布上下文
            counter: -1, //计数器
            drawPathQueue: [], //画图路径队列
        }
    },computed: {
            myPx() {
                return this.screenWidth / 750
            },
        }
    

    这里简单说一下:drawQueue为待绘制队列,其中path是图片网络路径,xy为坐标,type为区分是绘制图片还是文字,drawPathQueue为处理害的绘图队列,其中每个元素会以index标注绘制顺序

  3. 在App.vue应用加载时获取当前屏幕宽度(也可以使用uniapp内置api:upx2px)

    <script>
    	import Vue from 'vue'
    	export default {
    		onLaunch: function() {
    			console.log('App Launch')
    			uni.getSystemInfo({
    				success: (res)=> {
    					Vue.prototype.screenWidth = res.screenWidth
    				}
    			})
    		},
    		onShow: function() {
    			console.log('App Show')
    		},
    		onHide: function() {
    			console.log('App Hide')
    		}
    	}
    </script>
    
    <style>
    	/*每个页面公共css */
    </style>
    
  4. 在页面初始化时加载canvas实例

    onLoad() {
        this.ctx = uni.createCanvasContext('myCanvas', this)
    }
    
  5. 写两个监听器,分别监听处理后的队列生成进度和绘制进度

    watch: {
        drawPathQueue(newVal, oldVal) {
            /* 所有元素入队则开始绘制 */
            if (newVal.length === this.drawQueue.length) {
                console.log('生成的队列:'+JSON.stringify(newVal));
                console.log('开始绘制...')
                for (let i = 0; i < this.drawPathQueue.length; i++) {
                    for (let j = 0; j < this.drawPathQueue.length; j++) {
                        let current = this.drawPathQueue[j]
                        /* 按顺序绘制 */
                        if (current.index === i) {
                            /* 文本绘制 */
                            if (current.type === 'text') {
                                console.log('绘制文本:' + current.text);
                                this.ctx.setFillStyle('#000')
                                this.ctx.setFontSize(current.size * this.myPx)
                                this.ctx.fillText(current.text, current.x * this.myPx, current.y * this.myPx)
                                this.counter--
                            }
                            /* 图片绘制 */
                            if (current.type === 'image') {
                                console.log('绘制图片:' + current.path);
                                this.ctx.drawImage(current.path, current.x * this.myPx, current.y * this.myPx, current.width * this.myPx,
                                                   current.height * this.myPx)
                                this.counter--
                            }
                        }
                    }
                }
                console.log('final counter', this.counter);
            }
        },
            counter(newVal, oldVal) {
                if (newVal === 0) {
                    this.ctx.draw()
                    /* draw完不能立刻转存,需要等待一段时间 */
                    setTimeout(() => {
                        uni.canvasToTempFilePath({
                            canvasId: 'myCanvas',
                            success: (res) => {
                                console.log('in canvasToTempFilePath');
                                // 在H5平台下,tempFilePath 为 base64
                                console.log('图片已保存至本地:', res.tempFilePath)
                                uni.saveFile({
                                    tempFilePath: res.tempFilePath,
                                    success: function(res) {
                                        console.log('本地保存路径为',res.savedFilePath);
                                    }
                                });
                            }
                        })
                    }, 1000)
                }
            }
    },
    
  6. 最后添加生成点击事件

    generateImg() {
        this.counter = this.drawQueue.length
        this.drawPathQueue = []
        /* 将图片路径取出放入绘图队列 */
        for (let i = 0; i < this.drawQueue.length; i++) {
            let current = this.drawQueue[i]
            current.index = i
            /* 如果是文本直接放入队列 */
            if (current.type === 'text') {
                this.drawPathQueue.push(current)
                continue
            }
            /* 图片需获取本地缓存path放入队列 */
            uni.getImageInfo({
                src: current.path,
                success: (res) => {
                    current.path = res.path
                    this.drawPathQueue.push(current)
                }
            })
        }
        /* 
    				setTimeout(()=>{
    					console.log(JSON.stringify(this.drawPathQueue));
    				},10000) */
    }
    
  7. 调整样式并测试

    <style lang="scss">
    	page {
    		.content {
    			canvas {
    				width: 600rpx;
    				height: 450rpx;
    				border: 1px solid black;
    				margin: 0 auto;
    			}
    		}
    	}
    </style>
    
  8. 效果图

    在这里插入图片描述

在这里插入图片描述

因为只是demo,图片没有认真找,随意放了几张图片,只要将队列中的图片换成自己想要的图片和小程序二维码即可

完整代码

以下是本示例index.vue下的完整代码

<template>
<view class="content">
    <canvas canvas-id="myCanvas"></canvas>
    <button @click="generateImg">点我</button>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                drawQueue: //画图队列
                [
                    {
                        path: 'https://zftsw-oss.oss-cn-hangzhou.aliyuncs.com/5c354e4a710461580958674035.jpg',
                        x: 0,
                        y: 0,
                        width: 600,
                        height: 300,
                        type: 'image',
                    },
                    {
                        path: 'https://zftsw-oss.oss-cn-hangzhou.aliyuncs.com/tmp_cb726c3d5765754dc08ba01c6fa8fa522dff4b08c3aad6d4.jpg',
                        x: 520,
                        y: 20,
                        width: 60,
                        height: 60,
                        type: 'image',
                    },
                    {
                        text: '我是文章标题',
                        size: 32,
                        x: 50,
                        y: 365,
                        type: 'text',
                    },
                    {
                        text: '扫码关注我哟',
                        size: 24,
                        x: 300,
                        y: 365,
                        type: 'text',
                    },

                ],
                ctx: null, //画布上下文
                counter: -1, //计数器
                drawPathQueue: [], //画图路径队列
            }
        },
        computed: {
            myPx() {
                return this.screenWidth / 750
            },
        },
        onLoad() {
            this.ctx = uni.createCanvasContext('myCanvas', this)
        },
        watch: {
            drawPathQueue(newVal, oldVal) {
                /* 所有元素入队则开始绘制 */
                if (newVal.length === this.drawQueue.length) {
                    console.log('生成的队列:'+JSON.stringify(newVal));
                    console.log('开始绘制...')
                    for (let i = 0; i < this.drawPathQueue.length; i++) {
                        for (let j = 0; j < this.drawPathQueue.length; j++) {
                            let current = this.drawPathQueue[j]
                            /* 按顺序绘制 */
                            if (current.index === i) {
                                /* 文本绘制 */
                                if (current.type === 'text') {
                                    console.log('绘制文本:' + current.text);
                                    this.ctx.setFillStyle('#000')
                                    this.ctx.setFontSize(current.size * this.myPx)
                                    this.ctx.fillText(current.text, current.x * this.myPx, current.y * this.myPx)
                                    this.counter--
                                }
                                /* 图片绘制 */
                                if (current.type === 'image') {
                                    console.log('绘制图片:' + current.path);
                                    this.ctx.drawImage(current.path, current.x * this.myPx, current.y * this.myPx, current.width * this.myPx,
                                                       current.height * this.myPx)
                                    this.counter--
                                }
                            }
                        }
                    }
                    console.log('final counter', this.counter);
                }
            },
            counter(newVal, oldVal) {
                if (newVal === 0) {
                    this.ctx.draw()
                    /* draw完不能立刻转存,需要等待一段时间 */
                    setTimeout(() => {
                        uni.canvasToTempFilePath({
                            canvasId: 'myCanvas',
                            success: (res) => {
                                console.log('in canvasToTempFilePath');
                                // 在H5平台下,tempFilePath 为 base64
                                console.log('图片已保存至本地:', res.tempFilePath)
                                uni.saveFile({
                                    tempFilePath: res.tempFilePath,
                                    success: function(res) {
                                        console.log('本地保存路径为',res.savedFilePath);
                                    }
                                });
                            }
                        })
                    }, 1000)
                }
            }
        },
        methods: {
            generateImg() {
                this.counter = this.drawQueue.length
                this.drawPathQueue = []
                /* 将图片路径取出放入绘图队列 */
                for (let i = 0; i < this.drawQueue.length; i++) {
                    let current = this.drawQueue[i]
                    current.index = i
                    /* 如果是文本直接放入队列 */
                    if (current.type === 'text') {
                        this.drawPathQueue.push(current)
                        continue
                    }
                    /* 图片需获取本地缓存path放入队列 */
                    uni.getImageInfo({
                        src: current.path,
                        success: (res) => {
                            current.path = res.path
                            this.drawPathQueue.push(current)
                        }
                    })
                }
                /* 
				setTimeout(()=>{
					console.log(JSON.stringify(this.drawPathQueue));
				},10000) */
            }
        }
    }
</script>

<style lang="scss">
    page {
        .content {
            canvas {
                width: 600rpx;
                height: 450rpx;
                border: 1px solid black;
                margin: 0 auto;
            }
        }
    }
</style>

发布了52 篇原创文章 · 获赞 150 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/itkfdektxa/article/details/104841015