⼩程序利⽤Canvas绘制图⽚和竖排⽂字详解
前⾔
闲暇时间抽个空写了个三国杀武将⼿册的⼩程序,中间有个需求设计的是合成武将⽪肤图、竖排的武将姓名、以及⼩程序码,然后提供保存图⽚到相册,最终让⽤户可以分享到朋友圈或其他平台。合成图⽚应该按照 Canvas 的⽂档来做都没什么问题,主要是有个竖排⽂字的需求,这⾥和⼤家分享⼀下。
正⽂
⾸先放⼀张最终保存到相册的图⽚吧~
⾃我感觉良好,⾄少达到了我⾃⼰的预期吧~~~
下⾯让我们⼀步⼀步来看看如何实现的吧。
整个图⽚分为三个部分:
武将图⽚
⼩程序码
武将⽂字信息
先来看⼀下 wxml ⾥⾯的代码,主要是放了⼀个 canvas 标签,控制了⼀下⾼度和宽度属性。
<view>
<canvas class='share-canvas' canvas-id="share_canvas"></canvas>
</view>
武将图⽚
drawHeroImage: function (path) {
var that = this;
// 拿到canvas context
let ctx = wx.createCanvasContext('share_canvas');
// 为了保证图⽚⽐例以及绘制的位置,先要拿到图⽚的⼤⼩
src: path,
success: function (res) {
// 计算图⽚占⽐信息
let maxWidth = Math.min(res.width, that.data.canvasWidth * 0.65);
let radio = maxWidth / res.width;
let offsetY = (that.data.canvasHeight - res.height * radio) / 2;
console.log('offsetY=' + offsetY);
that.setData({
imageWidth: res.width * radio,
imageHeight: res.height * radio,
offsetY: offsetY,
});
// 绘制canvas背景,不属于绘制图⽚部分
ctx.setFillStyle('white')
ctx.fillRect(0, 0, that.data.canvasWidth, that.data.canvasHeight);
// 绘制武将图⽚,path是本地路径,不可以传⽹络url,如果是⽹络图⽚需要先下载
ctx.drawImage(path, 10, offsetY, res.width * radio, res.height * radio)
// 绘制⼩程序码
that.drawQrCodeImage(ctx);
// 绘制势⼒汉字:吴
that.drawInfluence(ctx, that.data.hero.HERO.INFLUENCE);
/
/ 绘制武将姓名:陆逊
that.drawName(ctx, that.data.hero.HERO.NAME);
// 绘制武将称号:江陵侯
that.drawHorner(ctx, that.data.hero.HERO.HORNER);
// 最终调⽤draw函数,⽣成预览图
// ⼀个坑点:只能调⽤⼀次,否则后⾯的会覆盖前⾯的
ctx.draw();
}
});
}
⼩程序码
⼩程序码和武将图⽚是⼀个类型,⽆⾮就是需要计算绘制的位置,这⾥就不再展⽰相关代码了。
武将⽂字信息
从刚刚的代码可以看出,我分了3个部分来绘制,其实吴和陆逊应该是可以放到⼀起的,但是我在绘制的时候发现,空格在绘制的时候会引起异常,导致空格后⾯的⽂字⽆法绘制出来,所以我这⾥吴和陆逊中间的空⽩是靠位置偏移来做的。
这⾥就展⽰⼀下如何绘制武将称号的。
// 绘制武将称号:江陵侯
drawHorner: function (ctx, text) {
// 设置字号
ctx.setFontSize(26);
// 设置字体颜⾊
ctx.setFillStyle("#000000");
// 计算绘制起点
let x = this.data.offsetX + 35;
let y = this.data.offsetY + 10;
console.log('drawHorner' + text);
console.log(x);
console.log(y);
// 绘制竖排⽂字,这⾥是个Util函数,具体实现请继续看
Canvas.drawTextVertical(ctx, text, x, y);
}
绘制竖排⽂字从⽹上了个开源的代码,需要看原理的请看
当然我这⾥为了适⽤⼩程序做了些改动,函数原型是这样⼦的:
CanvasRenderingContext2D.prototype.letterSpacingText = function (text, x, y, letterSpacing)
原谅我不是很会 js ,完全不懂这是个什么语法,看了⼀会没弄懂,感觉像是给类添加新的属性,不管他。
不管⽩猫⿊猫,能抓到耗⼦就是好猫
改造后的函数像下⾯的样⼦:
canvas.js
/**
* @author zhangxinxu()
* @licence MIT
* @description www.zhangxinxu/wordpress/?p=7362
*/
function drawTextVertical(context, text, x, y) {
var arrText = text.split('');
var arrWidth = arrText.map(function (letter) {
return 26;
// 这⾥为了到那个空格的 bug 做了许多努⼒,不过似乎是⽩费⼒了
// const metrics = asureText(letter);
// console.log(metrics);
// const width = metrics.width;
// return width;
});
var align = Align;
var baseline = Baseline;
if (align == 'left') {
x = x + Math.max.apply(null, arrWidth) / 2;
} else if (align == 'right') {
x = x - Math.max.apply(null, arrWidth) / 2;
svg和canvas的区别}
if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') {
y = y - arrWidth[0] / 2;
} else if (baseline == 'top' || baseline == 'hanging') {
y = y + arrWidth[0] / 2;
}
// 开始逐字绘制
arrText.forEach(function (letter, index) {
// 确定下⼀个字符的纵坐标位置
var letterWidth = arrWidth[index];
// 是否需要旋转判断
var code = letter.charCodeAt(0);
if (code <= 256) {
// 英⽂字符,旋转90°
} else if (index > 0 && text.charCodeAt(index - 1) < 256) {
// y修正
y = y + arrWidth[index - 1] / 2;
}
context.fillText(letter, x, y);
// 旋转坐标系还原成初始态
context.setTransform(1, 0, 0, 1, 0, 0);
// 确定下⼀个字符的纵坐标位置
var letterWidth = arrWidth[index];
y = y + letterWidth;
});
// ⽔平垂直对齐⽅式还原
}
drawTextVertical: drawTextVertical
}
绘制⽹络图⽚
由于⽹络图⽚⽆法直接绘制,所以需要先下载到本地,然后再按住本地图⽚绘制的流程⾛⼀遍。downloadHeroImage: function () {
// 不⽀持⾮https的图⽚下载,这⾥了个替换
let url = this.data.hero.place(/http/, "https");
var that = this;
wx.downloadFile({
url: url,
success: function (res) {
// 下载成功后拿到图⽚的路径,然后开始绘制
var path = pFilePath;
that.drawHeroImage(path);
}, fail: function (res) {
console.log(res)
}
});
}
保存图⽚
说了这么多,⾃然少不了最终的⼀步,将绘制到 canvas 的图⽚保存到⼿机相册,这⾥需要⽤户授权,你需要⾃⼰处理。⽤的是给我们提供的接⼝ wx.canvasToTempFilePath 。需要我们传⼊起点坐标 (x, y)和画布⼤⼩ (width, height) 以及canvasId 。
saveShareImage: function () {
wx.showLoading({
title: '正在保存图⽚..',
});
let that = this;
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: that.data.canvasWidth,
height: that.data.canvasHeight,
canvasId: 'share_canvas',
success: function (res) {
wx.saveImageToPhotosAlbum({
filePath: pFilePath,
success(res) {
console.log(res);
wx.showToast({
title: '保存到相册成功',
duration: 1500,
})
},
fail(res) {
console.log(res)
wx.showToast({
title: '保存到相册失败',
icon: 'fail'
})
},
complete(res) {
console.log(res)
}
})
}
})
}
开源
本着开源的精神,源码已经放在上,⼤家可以去上⾯查看具体代码。
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论