canvasUtils.js
/**
* 绘制矩形
* 参数:cxt、x坐标、y坐标、宽度、高度、圆角、颜色
*/
function fillRoundRect(cxt, x, y, width, height, radius, fillColor) {
console.log(cxt,'fillRoundRect')
//圆的直径必然要小于矩形的宽高
if (2 * radius > width || 2 * radius > height) {
return false;
}
cxt.save();
cxt.translate(x, y);
//绘制圆角矩形的各个边
drawRoundRectPath(cxt, width, height, radius);
cxt.fillStyle = fillColor || '#000'; //若是给定了值就用给定的值否则给予默认值
cxt.fill();
cxt.restore();
}
// 绘制矩形各个边过程
function drawRoundRectPath(cxt, width, height, radius) {
cxt.beginPath(0);
//从右下角顺时针绘制,弧度从0到1/2PI
cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2);
//矩形下边线
cxt.lineTo(radius, height);
//左下角圆弧,弧度从1/2PI到PI
cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);
//矩形左边线
cxt.lineTo(0, radius);
//左上角圆弧,弧度从PI到3/2PI
cxt.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2);
//上边线
cxt.lineTo(width - radius, 0);
//右上角圆弧
cxt.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2);
//右边线
cxt.lineTo(width, height - radius);
cxt.closePath();
}
/**
* 绘制矩形边框
* 参数:cxt、x坐标、y坐标、宽度、高度、圆角、border颜色
*/
function roundRectBorder(cxt, x, y, w, h, r,borderWidth) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
cxt.beginPath();
cxt.moveTo(x+r, y);
cxt.arcTo(x+w, y, x+w, y+h, r);
cxt.arcTo(x+w, y+h, x, y+h, r);
cxt.arcTo(x, y+h, x, y, r);
cxt.arcTo(x, y, x+w, y, r);
cxt.closePath();
cxt.lineWidth = borderWidth
}
//加载图片
function getImageInfo(image) {
return new Promise((req, rj) => {
// #ifndef APP-PLUS
uni.getImageInfo({
src: image,
success: function(res) {
req(res)
},
});
// #endif
// #ifdef APP-PLUS
if(uni.getSystemInfoSync().platform == 'ios'){
uni.downloadFile({
url: image,
success: (res) => {
res.path = res.tempFilePath
req(res)
},
})
}else{
uni.getImageInfo({
src: image,
success: function(res) {
req(res)
},
});
}
// #endif
})
}
/**
* 绘制圆形头像
* 参数:cxt、图标路径path、x坐标、y坐标、宽度、高度
*/
function drawCircular(ctx, url, x, y, width, height) {
//画圆形头像
var avatarurl_width = width;
var avatarurl_heigth = height;
var avatarurl_x = x;
var avatarurl_y = y;
ctx.save();
ctx.beginPath();
ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);
ctx.clip();
ctx.drawImage(url, avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth);
ctx.restore();
}
/*
* 绘制图片cover
* t:cxt;
* e:图片属性(通过getImageInfo获取)
* s:x坐标
* o:y坐标
* i:绘制图片宽度
* a:绘制图片高度
*/
function drawImgCover(t, e, s, o, i, a) {
console.log(e,'drawImgCover')
if (e.width / e.height >= i / a) {
var r = e.height,
n = Math.ceil(i / a * r);
t.drawImage(e.path, (e.width - n) / 2, 0, n, e.height, s, o, i, a)
} else {
var c = e.width,
l = Math.ceil(a / i * c);
t.drawImage(e.path, 0, (e.height - l) / 2, e.width, l, s, o, i, a)
}
}
/*
* 文本自定义换行
* family = " 'PingFang SC',tahoma,arial,'helvetica neue','hiragino sans gb','microsoft yahei',sans-serif";
* var options = {
font:"22px" + family, 字体大小
ctx:ctx, uni.createCanvasContext('firstCanvas')
word:"文字", 文字
maxWidth:750, 最大宽度
maxLine:2, 最大行数
x:100, x坐标
y:100, y坐标
l_h:40 行高
}
* callback 自定义函数
*/
function dealWords(options, callback) {
options.ctx.font = options.font; //设置字体
callback && callback();
var allRow = Math.ceil(options.ctx.measureText(options.word).width / options.maxWidth); //实际总共能分多少行
var count = allRow >= options.maxLine ? options.maxLine : allRow; //实际能分多少行与设置的最大显示行数比,谁小就用谁做循环次数
var endPos = 0; //当前字符串的截断点
for (var j = 0; j < count; j++) {
var nowStr = options.word.slice(endPos); //当前剩余的字符串
var rowWid = 0; //每一行当前宽度
if (options.ctx.measureText(nowStr).width > options.maxWidth) { //如果当前的字符串宽度大于最大宽度,然后开始截取
for (var m = 0; m < nowStr.length; m++) {
rowWid += options.ctx.measureText(nowStr[m]).width; //当前字符串总宽度
if (rowWid > options.maxWidth) {
if (j === options.maxLine - 1) { //如果是最后一行
options.ctx.fillText(nowStr.slice(0, m - 1) + '...', options.x, options.y + (j + 1) * options.l_h); //(j+1)*18这是每一行的高度
} else {
options.ctx.fillText(nowStr.slice(0, m), options.x, options.y + (j + 1) * options.l_h);
}
endPos += m; //下次截断点
break;
}
}
} else { //如果当前的字符串宽度小于最大宽度就直接输出
options.ctx.fillText(nowStr.slice(0), options.x, options.y + (j + 1) * options.l_h);
}
}
}
/*
* 绘制圆角按钮
* ctx:createCanvasContext
* color:背景颜色;
* x:x坐标
* y:y坐标
* width:宽
* height:高
* radius:圆角
* text:文字
* fontColor:文字颜色
* textAlign: left/center/right
*/
function drawButton(ctx, color, x, y, width, height, radius, text, fontColor, textAlign) {
//分为4条直线4个圆角绘制
ctx.beginPath();
ctx.fillStyle = color;
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.arc(x + width - radius, y + radius, radius, Math.PI * 3 / 2, Math.PI * 2);
ctx.lineTo(x + width, y + height - radius);
ctx.arc(x + width - radius, y + height - radius, radius, Math.PI, Math.PI / 2);
ctx.lineTo(x + radius, y + height);
ctx.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI);
ctx.lineTo(x, y + radius);
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = fontColor;
// ctx.font = 'normal bold 12px sans-serif';
ctx.setTextAlign(textAlign)
ctx.textBaseline = "middle";
ctx.fillText(text, x + width / 2, y + height / 2);
}
export default {
fillRoundRect:fillRoundRect, //绘制矩形
roundRectBorder:roundRectBorder,//绘制矩形+边框
getImageInfo:getImageInfo, //加载图片
drawCircular:drawCircular, //绘制圆形头像
drawImgCover:drawImgCover, //绘制图片cover
dealWords:dealWords, //文本自定义换行
drawButton:drawButton, //绘制圆角按钮
}
示例
<canvas canvas-id="myctx" :style="{ width: posterObj.width + 'px', height: posterObj.height + 'px'}" disable-scroll></canvas>
import canvasUtils from '@/utils/canvasUtils.js'; // 加载万能绘制方法
data() {
return {
posterObj: {
show: false,
height: 500,
width: 380,
qcCode:""
},
}
}
methods:{
savePoster() {
uni.canvasToTempFilePath({
canvasId: 'myctx',
success: (res) =>{
console.log(res.tempFilePath)
uni.saveImageToPhotosAlbum({//保存图片到系统相册。
filePath: res.tempFilePath,//图片文件路径
success: function() {
uni.showToast({
title: '海报保存成功',
icon: 'none',
});
},
fail: function(e) {
console.log(e);
uni.showToast({
title: '海报保存失败',
icon: 'none',
});
}
});
}
})
},
async createPosterObj() {
const ctx = uni.createCanvasContext("myctx", this)
let family = " 'PingFang SC',tahoma,arial,'helvetica neue','hiragino sans gb','microsoft yahei',sans-serif";
console.log("item", this.item);
canvasUtils.fillRoundRect(ctx, 0, 0, this.posterObj.width, this.posterObj.height, 5, "#db5e62")
canvasUtils.fillRoundRect(ctx, 15, 15, this.posterObj.width - 30, this.posterObj.height - 140, 5, "#FFFFFF")
ctx.setFillStyle('#db5e62')
ctx.font = "24px" + family
ctx.fillText(this.item.categoryName, 30, 60)
let avatarUrl = await canvasUtils.getImageInfo(this.$store.getters.userInfo.avatarUrl)
canvasUtils.drawCircular(ctx, avatarUrl.path, this.posterObj.width - 100, 30, 60, 60)
ctx.setFillStyle('#000000')
var options = {
font: "15px" + family, //设置字体
ctx: ctx,
word: this.$store.getters.userInfo.nickName, //文本
maxWidth: 100, //最大宽度
maxLine: 1, //最大行数--超出省略
x: this.posterObj.width - 120, //x坐标
y: 100, //y坐标
l_h: 10 //行高
}
canvasUtils.dealWords(options)
ctx.setFillStyle('#4c4c4c')
ctx.font = "12px" + family
ctx.fillText(this.item.createTime, 30, 90)
ctx.setFillStyle('#ccb45a')
ctx.font = "16px" + family
ctx.fillText("标题:", 30, 130)
ctx.fillText("内容:", 30, 160)
ctx.setFillStyle('#000000')
options = {
font: "16px" + family, //设置字体
ctx: ctx,
word: this.item.postName, //文本
maxWidth: 200, //最大宽度
maxLine: 1, //最大行数--超出省略
x: 80, //x坐标
y: 120, //y坐标
l_h: 10 //行高
}
canvasUtils.dealWords(options)
options = {
font: "17px" + family, //设置字体
ctx: ctx,
word: this.item.content, //文本
maxWidth: 200, //最大宽度
maxLine: 6, //最大行数--超出省略
x: 30, //x坐标
y: 180, //y坐标
l_h: 10 //行高
}
canvasUtils.dealWords(options)
if (this.item.imgUrls && this.item.imgUrls.length > 0) {
let img = await canvasUtils.getImageInfo(this.item.imgUrls[0])
canvasUtils.drawImgCover(ctx, img, 30, this.posterObj.height - 230, 100, 100)
}
let logo = await canvasUtils.getImageInfo(
"xxxx.png")
canvasUtils.drawImgCover(ctx, logo, 20, this.posterObj.height - 120, 80, 80)
let qrcode = await canvasUtils.getImageInfo(this.posterObj.qcCode)
canvasUtils.drawImgCover(ctx, qrcode, this.posterObj.width - 100, this.posterObj.height - 120, 80, 80)
ctx.setFillStyle('#ffffff')
ctx.font = "14px" + family
ctx.fillText('xxx ', 30, this.posterObj.height - 20)
ctx.fillText('长按查看详情 ', this.posterObj.width - 100, this.posterObj.height - 20)
ctx.draw()
},
getQrCode() {
let path = this.path
createWxMaCode({
page:path
}).then(res=>{
this.posterObj.qcCode = res
console.log(res);
this.createPosterObj()
})
},
doCreateQr() {
if (!this.$store.getters.userInfo) {
uni.$u.toast('请先登录')
return
}
this.posterObj.show = true
if(this.posterObj.qcCode) {
this.createPosterObj()
} else {
this.getQrCode()
}
},
}