首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >uniapp H5 画布自定义海报实现长按识别,分享,转发

uniapp H5 画布自定义海报实现长按识别,分享,转发

原创
作者头像
IT工作者
发布于 2022-05-16 10:36:38
发布于 2022-05-16 10:36:38
4.1K00
代码可运行
举报
文章被收录于专栏:程序技术知识程序技术知识
运行总次数:0
代码可运行

前端使用uniapp制作H5自定义海报,本来以为挺简单的一常用功能画布,因为之前也在 H5 和小程序做过类似功能,所以直接上手干了,没想到还是遇到了一些坑,特此记录希望能够帮到大家。

实现功能讲解:

海报实现的效果图,上面的微信头像,昵称和下面的文案,二维码都是动态的,也就是说是需要后端生成的,包括背景图片,也需要随机生成,然后用户长按界面能够保存,识别二维码,分享功能。

实现:

创建一个画布,给画布设置 style 。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<canvas style="" canvas-id="myCanvas" id="myCanvas"></canvas>
 
    canvas {
        background-color: #fff;
        border: 1px solid #d0d0d0;
        width: 100vw;
        height: 100vh;
        position: absolute;
        left: 100%;
    }

创建画布实例并开始绘画。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var context = uni.createCanvasContext('myCanvas');

所有数据信息都由后端传过来,下面关于图片素材有两种情况,

1.如果图片允许跨域,可以通过 uni.getImageInfo 方法获取图片路径放到画布里面;

2.如果图片不允许跨域,需要先转成base64格式,然后通过 context.drawImage() 放到画布里面

以上图片两个报错,都是图片不支持跨域导致的。开发者工具可能可以正确画布出来并导出为图片,但是手机画布导出的时候就会报错,这个时候就需要转成base64 了,先尝试用代码转,如果不能转,就在站点 在线转换,然后把base64保存到文件里面。

有人可能想到把图片放到代码包里面做成本地图片,就绕过跨域问题了,但是这个方法我尝试的时候发现小程序可以,H5不可以,很遗憾。如果有H5本地图片画布成功的也可以跟我分享一下经验,谢谢。

 

H5实现长按识别图片功能:

用户长按图片自然就会有系统默认的弹窗可以识别或者保存分享了,不需要代码实现。

下面看看完整的实现的代码吧:(干净整洁,有注释,喜欢的点个赞!)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
    <!--pages/poster/poster.wxml-->
    <view class="page">
        <canvas style="" canvas-id="myCanvas" id="myCanvas"></canvas>
        <view class="container">
            <image :src="tempFilePath" mode="widthFix"></image>
        </view>
    </view>
</template>
 
