共计 4344 个字符,预计需要花费 11 分钟才能阅读完成。
应用小程序生成分享海报图,是很常见的性能了,很多的小程序都有相似的性能。尤其是浏览类、学习类的,总能看到朋友圈有人分享“我明天背会了 ** 单词,快来一起学习吧”这种。然而这种海报,都是主动排版生成的,用户是无奈手动参加的。
为了加强用户参与性,咱们模拟一些图片解决 App,实现可能本人拼图生成海报的性能。
1. 整体思路
将整个页面分为 绘制区域 与 操作区域。
- 抉择背景图。
- 减少要绘制的图片、文字等元素(以下简称拼图元素)。减少的拼图元素在绘制区域中展现。
- 在绘制区域中,触控操作拼图元素,获取操作后的后果(地位、缩放等)。
- 在管制区域,对拼图元素进行其它操作,如层叠程序扭转、旋转、缩放等。
- 依据每一个拼图元素的地位、缩放等信息,应用 canvas 进行绘制,并生成图片。
思路确立了,就开始实操。
2. 素材局部
因为小程序有对包大小的限度,所以咱们的图片素材是不能间接扔在小程序里的。
我是间接将素材依照肯定的命名规定间接传到了云存储服务中,小程序中通过规定进行拼接获取所有素材的地址。
初始开发时,能够先把图片素材放在小程序文件夹里。不影响开发者工具内的开发与测试,只不过无奈真机调试与预览。
为了便于操作与计算,所有的图片素材都采纳的对立大小的 png 格局(最好是正方形)。
3. 绘制区域
1. 组件抉择
选用小程序组件 <movable-area>
与 <movable-view>
。
应用上述组件,能够实现拼图元素在绘制区域内的挪动、缩放,监听事件还能够回传触控操作后拼图元素的地位。
具体的应用形式请参考文档。
https://developers.weixin.qq….
留神:
<movable-view>
容许双指缩放后,呈现了 bug,比方挪动拼图元素时会产生图片放大、挪动时产生惯性动画(即便没有设置)等。因而放弃,采纳其余办法实现。*- 通过列表渲染,渲染
<movable-view>
时,肯定要谨严的给出惟一 key 作为wx:key
,不要应用诸如wx:key="index"
。否则,在删除拼图元素、批改拼图元素层叠程序时,会呈现 bug。
2. 单位与比例换算
<movable-area>
元素自身在页面的高宽、定位能够应用 rpx。
其外部的拼图元素宽高、地位等,我集体倡议应用 px 单位。
- 首先,rpx 不是相对单位,是比例单位,最终生成的图片不能应用 rpx 作为宽高单位,否则大家生成的图片大小不统一,为此拼图元素的相干数据也应该间接应用 px 为单位,便于后边的换算。
- 其次,
<movable-view>
的监听事件返回的定位信息也是以 px 为单位的。
比例换算就没什么好说的了,拿到绘制区域的实在宽度,晓得输入指标宽度,算就完了~
4. 操作区域
1. 文字拼图元素的新增
图片拼图元素的减少没什么特地的中央,比拟容易解决,设置好 <movable-view>
的宽、高、x、y 等属性,再在其外部减少 <image>
即可。文字拼图元素的减少绝对麻烦一点。
文字拼图元素能够分成 横排文字 和 竖排文字。
抛开字号、字体、初始的 x、y、等共性的款式 / 属性:
- 横排文字只需为
<movable-view>
设置足够的宽度就好了,否则汉字会换行。 - 竖排文字需为
<movable-view>
设置 1 倍字号的宽度,好让每一个文字刚好占据一行。也因而,要思考line-height
。咱们须要为竖排文字设置一个适合的line-height
。我这里间接设置为了 1.5 倍字号。
2. 缩放与旋转
之前咱们提到过,应用 <movable-view>
自带的缩放性能,呈现了 bug,因而这里通过 <slider>
手动设置缩放倍数。缩放倍数间接反馈在 <movable-view>
的宽高款式上。通过间接批改宽高款式实现缩放倍数,一个益处就是缩放时不会影响拼图元素的地位(由左上缩放,而不是核心缩放)。
旋转角度也通过 <slider>
手动设置(0 – 360)。
留神:
咱们在<movable-area>
中触控滑动拼图元素,本质上是实时批改所选<movable-view>
的transform
款式(translate
)。因而,拼图元素的旋转(transform: rotate(*deg)
),应该加在<movable-view>
的子元素上。
因为比拟麻烦,我这里只为图片拼图元素的缩放与旋转。文字拼图元素的 pass 掉了。
3. 层叠程序的批改
<movable-view>
是采纳 相对定位 的,因而渲染的程序就能代表层叠程序。
5. 应用 canvas 绘制海报图
1. canvas 组件
canvas 组件的大小,设置成为最终海报图输入大小即可。
canvas 组件能够设置相对定位,搁置在屏幕区域外即可。
2. 不同根底库版本,获取上下文对象的办法 / 绘制办法
1. 在根底库 2.9.0 以下版本
应用 wx.createCanvasContext()
办法获取上下文对象。
而后应用小程序的 CanvasContext
API 进行绘制操作。
详情请参考文档:https://developers.weixin.qq….
留神:
- drawImage 办法第一个参数为图片资源的本地地址,如果应用网络图片,须要通过
getImageInfo / downloadFile
获取本地地址。- 应用
wx.canvasToTempFilePath
办法生成图片,须要将其放在draw
函数的回调里,来保障图片胜利导出。
2. 根底库 2.9 及以上(我用的本办法,后边介绍的留神状况中,有些基于此)
在 canvas
组件中设置 type = "2d"
,通过 SelectorQuery
获取 canvas 实例,进而获取上下文对象。
上下文对象的相干 API 与 Web 原生统一。
query.select('#myCanvas').fields({node: true, size: true}).exec(res => {const canvas = res[0].node;
const ctx = canvas.getContext('2d');
canvas.width = 1000;
canvas.height = 1500;
// 其它操作...
})
留神:
- 获取 canvas 实例后,须要显示的设置画布的宽高。画布宽高最大值为 1365 * 1365。
- drawImage 办法在这里与 Web 原生办法统一,第一个参数须要为 HTMLImageElement(这里咱们只探讨 HTMLImageElement 的状况),因而须要先应用
canvas.createImage
办法创立图片对象,设置 src,并期待加载结束。- 应用
canvas.toDataURL
返回一个蕴含图片展现的 data URI(data:<mediatype>;base64,<data>)。
3. 一些要留神的中央
1. 所有的原始间隔数据都要通过比例校对到 canvas 的尺寸比例上。
2. 竖排文字的绘制
通过逐字绘制的形式实现。
用 y + n * line-height - (line-height - font-size) / 2
的形式确定第 n 个汉字的的垂直绘制地位 y’。
肯定要记住减去 line-height
带来的空白。
3. 绘制文字时,设置字体属性
ctx.font = "字号 字体"
必须采纳残缺格局,否则会生效。
4. 文字绘制时,垂直地位 y
会多出 1 倍字号左右的间隔
不晓得什么起因导致的,反正我这里呈现了。
呈现了的话,记得补上。
5. 绘制图片的旋转时
ctx.save();
ctx.translate(图片中心点 x,y);
ctx.rotate(度数);
//(负的图片宽高的一半)drawImage(img, -x, -y);
ctx.restore();
ctx.rotate()的度数是 弧度。
6. 网络图片加载问题
应用 drawImage
办法绘制图片时,须要先应用 canvas.createImage
办法创立图片对象,设置 src,并期待加载结束。
而所有的拼图素材都是网络图片,异步加载,因而会呈现 因为加载速度不同导致绘制程序错乱、返回 dataURI 时图片加载未实现导致局部素材绘制未实现 等问题。
因而须要期待所有要用的素材加载结束之后,再进行绘制。
这里我将图片的加载封装为 Promise
对象,通过 Promise.all()
实现。
addImagePromise(canvas, temp){return new Promise((resolve, reject) => {let imgEle = canvas.createImage();
imgEle.src = temp.image;
imgEle.onload = function(){
//!!! 很多人在这里都会习惯写 imgEle.onload = null; 然而小程序内会报错
resolve(imgEle);
}
})
}
...
let eleImagePromiseList = [];
for(let i = 0; i < eles.length; i++){let temp = eles[i];
if(temp.type == 1){
// 文字
eleImagePromiseList.push(null);
}
if(temp.type == 2){
// 图片
eleImagePromiseList.push(this.addImagePromise(canvas, temp));
}
}
Promise.all(eleImagePromiseList).then(res => {// 加载结束,开始绘制})
6. 把返回的 data URI 存入零碎相册
通过 小程序的文件管理器 将 data URI 中 base64 编码的图片局部存储到本地,再保留至相册。
let fs = wx.getFileSystemManager();
let imagePath = wx.env.USER_DATA_PATH + '/myPost.png';
let imageSrc = this.data.drawImageSrc.replace(/^data:image\/\w+;base64,/, '');
fs.writeFile({
filePath: imagePath,
data: imageSrc,
encoding: "base64",
success(res){
wx.saveImageToPhotosAlbum({
filePath: imagePath,
success(res) {//...}
})
}
})
7. 真机调试问题
在 canvas
组件中设置 type = "2d"
的形式进行 canvas 绘制时,进行真机调试时,会报错,报错来源于 通过 createSelectorQuery
获取 canvas 实例。
能够间接通过 预览 查看成果。如果的确须要调试,请更新开发工具,应用 真机调试 2.0 进行。
相干材料:
https://developers.weixin.qq….
https://developers.weixin.qq….
8. 完结
至此,整体思路与开发时呈现的问题介绍结束。欢送各位探讨交换。