写一个舔狗日记
不要让自己的底限成为你的上限
思路
- 熊猫头表情包固定的宽高,储存熊猫头高度
- 次要内容,标题、时间、天气,此处也需要记录高度以及间距(这些内容可以控制显示)
- 随机的文案
- 可以生成图片,canvas直接渲染会变成一行,所以需要根据每行多少字做切割
render node节点
处理页面文案显示,舔狗文案是从网上扒的,包含了****
高亮内容。通过切割**
形式高亮文本,小程序无法直接渲染wxml,需要转换成array形式。
getNodeText(text) {
const soulTextArray = text.split('**');
let nodes = []
for (const key of Object.keys(soulTextArray)) {
nodes.push({
name: 'view',
attrs: {
class: key % 2 === 0 ? 'text' : 'lineheight'
},
children: [{
type: 'text',
text: soulTextArray[key]
}]
})
}
return nodes;
}
动态处理canvas高度
setCanvasHeight() {
let canvasDrawHeightOffset = 20;
const { title, time, weather } = this.data.checkboxValue;
if (title) {
canvasDrawHeightOffset = canvasDrawHeightOffset + 4;
} else {
canvasDrawHeightOffset = 0;
}
if (weather || time) {
canvasDrawHeightOffset = canvasDrawHeightOffset + 40;
}
// 这里处理空白区域的
if (!title && !time && !weather) {
canvasDrawHeightOffset = 20;
}
this.setData({
// 按照每行高度 18 * 每句句子 + 图片高度 + canvas渲染文案差距的高度
'canvasInfo.height': 18 * this.data.textArr.length + this.data.imgHeight + canvasDrawHeightOffset
});
}
生成canvas内容
需要注意的是,我用的是最新的2.17.0
的库,使用的也是小程序最新的api<canvas type="2d" id="myCanvas"></canvas>
,之前的api被官方废弃了。
wxml
通过动态高度,这样就能动态适应文案内容了。
<canvas type="2d" id="myCanvas" style="width: 300px;height: {{canvasInfo.height}}px;" bindlongtap="saveCanvas"></canvas>
js
小程序canvas这块的api更新后用法需要注意
const ctx = canvas.getContext('2d');
获得Canvas
的绘图上下文- 需要先拿到页面canvas实例,然后使用
canvas.requestAnimationFrame
添加到计划中的动画帧 - 渲染图片使用
canvas.createImage()
, ctx.drawImage(this._img, 0, 0, 300, imgHeight * 2, 75, 20, 150, imgHeight); - 结尾使用
ctx.restore()
即可,每次对ctx
的操作都会由requestAnimationFrame
自动渲染
// 页面转canvas
pageToCanvas() {
this.setData({
canvasHidden: false
});
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec(this.canvasInit.bind(this));
},
// canvas初始化
canvasInit(res) {
const width = res[0].width
const height = res[0].height
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
// 先清空一次
ctx.clearRect(0, 0, 300, 300);
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = width * dpr;
canvas.height = height * dpr;
this.setData({
'canvasInfo.destWidth': width * dpr,
'canvasInfo.destHeight': height * dpr
})
ctx.scale(dpr, dpr);
const renderLoop = () => {
this.drawDog(ctx, canvas);
// 控制渲染
if (this.data.countDraw < 3) {
this.setData({
countDraw: this.data.countDraw + 1
});
canvas.requestAnimationFrame(renderLoop);
}
};
canvas.requestAnimationFrame(renderLoop);
// 渲染图片
const img = canvas.createImage();
img.onload = () => {
this._img = img;
}
img.src = this.data.dogOptions.img;
this.setData({
canvas
});
},
drawDog(ctx, canvas) {
if (!this._img) return;
// 获得句子
const {
imgHeight,
canvasInfo: {height}
} = this.data;
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, 300, height);
ctx.fillStyle = '#000000';
ctx.font = 'normal 14px sans-serif';
// 渲染图片高度
ctx.drawImage(this._img, 0, 0, 300, imgHeight * 2, 75, 20, 150, imgHeight);
let canvasDrawHeightOffset = 4;
if (this.data.checkboxValue.title) {
ctx.font = 'normal bold 16px sans-serif';
ctx.fillText('舔狗日记', 300 / 2 - 30, imgHeight + canvasDrawHeightOffset);
canvasDrawHeightOffset = canvasDrawHeightOffset + 20;
}
ctx.font = 'normal 14px sans-serif';
// canvas渲染平行offset
let timeOffset = 0;
let timeText = '';
if (this.data.checkboxValue.time) {
timeText += this.data.currTime;
timeOffset = timeOffset - 30;
}
if (this.data.checkboxValue.weather) {
timeText += ' 阴';
timeOffset = timeOffset - 10;
}
console.log(this.data.checkboxValue)
if (timeText) {
ctx.fillText(timeText, 300 / 2 + timeOffset, imgHeight + canvasDrawHeightOffset);
canvasDrawHeightOffset = canvasDrawHeightOffset + 20;
}
for (const [key, value] of Object.entries(this.data.textArr)) {
ctx.fillText(value, 20, imgHeight + key * 18 + 4 + canvasDrawHeightOffset);
}
ctx.restore();
},
保存canvas渲染后的图片
小程序canvas这块的api更新后用法需要注意
保存图片前需要把canvas转成图片,通过canvasToTempFilePath
api可以拿到canvas转成图片后的内容
生成图片
async getCurrentCanvasImage() {
return new Promise((resolve, reject) => {
wx.canvasToTempFilePath({
canvas: this.data.canvas,
success: (toFile) => {
console.log(toFile.tempFilePath);
resolve(toFile.tempFilePath);
},
fail: (res) => {
console.log('图片生成失败');
reject(null);
}
});
});
}
保存图片
使用saveImageToPhotosAlbum
可以保存图片,但是需要拿到保存图片的权限,如果没有权限需要进行判断提示用户开启权限
async saveCanvas() {
const filePath = await this.getCurrentCanvasImage();
// 此处做一下失败处理
if (!filePath) {
wx.showModal({
title: '失败',
content: '未知原因,生成图片失败'
});
return;
}
wx.saveImageToPhotosAlbum({
filePath,
success: (res) => {
wx.showToast({
title: '图片已保存',
});
},
fail: async (error) => {
const settingInfo = await wx.getSetting();
if (!settingInfo.authSetting['scope.writePhotosAlbum']) {
wx.showModal({
title: '没有权限',
content: '请开启相册权限',
success: () => {
wx.openSetting({
success: (setting) => {
console.log(setting)
}
});
}
});
}
}
})
}
成果: