小程序开发关于Canvas的爱恨情仇

前言

在帮客户开发一个关于生成证书的功能!需要把图片合成,然后在指定的位置添加上文字,最后把图片合成让用户保存到相册里面!

这就是效果图

其中,爱呵呵,小程序二维码,比赛名称,项目,都是会动态替换的文字!

碰到的所有问题

总体来说就是开发的时候容易钻牛角尖

  • 问题1:小程序使用canvas怎么隐藏
  • 问题2:保存到用户手机相册需要更新用户隐私协议
  • 问题3: 配置下载域名,
  • 问题4:下载太慢了

解决的办法

其实解决办法就没用canvas!

哈哈哈,并不是没用使用,是中间有一个步骤没想明白!客户着急要,最后使用了sharp在服务端去合成的然后返回链接下载,保存

因为一开始,我希望原图下载,我就使用了2481px * 3580px 这个宽高比例 然后去生成 出来的图片1.5mb差不多,感觉没问题,但不知道为什么上传到服务器,图片突然变成了10mb 特别是不同手机好像出来的还不一样。我的代码是

javascript 复制代码
download() {
	if (this.isDownloading) {
		return
		console.log('下载中')
	}
	this.isDownloading = true
	uni.getSetting({
            success: res => {
                    if (!res.authSetting['scope.writePhotosAlbum']) {
                            uni.showModal({
                                    title: '提示',
                                    content: '需要获取图片权限哦',
                                    success: r => {
                                            console.log('同意')
                                            this.downloadImages()
                                            uni.openSetting({
                                                    success: res => {
                                                            console.log(res)
                                                    },
                                                    fail: er => {
                                                            console.log(er)
                                                    }
                                            })
                                    }
                            })
                    } else {
                            console.log('授权了的')
                            this.downloadImages()
                    }

            }
        })
}

下载之前 先去 判断是否授权了,没有就去授权, 然后就去执行 downloadImages 下载图片

kotlin 复制代码
async downloadImages() {
    try {
            uni.showLoading({
                    title: '正在颁发中...',
            })
            const [imageRes, logoRes] = await Promise.all([
                    this.downloadFilePromise(this.bgUrl),
                    this.downloadFilePromise(this.logoUrl)
            ])

            if (imageRes.statusCode === 200 && logoRes.statusCode === 200) {
                    this.localImagePath = imageRes.tempFilePath
                    this.localImageLogo = logoRes.tempFilePath
                    // 绘制图片到 canvas 并保存
                    console.log('图片', this.localImageLogo,this.localImagePath)
                    this.drawAndSaveImage()
            } else {
                    console.error('下载图片失败')
            }
    } catch (error) {
            console.error('下载图片失败', error)
            this.isDownloading = false
    }
},
javascript 复制代码
downloadFilePromise(url) {
        return new Promise((resolve, reject) => {
                uni.downloadFile({
                        url,
                        success: resolve,
                        fail: reject
                })
        })
},

这里就是去下载图片,然后去开始使用canvas绘画,

到了绘画哪里 就是使用canvas 获取,在小程序里面 使用:

kotlin 复制代码
const ctx = uni.createCanvasContext('myCanvas', this)

在template 需要去所明一个:

css 复制代码
<canvas canvas-id="myCanvas" style="position: fixed; left: 100%;"></canvas>

这里就是canvas在绘画的时候,会在页面出现,特别是我这个宽高特别丑,关于隐藏!首先排除v-if 然后使用display:none 这个也是不可以的,v-show 同样不可以, visibility: hidden; 我也使用了,测试的时候可以,但一到真机上,我发现还是会出现!

最后可以的办法就是使用:position: fixed; left: 100%; 只能说 CSS YYDS

