小程序怎么临时加载本地相册图片
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小程序怎么临时加载本地相册图片相关的知识,希望对你有一定的参考价值。
可以临时加载本地相片显示,不用上传到后台的。
小程序中获取图片可通过两种方式得到,第一种是直接打开微信内部自己的样式,第一格就是相机拍照,后面是图片,第二种是弹框提示用户是要拍照还是从相册选择,下面一一来看。
选择相册要用到wx.chooseImage(OBJECT)函数,具体参数如下:
直接来看打开相机相册的代码:
Page( data: tempFilePaths: '' , onLoad: function () , chooseimage: function () var that = this; wx.chooseImage( count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 that.setData( tempFilePaths: res.tempFilePaths ) ) , )
方法一效果图如下:
个人认为第二种用户体验要好一点,效果如下:
点击获取弹框提示,代码如下:
Page( data: tempFilePaths: '' , onLoad: function () , chooseimage: function () var that = this; wx.showActionSheet( itemList: ['从相册中选择', '拍照'], itemColor: "#CED63A", success: function (res) if (!res.cancel) if (res.tapIndex == 0) that.chooseWxImage('album') else if (res.tapIndex == 1) that.chooseWxImage('camera') ) , chooseWxImage: function (type) var that = this; wx.chooseImage( sizeType: ['original', 'compressed'], sourceType: [type], success: function (res) console.log(res); that.setData( tempFilePaths: res.tempFilePaths[0], ) ) )
文件的临时路径,在小程序本次启动期间可以正常使用,如需持久保存,需在主动调用 wx.saveFile,在小程序下次启动时才能访问得到。
布局文件:
<button style="margin:30rpx;" bindtap="chooseimage">获取图片</button> <image src="tempFilePaths " catchTap="chooseImageTap" mode="aspectFit" style="width: 100%; height: 450rpx" />
参考技术A 步骤一:微信开发工具 打开项目 步骤二:新建个文件夹(放项目的一级或者二级目录都可以),然后把图片拷贝到这个目录下。 步骤三:相对路径去访问图片,可以用style样式的方式或者image标签 而外:不能用wxml样式去引用本地的图片,不会报错,也没效果 注意:在手机模拟预览,样式的背景图只能用服务器的图片,不能用本地
钉钉小程序通过 Canvas 将页面生成图片并保存到本地相册
背景
最近公司有个账户充值业务场景需要从线下支付迁移到线上支付:
- 线下支付场景:客户通过 POS 机付款或者扫码销售同学提供的付款二维码进行付款来完成支付,之后销售同学将相关信息录入到 CRM 后台,财务审核通过后才正式完成充值流程。
- 线上支付场景:销售同学先在 CRM 钉钉小程序中录入充值信息后生成订单,然后系统生成支付宝或者微信付款码,销售同学将付款码页面生成的图片发送给客户,客户付款后即完成充值流程。
整个充值流程优化上线后,大大缩短了客户账户从充值付款到充值到账的时间,明显提高了给客户账户充值的效率。
需求分析
本次迭代功能的小程序是使用原生钉钉语言开发的小程序,至于为什么是原生语言开发,那是历史原因了,不在本文讨论范围,原生语言开发体验明显没有使用了 uni-app、taro 等小程序框架的开发体验好,刚接手时还需要一边查文档一边开发,效率比较低。
要实现线上支付功能,要解决的关键问题有以下两个:
- 后端接口返回给小程序的是微信或支付宝的支付链接,小程序需要将它转成二维码显示到页面上
- 页面上除了付款二维码,还有公司 logo,客户信息,付款金额等需要生成图片的信息,点击页面底部的保存图片按钮后,将上述信息生成图片并保存到本地相册
综合以上两步,实现需求在技术上要解决的问题包括以下几点:
- 使用 Canvas 将链接转成二维码,显示到页面上,可以借助一个第三方库 weapp-qrcode 来实现,这个库是给微信小程序使用的,但钉钉小程序里也可以使用,需要改下源码
- 将整个页面的元素画到另一个 Canvas 上,但问题是如何将二维码 Canvas 画到另一个 Canvas 上呢,这一点开发时有遇到坑,后面会说, 本次我是采用了个小技巧,保存图片时,先使用 toTempFilePath 将二维码 Canvas 转成临时图片,然后画到另一个 Canvas 上,再使用 toTempFilePath 将另一个 Canvas 转成临时图片,最后使用 dd.saveImage 将临时图片保存到本地相册
- 小程序 Canvas 里面内容的自适应
技术实现
页面实现
<view class="container">
// 省略一些代码
<canvas canvas-id="myQrCode" id="myQrCode" class="pay-code"></canvas>
// 省略一些代码
</view>
</view>
最终效果如下:
页面上的二维码就是使用 weapp-qrcode 实现的,由于原生小程序不能使用 npm 安装第三方库,所以我们需要将源码下载到项目目录中,官方文档也给了使用示例:
我下载后需要改下源码,就是将 weapp.qrcode.esm.js 文件中使用到的微信小程序api替换成钉钉小程序的api,全局搜索 wx. 并替换为 dd. 。
第一步:在页面引入插件:
import drawQrcode from \'/utils/weapp.qrcode.esm.js\'
const app = getApp()
page({
data:{
},
onload() {
}
})
第二步:在 onload 生命周期将二维码画到 Canvas 上:
import drawQrcode from \'/utils/weapp.qrcode.esm.js\'
const app = getApp()
page({
data:{
},
onload(query) {
let self = this
let { qrCodeLink } = query
setTimeout(() => {
drawQrcode({
width: 250,
height: 250,
canvasId: \'myQrCode\',
text: qrCodeLink,
})
}, 500)
}
})
这一步有两个要注意的地方,一个是设置了一个倒计时,是为了保证执行 drawQrcode 的时候为了保证能获取到页面上的 canvas 了,否则二维码画不出来,另一个就是 canvas 的 id,插件上的 canvasId 对应的是页面元素上的 canvas-id 属性,而钉钉小程序的 canvasId 对应的是页面元素上的 id,这一点没注意到的话会影响下一步。
第三步:将二维码转为临时图片文件
import drawQrcode from \'/utils/weapp.qrcode.esm.js\'
const app = getApp()
page({
data:{
filePath: \'\'
},
onload() {
let self = this
let { qrCodeLink } = query
setTimeout(() => {
drawQrcode({
width: 220,
height: 220,
canvasId: \'myQrCode\',
text: qrCodeLink,
})
setTimeout(() => {
let ctx = dd.createCanvasContext(\'myQrCode\')
ctx.toTempFilePath({
fileType: "jpg",
quality: 1,
canvasId: \'myQrCode\',
success: function(res) {
self.setData({
filePath: res.filePath
})
},
fail: function(e) {
console.log(\'fail:\', e)
}
})
}, 500)
}, 500)
}
})
这一步使用到了 toTempFilePath 方法,仍旧了设置了一个1秒的倒计时,为什么这样做呢? 因为上一步的 drawQrcode 是个耗时的同步任务,将 canvaas 转成图片前需要保证 canvas 已经在页面上生成了。需要注意的是 dd.createCanvasContext(\'myQrCode\') 和 toTempFilePath 方法里的 canvasId 对应的是页面元素上的 id 属性。
第四步:使 Canvas 上的内容自适应
在onload生命周期里已经获取到屏幕尺寸:
dd.getSystemInfo({
success(res){
self.setData({
canWidth: res.windowWidth / 750, // 750宽的设计稿
canHeight: res.windowWidth / 750 * 1239 // 750px 宽设计稿导出图片的高度像素
})
}
})
设置最终要转成图片的 canvas 宽高:
<canvas style="width:{{canWidth*750}}px;height:{{canHeight}}px;position:absolute;left:-1000px;top:-1000px;" canvas-id="myCanvas" id="myCanvas" class="myCanvas"></canvas>
同时我还设置了绝对定位,目的是让这个画布脱离文档流并且显示在屏幕之外。
将元素绘制到 canvas 上:
let rpx = res.windowWidth / 750
const ctx = dd.createCanvasContext(\'myCanvas\')
ctx.setFillStyle(\'#fff\'); // 默认白色
ctx.drawImage(\'/static/icon/logo.png\',rpx * 307, rpx * 32, rpx * 135.2, rpx * 64)
ctx.fillRect(0, 0, rpx * 750, res.windowWidth / 750 * 1239) // fillRect(x,y,宽度,高度)
ctx.setFontSize(rpx * 56)
ctx.setFillStyle(\'#191F25\')
ctx.setTextAlign(\'center\')
ctx.fillText(self.data.shopName, rpx * 750 / 2, rpx * 176)
ctx.setFontSize(rpx * 24)
ctx.setFillStyle(\'#333333\')
ctx.fillText(\'档口ID:\'+ self.data.shopId, rpx * 750 / 2, rpx * 246)
ctx.setFontSize(rpx * 28)
ctx.setFillStyle(\'#333333\')
ctx.fillText(\'支付金额\', rpx * 750 / 2, rpx * 338)
ctx.setFontSize(rpx * 48)
ctx.setFillStyle(\'#333333\')
ctx.fillText(\'¥\' + self.data.totalAmount, rpx * 750 / 2, rpx * 396)
ctx.drawImage(self.data.bankType == 2 ? \'/static/icon/wechat.png\' : \'/static/icon/alipay.png\',rpx * 153, rpx * 478, rpx * 64, rpx * 64)
ctx.setFontSize(rpx * 28)
ctx.setFillStyle(\'#333333\')
ctx.setTextAlign(\'left\')
ctx.fillText(self.data.bankType == 2 ? \'微信\' : \'支付宝\', rpx * 236, rpx * 520)
ctx.setFontSize(rpx * 28)
ctx.setFillStyle(\'#3296FA\')
ctx.setTextAlign(\'left\')
ctx.fillText(\'请使用\' + (self.data.bankType == 2 ? \'微信\' : \'支付宝\') + \'扫一扫\', rpx * 355, rpx * 500)
ctx.setFontSize(rpx * 28)
ctx.setFillStyle(\'#3296FA\')
ctx.fillText(\'扫描二维码支付\', rpx * 355, rpx * 544)
ctx.drawImage(self.data.filePath, rpx * 149, rpx * 570, rpx * 452, rpx * 458)
ctx.setFontSize(rpx * 24)
ctx.setFillStyle(\'#919497\')
ctx.setTextAlign(\'center\')
ctx.fillText(\'充值单号\', rpx * 750 / 2, rpx * 1095)
ctx.setFontSize(rpx * 24)
ctx.setFillStyle(\'#919497\')
ctx.setTextAlign(\'center\')
ctx.fillText(self.data.applyId, rpx * 750 / 2, rpx * 1134)
ctx.draw(true)
上面代码中的数值都是直接在设计稿上量出来的乘以 rpx 后就能自适应显示了。
最后一步:将 canvas 转成图片并保存到相册,这些操作在 draw 方法的回调方法里执行:
dd.showLoading() // 点击保存图片按钮后展示 loading
let rpx = res.windowWidth / 750
const ctx = dd.createCanvasContext(\'myCanvas\')
// 省略一些代码
ctx.draw(true, (()=>{
setTimeout(()=>{
ctx.toTempFilePath({
fileType: "jpg",
quality: 1,
canvasId: \'myCanvas\',
success: function(res) {
dd.saveImage({
url: res.filePath,
showActionSheet: true,
success: () => {
dd.hideLoading()
dd.alert({
title: \'保存成功\',
});
},
fail: function() {
dd.hideLoading()
dd.alert({
title: \'保存失败\',
});
}
});
},
fail: function() {
dd.hideLoading()
dd.alert({
title: \'保存失败\',
});
}
})
}, 1000)
})())
到这里就基本实现需求了,当然还有可以优化的地方。导出的图片效果图如下:
总结
需求是实现了,但还是有几个点是值得再思考一下的:
- canvas 转成图片后不清晰的问题
- 保存图片到相册时,如果用户已禁止钉钉访问相册的话,如何给予用户友好的提示
水平有限,文中难免有不足之处,欢迎大家关注我的微信公众号。(前端民工)
以上是关于小程序怎么临时加载本地相册图片的主要内容,如果未能解决你的问题,请参考以下文章
2020-08-29 Uniapp APP端、小程序长按图片保存图片到本地相册(亲测可用)
微信小程序导出当前画布指定区域的内容并生成图片保存到本地相册(canvas)