<script>
    import {
        IMAGES_PATH
    } from '@/common/const.js';
    import {
        getPoste
    } from '@/common/api.js';
    import {
        pathToBase64,
        base64ToPath
    } from '../../js_sdk/gsq-image-tools/image-tools/index.js'
    import base64_img from "./base64_img.js";
 
    export default {
        data() {
            return {
                tempFilePath: ''
            };
        },
        onLoad: function(options) {
            var that = this;
            var context = uni.createCanvasContext('myCanvas');
            
            getPoster(options.id)
                .then(res => {
                    var {
                        img,
                        avatar,
                        nickname,
                        avatar_content,
                        active_describe
                    } = res.data.data || {};
                    var right_bottom_txt_base64 =
                        "data:image/png;base64"
                    pathToBase64(avatar)
                        .then(base64_avatar => {
                            // 背景底图
                            context.drawImage(base64_img[img], 0, 0, 200, 387, 400, 774)
 
                            // 用户昵称
                            context.setFillStyle('#233582');
                            context.fillText(nickname, 90, 132);
 
                            // 文字上面横线 1
                            context.setFillStyle('#233582')
                            context.fillRect(16, 146, 166, 1.2)
 
                            // 文字 判断长度换行处理
                            context.setFontSize(9)
                            if (active_describe.length > 18) {
                                let tt1 = active_describe.substr(0, 17);
                                let tt2 = active_describe.substr(17, active_describe.length - 1);
                                context.fillText(tt1, 16, 166);
                                context.fillText(tt2, 16, 181);
                            } else {
                                context.fillText(active_describe, 20, 166, 320);
                            }
 
                            // 文字下面横线 2
                            context.fillRect(16, 196, 166, 1.2)
 
                            // 右下角的文字用图片,因为在背景图的话会失真模糊
                            context.drawImage(right_bottom_txt_base64, 100, 230, 90, 30)
 
                            // 规定一个圆形的位置,里面放头像图片
                            let avatarurl_width = 40
                            let avatarurl_heigth = 40
                            let avatarurl_x = 80
                            let avatarurl_y = 80
                            context.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y,
                                avatarurl_width / 2,
                                0, Math.PI * 2, false)
                            context.clip()
                            context.drawImage(base64_avatar, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth)
                            context.restore()
 
                            // 保存画布,生成图片指定大小的图片,并返回图片路径
                            // 有个坑 : H5端 Canvas 内绘制的图像需要支持跨域访问才能成功。(本代码的解决方案:把图片转成base64)
                            context.draw(true, () => {
                                uni.canvasToTempFilePath({
                                    x: 0,
                                    y: 0,
                                    width: 200,
                                    height: 387,
                                    destWidth: getApp().windowWidth,
                                    destHeight: getApp().windowHeight,
                                    canvasId: 'myCanvas',
                                    fileType: "jpg",
                                    quality: 1,
                                    success(res) {
                                        console.log('绘制成功-------', res)
                                        uni.hideLoading();
                                        that.tempFilePath = res.tempFilePath;
                                    },
                                    fail(err) {
                                        console.log('绘制失败', err)
                                    }
                                });
                            });
 
                        })
                })
 
        },
        methods: {
            // 小程序的场景使用,小程序支持本地图片
            getImageInfo(url) {
                return new Promise(function(resolve, reject) {
                    uni.getImageInfo({
                        src: url,
                        success: function(res) {
                            console.log(res);
                            resolve(res);
                        },
                        fail: function(err) {
                            console.log(err);
                            uni.showToast({
                                title: '生成失败',
                                icon: 'none'
                            });
                        }
                    });
                });
            },
            // 小程序的场景使用,长按保存图片
            saveImg(e) {
                let url = this.tempFilePath;
                uni.saveFile({
                    tempFilePath: url,
                    success: function(res) {
                        var savedFilePath = res.savedFilePath;
                        uni.showToast({
                            title: "图片保存成功",
                            icon: "none"
                        });
                    }
                });
 
 
            },
            // 小程序场景使用,获取图片信息,保存到相册
            downImage() {
                const url = this.images1;
                uni.getImageInfo({
                    src: url,
                    success: res => {
                        let path = res.path;
                        uni.saveImageToPhotosAlbum({
                            filePath: path,
                            success: res => {
                                uni.showToast({
                                    title: '保存成功',
                                    icon: 'none'
                                });
                            },
                            fail: res => {
                                console.log(res);
                            }
                        });
                    },
                    fail: res => {
                        console.log(res);
                    }
                });
            }
        }
    };
</script>
 
<style>
    canvas {
        background-color: #fff;
        border: 1px solid #d0d0d0;
        width: 100vw;
        height: 100vh;
        position: absolute;
        left: 100%;
    }
 
    .container {
        width: 100vw;
        align-items: center;
        overflow: auto;
        background: #fefefe;
    }
 
    .container image {
        width: 100%;
        position: absolute;
        top: 0;
    }
</style>

app.vue 设置全局常数,屏幕的宽度和高度

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    onLaunch: function(info) {
        var that=this;
        uni.getSystemInfo({
            success: function (res) {
                that.windowWidth =res.windowWidth;
                that.windowHeight =res.windowHeight;
                
                console.log('----this---www',this.windowWidth);
            }
        });
        //网络状态监听
        // network();
        console.log('App Launch');
    },

