云开发最近开始出现大幅度调价,并配置了一个默认的最低消费:19.9 元;说起来,这个钱不多,奈何我的很多个小程序都属于用量极小,但也不能停止运转的。过去的按量付费的模式对于我这样佛系运营的小程序会比较友好。但现在这种付费模式对于我这种每个小程序量级都不大,但又多个小程序的人来说不太友好。既然我能给开发一套标准的后端,且确实觉得没必要,就花点时间从云开发迁移到自建的服务器。
评估修改工作量
在开始动手迁移的时候,先要评估一下迁移所需要的成本,我选择使用 grep 查询一下我的变更的工作量。执行如下命令,来判断我具体在哪些文件当中调用了云开发的方法,判断出具体的工作量
grep -w -c -E 'callFunction|db.collection|wx.cloud' ./**/**.wpy
执行命令后,你会看到类似这样的输出,这个输出表现出了我的具体没个文件所需要的修改的量。
这样对于具体要做的工作量就心里有数了。接下来要做的,无非是具体的迁移工作了。
评估云函数工作量
云开发用久了,很容易出现一些其实不再运转的函数。在这种情况下,可以不再迁移这些函数,降低自己的迁移成本。
云函数在迁移的时候,可以通过云开发提供的函数监控面来判断每个函数的调用情况,对于调用情况为 0 的函数,就可以选择性的不再提供服务了。
导出数据库
从云开发迁移走的时候,我们需要迁移小程序侧的代码、云函数代码和云数据库的资源,因此,也需要将对应的数据提供导出和导入。
不过,你需要注意的是,云开发导出的 JSON 不是标准的 JSON 格式,而是 JSON Lines 的格式,在你对应的数据导入时,需要使用 package 来 parse ,而不是使用标准的方式来进行 Parse。
修改代码
当上面的事情做完了,接下来要做的,就是具体的写代码来进行迁移了,记得迁移服务端 & 客户端两侧的代码~。
在小程序当中,我们可以通过在 Canvas 画布上绑定 Tap 事件,来获取到用户点击行为,当我们获取到点击行为对应的坐标时,就可以读取到对应位置的颜色。并根据我们的需求,将颜色转换成我们想要的 RBG 色值。
这就是我们常见的各种取色软件的最常见的实现方式。当然,落实到实际开发过程中,大家或多或少会有所不同(涉及到调色。我们拍照的颜色和我们所看到的颜色并不完全一样。不同的相机调色会有所不同。类似的,我们需要加入相应的逆向算法排除对应的相机自身调教的影响)。
在小程序的场景中具体实现方式可参考如下代码:
页面布局
<view>
<view><button bindtap="chooseImage">1. 选择图片</button></view>
<view><canvas id="myCanvas" bindtap="onCanvasTap" type="2d" style="background-color:gray;width: 100%;margin-top: 100rpx;"></canvas></view>
</view>
对应的 JS
和在 在小程序中使用的 Canvas 2D API 绘制本地图片 一样,你需要提取到 Canvas 的 Ctx ,用于执行操作。当你通过 bindTap 拿到你的点击点相对于 Canvas 的 左上角的坐标后,就可以使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/getImageData">getImageData</a>
来提取具体范围的数据。因为我的目的是提取某个点的颜色,因此我的第三、第四参数都是 1 (取一个像素点的宽度和高度)。
提取出图片数据后,你可以在 data 当中提取一个 4 个元素的数组,按照顺序,分别是 RGBA 的四个值,你可以根据需要使用这个数据(比如放大展示在界面上)。
Page({
data: {
color: ''
},
onCanvasTap(e) {
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const data = ctx.getImageData(e.detail.x, e.detail.y, 1, 1).data;
const [red,gree,blue,alpha] = data;
})
},
});
小程序自 2.9.0 起,不再推荐使用其自己封装的 Canvas Context,而是更加推荐大家使用标准的 Canvas 2D API 来完成相关操作。因此,对于开发者来说,需要将过去的 Canvas API 调整为新的标准的 API 。
刚好我最近也实现了类似的功能,将这部分逻辑分享给大家。
页面布局
<view>
<view><button bindtap="chooseImage">选择图片</button></view>
<view><canvas id="myCanvas" type="2d" style="background-color:gray;width: 100%;margin-top: 100rpx;"></canvas></view>
</view>
页面逻辑
Page({
chooseImage(){
// 创建一个 Query
const query = wx.createSelectorQuery()
// 选中 Canvas 对象
query.select('#myCanvas')
.fields({
node: true,
size: true // 提取 Node 信息
})
.exec((res) => {
// 获取到 Canvas Node
const canvas = res[0].node
// 获取到 Context
const ctx = canvas.getContext('2d')
wx.chooseMedia({
count: 1,
success: function (res) {
// 提取图片的基本信息
wx.getImageInfo({
src: res.tempFiles[0].tempFilePath,
success: imgInfo => {
// 使用 canvas.createImage 来创建一个图片
const img = canvas.createImage()
img.src = imgInfo.path
img.onload = () => {
// 将图片画在画布上
ctx.drawImage(img, 0, 0, imgInfo.width, imgInfo.height)
}
}
})
}
})
})
}
})
参考文档
我最近在做 Crypto 投资,需要一个地方记录自己的持仓和涨跌等信息。因此,有了这个灵感。
这个小程序从整体的功能上,接近于有知有行的记账功能和且慢的且慢小账本。
应该具备以下功能:
- 对接 CMC or Crypto.com 的 API,实现币价实时获取
- 支持计算资产总额
- 支持多个钱包
- 支持展示图表
- 如持仓饼图
- 如价格走势图
- 支持初始化仓位的功能:我在使用非小号的记录的时候,最大的问题就是没有初始化仓位的能力,这对于已经有投资的人来说,比较麻烦。还要计算一下。
- 支持年度总结:大家肯定都有炫富的需求,还是可以搞一搞的
- 支持关注行情:这个需求存疑,我只是想到了。因为有可能我们做不到比金色财经更好,那就没啥意义了。
- 支持与其他公开的 ETF 对比收益率:满足炫富的心理,也可以让你了解到市场上的情况。
- 支持同步钱包的资产:可以让初始化更简单
在小程序开发过程中,用户输入是必不可少的,我们经常会需要用户输入一些内容,来完成产品收集用户信息的需求。
在这种情况下,我们可以考虑借助小程序提供的一些和键盘相关的 API 来优化小程序的使用体验。
Input 组件的 type 属性
Input 组件的 type 属性
从小程序的 1.0 版本开始,就支持为 input 组件设置 type,不同的 type 会显示不同的手机键盘。默认情况下,显示的是 text 文本输入键盘,这个键盘的特点是显示所有的内容,可以适用于所有的场景。
但,适用于所有场景也就意味着不适用于所有场景,总会在每一个场景中有着种种不便,因此,在实际的开发中,为了获得更佳的体验,你可以通过设置不同的 Type 来控制实际的键盘显示情况。
text 类型 input 适用建议
除了默认的 text 类以外,你还可以使用 number
(数字输入键盘)、idcard
身份证输入键盘和 digit
带小数点的数字键盘。
idcard 类型 input 适用建议
你可以根据自己的实际使用场景来设置不同的类型,比如说
- 如果你的小程序的验证码都是数字的,那么你给出一个
text
类型的键盘,显然不如给一个 number
类型的键盘更合适。 - 如果你的小程序中涉及到了手机号的输入,那么这种情况下你就可以选择使用
number
类型的键盘,来优化用户输入时的体验。
number 类型 input 适用建议
这里的思路是类似的,当你预期用户输入的内容只有数字,就可以考虑 number
、digit
、idcard
等类型,来优化你的小程序的实际使用体验。
digit 类型 input 适用建议
总结
input 组件默认提供的 四种 type ,可以通过选择不同的类型,从而获得不同的体验效果,从而对于你的小程序体验进行优化和推进。
TL;DR
如果想要 marker 可以响应 bindmarkertap 事件,需要设定 marker 的 ID,这一点文档中并没有标注。
具体情况
在开发美食地图时,出现了一个问题,marker 的点击总是不会触发 bindmarkertap 事件。
我的代码是这样写的
store.get().then(res => {
this.setData({
stores: res.data,
windowHeight: app.globalData.windowHeight,
}, () => {
wx.hideLoading();
wx.showToast({
title: '双指缩放可以调整地图可视区域,查看更多美食',
icon: 'none'
})
})
})
在地图中去调用 stores. marker 。
在地图上的确可以出现图标,但是无法点击 maker 并触发事件。
通过复制官方的 marker 的数据进行调试后发现,当 maker 有 id 时,marker 就可以触发事件,因此,怀疑是 ID 的问题。
在美食地图小程序中,我使用的是腾讯云提供的小程序·云开发,其使用的是 MongoDB 作为后台的 Database ,默认的主键为 _id,所以,我自己写了代码来转换 _id。
data.map(item => {
item.id = item._id
});
对应的 commit :https://github.com/CloudKits/miniprogram-foodmap/commit/5abcad1f756e03a388bb33dd1c699d3cae9ea0c4#diff-f5ea41cdd371d7b65bfdf8d32188e37d