说在前面的话
自己第一次制作了海报并且保存相册,故来记录一下。。。分享给需要的朋友,这里以字节小程序为例,明白思路,学以致用!!!
正文开始了(一本正经)
思路:
1.用canvas绘图,将自己的海报绘制出来
2.将绘制好的canvas转化成图片
3.调用api将生成的图片保存到相册
tip:为什么要用canvas呢?因为只有图片才可以保存到相册,把你海报的所有东西变成一张图片,仿佛只能canvas了吧。思路了解,码上就来!
第一步:绘制海报
我们可以想一下海报的构成:背景,头像,昵称,标题,正文,二维码。。。主要可能也就是这些了吧。然后再来分析一下,背景、二维码、头像是图片;昵称标题,正文是文字。所以就是这么简单,图片加文字,自己测量好长宽、位置放置就好啦。第一次绘制canvas的同学不要慌,就是如此简单的吖!!!
我呢还是很贴心的啦,虽然没有成品的海报(毕竟每个人的海报中内容不一样),but 我把文字图片的绘制方法都写出来了,还绘制了一些常用图形,以及海报中可能遇到的一些需求,所以大家根据自己的需要修改他们的大小和位置就好啦
index.js 首先创建一个canvas(我是在onshow中)
data: {
canvasWidth: 375,
canvasHeight: 500,
bgwidth: "",
bgHeight: "",
text: "骑着我心爱的小摩托温婉居家小仙女啦啦啦",
img: ''
},
onShow() {
this.ctx = tt.createCanvasContext("myCanvas");//myCanvas是canvas的id,这个之后我们在html中给大家展示写法
this.createCanves();//调用绘制海报的方法
},
createCanves() {
let ctx = this.ctx;
let imgArr = {
url1:
"../../image/bg.jpg",
url2: "../../image/user.jpg",
};
this.drawBg(imgArr.url1);
let row = this.drawText(this.data.text, 60, 240, 20, 50);
this.drawOther(imgArr.url2, row, 20);//这个是文字换行的方法
ctx.draw(true, () => {
setTimeout(() => {
this.canvasToTempFilePath();
}, 500);
});
},
// 绘制背景
async drawBg(url) {
let ctx = this.ctx;
ctx.setFillStyle("#ffffff");//最好是白色的背景,默认是黑色的
ctx.fillRect(0, 0, this.data.canvasWidth, this.data.canvasHeight);
let bgImg = this.data.canvasBg ? this.data.canvasBg : url;
// drawImage 绘制一个图片,可以有三种写法
// 3参数法:图片,开始横坐标,开始纵坐标。原图铺满整个canvas,导致图片显示不完整
// 5参数法:图片,开始横坐标,开始纵坐标,在canvas中的宽,在canvas中的高。图片回压缩完整显示在指定大小内,图片全部显示
// 7参数法:图片,图片开始截取横坐标,图片开始截取纵坐标,截取图片的宽,截取图片的高,canvas中开始横坐标,canvas中开始纵坐标,在canvas中的宽,在canvas中的高。可以灵活截取图片的大小。
ctx.drawImage(bgImg, 0, 0, this.data.canvasWidth, this.data.canvasHeight);
},
// 绘制其他一些东西,这里可以根据需要自己绘制,我这里只绘制了几个常用的图形
drawOther(url, row, textLineHeight) {
let ctx = this.ctx;
// 画长方形
ctx.save();
ctx.setStrokeStyle("#ffff00");
ctx.strokeRect(50, 150, 50, 50);
ctx.setFillStyle("#ff00ff");
ctx.fillRect(120, 150, 50, 50);
// 画圆,给圆里面添加图片,此时图片会将圆覆盖
let r = 25;
ctx.setStrokeStyle("#ffff00");
ctx.setLineWidth("4");
ctx.setLineCap("round"); //圆环结束断点的样式 butt为平直边缘 round为圆形线帽 square为正方形线帽
ctx.arc(215, 150 + r, r, 0, 2 * Math.PI);
ctx.stroke();
ctx.clip();
ctx.drawImage(url, 215 - r, 150, 50, 50);
ctx.restore();
// 画一个圆角矩形,绘制顺序:左上角-top线-右上角-right线-右下角-bottom线-左下角-left线
let h = 220;
let w = 50;
let r1 = 10;
let textH = row * textLineHeight;
ctx.save();
ctx.beginPath();
ctx.arc(w + r1, h + r1, r1, 1 * Math.PI, 1.5 * Math.PI);
ctx.moveTo(w + r1, h);
ctx.lineTo(w + r1 + 40, h);
ctx.arc(w + r1 + 40 + r1, h + r1, r1, 1.5 * Math.PI, 2 * Math.PI);
ctx.moveTo(w + 3 * r1 + 40, h + r1);
ctx.lineTo(w + 3 * r1 + 40, h + r1 + textH);
ctx.arc(w + r1 + 40 + r1, h + r1 + textH, r1, 0 * Math.PI, 0.5 * Math.PI);
ctx.moveTo(w + 2 * r1 + 40, h + 2 * r1 + textH);
ctx.lineTo(w + r1, h + 2 * r1 + textH);
ctx.arc(w + r1, h + r1 + textH, r1, 0.5 * Math.PI, 1 * Math.PI);
ctx.moveTo(w, h + r1 + textH);
ctx.lineTo(w, h + r1);
ctx.stroke();
ctx.restore();
},
// 在canvas中文字是不会自己换行的,所以这里用到了一个自动换行的方法
drawText(text, startX, startY, lineHeight, MAX_WIDTH) {
let ctx = this.ctx;
let allArr = text.split("");
let rowArr = []; //拆分出来的每一行
let rowStrArr = []; //每一行的文字数组
for (let i = 0; i < allArr.length; i++) {
let currentStr = allArr[i];
rowStrArr.push(currentStr);
let rowStr = rowStrArr.join("");
console.log(rowStr);
if (ctx.measureText(rowStr).width > MAX_WIDTH) {
rowStrArr.pop(); //删除最后一个
rowArr.push(rowStrArr.join(""));
rowStrArr = [currentStr];
continue;
}
// 最后一个字母,直接添加到最后一行
if (i == allArr.length - 1) {
rowArr.push(rowStr);
}
}
ctx.setFillStyle("#ff0000");
// ctx.setTextAlign('center');
for (let i = 0; i < rowArr.length; i++) {
ctx.fillText(rowArr[i], startX, startY + i * lineHeight);
}
return rowArr.length; //返回行数,几行
},
第二步:canvas转换图片
// 转化图片
canvasToTempFilePath() {
let that = this;
// 当canvas太小的时候,转化为图片会变模糊,可以将图片适当调大一些,canvas足够大时则不用,注意canvas大小有限制。
tt.canvasToTempFilePath({
canvasId: 'myCanvas',
x: 0,
y: 0,
width: this.data.canvasWidth,
height: this.data.canvasHeight,
destWidth: this.data.canvasWidth * 2,//destWidth比width大点会更清晰,不要太大
destHeight: this.data.canvasHeight * 2,//destHeight比height大点会更清晰,不要太大
async success(res) {
console.log('image::::', res);
that.setData({
img: res.tempFilePath //将转化好的图片放到data中用来展示
})
},
fail(err) {
_showToast('图片转换失败');
}
});
},
第三步:长按保存到相册
// 前两个函数用来实现长按,在页面中定义touchStart和touchEnd
touchstart(e) {
touchStart = e.timeStamp;
},
touchend(e) {
touchEnd = e.timeStamp;
},
saveImg() {
if (touchEnd - touchStart > 600) {
// 想要保存到相册,需要开启授权
tt.authorize({
scope: 'scope.album',
success: () => {
tt.saveImageToPhotosAlbum({
filePath: this.data.img, // 图片文件路径
success: (res) => {
_showToast('保存成功');
},
fail: res => {
_showToast('保存失败');
}
});
},
fail: res => {
_showToast('授权失败');
}
})
}
}
以上三步走就好了吖,再来看一眼html中是怎么写的吧。
提示一下思路(不要嫌啰嗦呀,小声bb):canvas是有大小限制的,并且canvas大一些会比较清晰,在展示的时候可能会导致展示不全。然后就想着展示一个图片岂不是很好,图片可以自己调整大小还方便操作,所以呢就把那个canvas藏起来,canvas不能隐藏所以只能利用定位把他移出屏幕了。
index.ttml
// 这里的canvas-id就是上面提到的id
<canvas canvas-id="myCanvas" class="canvas" style="width: {{canvasWidth}}px; height: {{canvasHeight}}px;position:absolute;top:-2000px"></canvas>
<image src="{{img}}" mode="" style="width: {{canvasWidth}}px; height: {{canvasHeight}}px;" bindtouchstart="touchstart" bindtouchend="touchend" bindtap="saveImg"/>
希望对大家有帮助呀,以上内容也有查自网络的东西。本人也是小白,哪里不对欢迎指正,告辞!