./base64_img.js 代码,里面放 base64 图片, 是背景图的数组,后端随机生成1~5,拿下标画布就行。

'../../js_sdk/gsq-image-tools/image-tools/index.js' 代码,图片转base64 函数封装,无需修改。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function getLocalFilePath(path) {
    if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
        return path
    }
    if (path.indexOf('file://') === 0) {
        return path
    }
    if (path.indexOf('/storage/emulated/0/') === 0) {
        return path
    }
    if (path.indexOf('/') === 0) {
        var localFilePath = plus.io.convertAbsoluteFileSystem(path)
        if (localFilePath !== path) {
            return localFilePath
        } else {
            path = path.substr(1)
        }
    }
    return '_www/' + path
}
 
export function pathToBase64(path) {
    return new Promise(function(resolve, reject) {
        if (typeof window === 'object' && 'document' in window) {
            if (typeof FileReader === 'function') {
                var xhr = new XMLHttpRequest()
                xhr.open('GET', path, true)
                xhr.responseType = 'blob'
                xhr.onload = function() {
                    if (this.status === 200) {
                        let fileReader = new FileReader()
                        fileReader.onload = function(e) {
                            resolve(e.target.result)
                        }
                        fileReader.onerror = reject
                        fileReader.readAsDataURL(this.response)
                    }
                }
                xhr.onerror = reject
                xhr.send()
                return
            }
            var canvas = document.createElement('canvas')
            var c2x = canvas.getContext('2d')
            var img = new Image
            img.onload = function() {
                canvas.width = img.width
                canvas.height = img.height
                c2x.drawImage(img, 0, 0)
                resolve(canvas.toDataURL())
                canvas.height = canvas.width = 0
            }
            img.onerror = reject
            img.src = path
            return
        }
        if (typeof plus === 'object') {
            plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
                entry.file(function(file) {
                    var fileReader = new plus.io.FileReader()
                    fileReader.onload = function(data) {
                        resolve(data.target.result)
                    }
                    fileReader.onerror = function(error) {
                        reject(error)
                    }
                    fileReader.readAsDataURL(file)
                }, function(error) {
                    reject(error)
                })
            }, function(error) {
                reject(error)
            })
            return
        }
        if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
            wx.getFileSystemManager().readFile({
                filePath: path,
                encoding: 'base64',
                success: function(res) {
                    resolve('data:image/png;base64,' + res.data)
                },
                fail: function(error) {
                    reject(error)
                }
            })
            return
        }
        reject(new Error('not support'))
    })
}
 
export function base64ToPath(base64) {
    return new Promise(function(resolve, reject) {
        if (typeof window === 'object' && 'document' in window) {
            base64 = base64.split(',')
            var type = base64[0].match(/:(.*?);/)[1]
            var str = atob(base64[1])
            var n = str.length
            var array = new Uint8Array(n)
            while (n--) {
                array[n] = str.charCodeAt(n)
            }
            return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
        }
        var extName = base64.match(/data\:\S+\/(\S+);/)
        if (extName) {
            extName = extName[1]
        } else {
            reject(new Error('base64 error'))
        }
        var fileName = Date.now() + '.' + extName
        if (typeof plus === 'object') {
            var bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
            bitmap.loadBase64Data(base64, function() {
                var filePath = '_doc/uniapp_temp/' + fileName
                bitmap.save(filePath, {}, function() {
                    bitmap.clear()
                    resolve(filePath)
                }, function(error) {
                    bitmap.clear()
                    reject(error)
                })
            }, function(error) {
                bitmap.clear()
                reject(error)
            })
            return
        }
        if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
            var filePath = wx.env.USER_DATA_PATH + '/' + fileName
            wx.getFileSystemManager().writeFile({
                filePath: filePath,
                data: base64.replace(/^data:\S+\/\S+;base64,/, ''),
                encoding: 'base64',
                success: function() {
                    resolve(filePath)
                },
                fail: function(error) {
                    reject(error)
                }
            })
            return
        }
        reject(new Error('not support'))
    })
}

完成!!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档