kotlin 复制代码
drawAndSaveImage() {
  // 设置画布宽高
       const canvasWidth = 827
       // const canvasWidth = 2481
       const canvasHeight = 1169
       // const canvasHeight = 3580

       const ctx = uni.createCanvasContext('myCanvas', this)

       // 填背景图
       ctx.drawImage(this.localImagePath, 0, 0, canvasWidth, canvasHeight)
       
       // 添加姓名
       ctx.setFontSize(50)
       ctx.setFillStyle('#555500')
       ctx.setTextAlign('center')
       ctx.fillText(this.name, 420, 680)

       // 添加内容
       ctx.setFontSize(24)
       ctx.setFillStyle('#111')
       ctx.setTextAlign('center')
       ctx.fillText(this.title, 460, 765)
       ctx.setTextAlign('center')
       ctx.fillText(this.subtitle, 300, 805)
       
       // 添加小程序logo
       ctx.drawImage(this.localImageLogo, 330, 900, 150, 150)
       
       uni.hideLoading()
       ctx.draw(false, () => {
         uni.showLoading({
            title: '正在加载图片'
         })
         
         // 开始绘制
         uni.canvasToTempFilePath({
           canvasId: 'myCanvas',
           quality: 0.7,
           success: res => {
            
           },
           fail: err => {}
         })
       }
  }

这里我一开始就是把这里的图片直接使用uni.saveImageToPhotosAlbum 保存到了 用户手机相册,但是我报了下载域不在配置名单的错误,因此,我就只能上传到我的服务器 也是这里上传的时候,我莫名其妙发现res.tempFilePath 上传到服务器 我手机上传是1.49MB, 其他人的手机每一个上传的大小都不一样,还有的到10MB, 导致的结果就是 太慢了 几十秒 根本不可能等待! 于是我的第一个解决办法就是把 canvas的宽高但除 3 然后一样的步骤去操作

我发现还是不可以!一样的问题!太大了,我百思不得其解,为什么图片体积增大了

我去查了文档 看了下 uni.canvasToTempFilePath 这个方法的介绍

destWidth Number 输出图片宽度(默认为 width * 屏幕像素密度)
destHeight Number 输出图片高度(默认为 height * 屏幕像素密度)

感觉这里是图片可能变化的问题,但我不确定,这个东西着急要,我不能拖着!

于是我放弃了之前的做法,我直接在服务端生成图片,让小程序端下载,然后保存就可以了,前端只需要提供我需要的三个参数就可以了!

于是在服务端使用sharp 完成了代码!然后返回前端一个链接,

javascript 复制代码
// 这就是服务端处理好的图片url: fillUrl
uni.downloadFile({
url: fillUrl,
success: downloadRes => {
        if (downloadRes.statusCode === 200) {
                console.log('图片下载成功')
                // 将图片保存到相册
                uni.hideLoading()
                uni.saveImageToPhotosAlbum({
                        filePath: downloadRes.tempFilePath,
                        success: saveRes => {
                                uni.showToast({
                                        title: '图片保存成功',
                                        icon: 'none'
                                })
                                this.isDownloading = false
                        },
                        fail: saveErr => {
                                uni.showToast({
                                        title: `保存失败${saveErr}`,
                                        icon: 'none'
                                })
                                this.isDownloading = false
                        }
                })
        } else {
                this.isDownloading = false
                console.error('图片下载失败', downloadRes)
        }
},
fail: err => {
        console.error(err)
        uni.hideLoading()
        uni.showToast({
                title: '下载失败',
                icon: 'none'
        })
        this.isDownloading = false
}
})

速度大概几百毫秒把!体积也才100kb 左右使用的webp 画质也不错!

下面是服务端的代码:

ini 复制代码
const sharp = require('sharp')
const path = require('path')
const fs = require('fs')

    async generateImage(ctx) {
       const { name, title, subtitle } = ctx.request.body

       const width = 827
       const height = 1169
       const x = width / 2
       // const y = 680
       const fontFamily = 'Microsoft YaHei'
       const fontSize = 40
       const color = 'black'
       const stroke = 'transparent'

       const nameImage = Buffer.from(`
       <svg width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">
           <text
           text-anchor="middle"
           y="680" x="${x}"
           font-family="${fontFamily}"
           font-size="${fontSize}px"
           fill="red"
           stroke="${stroke}"
           >${name}</text>
       </svg>`)

       const titleImage = Buffer.from(
           `<svg width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">
       <text
       text-anchor="middle"
       y="765" x="460"
       font-family="${fontFamily}"
       font-size="24px"
       stroke="${stroke}"
       fill="${color}"
       >${title}</text>
   </svg>`
       )

       const subImage = Buffer.from(
           `<svg  width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">
       <text
       text-anchor="middle"
       y="805" x="300"
       font-family="${fontFamily}"
       font-size="24px"
       stroke="${stroke}"
       fill="${color}"
       >${subtitle}</text>
   </svg>`
       )
       try {
           // 读取原始图像
           const image = await sharp('public/img/logo.webp')
               // 缩放至指定尺寸
               .resize(827, 1169)
               // 添加文字水印
               .composite([
                   { input: nameImage },
                   { input: titleImage },
                   { input: subImage },
                   {
                       input: 'public/img/logo.png',
                       top: 900,
                       left: 330,
                   },
               ])
               // 输出为 WebP 格式,质量设置为 90%
               .webp({ quality: 90 })
               // 将处理后的图像转换为 Buffer
               .toBuffer()

           // 保存图片到服务器指定路径
           const fileName = Date.now().toString() + 'zs.webp'
           const DIST_FOLDER_PATH = path.join(__dirname, '..', '..', 'uploadFiles')
           const distFilePath = path.join(DIST_FOLDER_PATH, fileName) //目的地
           fs.writeFileSync(distFilePath, image)
           
           // 发送处理后的图像给前端
           ctx.body = new SuccessModel({ data: `/${fileName}` })
       } catch (error) {
           // 如果处理过程中出现错误,返回错误信息给前端
           console.log(error)
           ctx.status = 500
           ctx.body = new ErrorModel({ status: 500, message: 'Internal Server Error' })
       }
   }

npm install sharp 就可以,记得确保你的环境是windows还是linux服务端还是mac 好像不太一样,文档有比较仔细的教程!

不清晰的地方

  • 关于图片尺寸为什么在上传的时候变大,我为此还以为是我的服务上传代码配置问题,后面排查了很多次,才发现是我的平台尺寸问题,上传就一直失败,百思不得其解 ~
  • 为什么visibility: hidden; 在电脑上测试可以,到真机上就不可以了会显示出来,除此之外我还试了用 view去包裹 canvas 然后 view 设置了width:0px;height:0px;overflow:hidden; 可是一样不可以没有效果,canvas在渲染的时候还是会出现!

反馈总结

记录这次使用uni-app开发小程序中,使用canvas的问题,虽然最后还是没有使用这个方法,哈哈哈。 sharp确实好用

相关推荐
终将老去的穷苦程序员1 小时前
使用 IntelliJ IDEA 创建简单的 Java Web 项目
java·前端·intellij-idea
JINGWHALE12 小时前
设计模式 行为型 模板方法模式(Template Method Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·模板方法模式
&活在当下&3 小时前
Vue3 给 reactive 响应式对象赋值
前端·vue.js
坐公交也用券4 小时前
VUE3配置后端地址,实现前后端分离及开发、正式环境分离
前端·javascript·vue.js
独孤求败Ace4 小时前
第31天:Web开发-PHP应用&TP框架&MVC模型&路由访问&模版渲染&安全写法&版本漏洞
前端·php·mvc
星星不闪包退换4 小时前
css面试常考布局(圣杯布局、双飞翼布局、三栏布局、两栏布局、三角形)
前端·css
椒盐大肥猫5 小时前
uniapp使用scss mixin抽离css常用的公共样式
css·uni-app·scss
疯狂的沙粒5 小时前
HTML和CSS相关的问题,如何避免 CSS 样式冲突?
前端·css·html
家电修理师5 小时前
HBuilderX打包ios保姆式教程
前端·ios
草木红5 小时前
六、Angular 发送请求/ HttpClient 模块
服务器·前端·javascript·angular.js