当前环境下,大家都非常需要分享到朋友圈这个功能,但是实现起来各有心酸(坑比较多),所以才有了如下的 canvas 绘图工具
npm地址: https://www.npmjs.com/package/vue-poster
具有如下特性:
- 简单易用 —— 一个 json 搞定绘制图片
- 功能全 —— 满足 90% 的使用场景
- 支持圆形图片绘制(eg: 微信头像圆形)[round: true]
- 绘制文本(换行、超出内容省略号、中划线、下划线、文本加粗)
- 绘制图片
- 绘制矩形
- 保存图片
- 多图绘制 (支持回调)
- 导出图片格式 jpg/png
... - 代码量小
/**
* @description: 生成分享海报
* @json {Array}
* @return: String
*/
function canvasPosterInit (json, callback) {
// 创建canvas对象
var canvas = document.createElement('canvas')
var ctx = canvas.getContext('2d')
// 初始化canvas的宽高度
canvas.width = json.width
canvas.height = json.height
canvasRect({ backgroundColor: '#fff', left: 0, top: 0, width: json.width, height: json.height }, ctx)
canvasAll(json.views, 0, ctx, function () {
canvasTranImg(canvas, json.quality, function (res) {
callback(res)
})
})
}
/**
* @description: 画图递归回调,抛出最后一次回调
* @param {type}
* @return:
*/
function canvasAll (views, index, ctx, callback) {
if (!views[index]) {
callback()
return false
}
switch (views[index].type) {
case 'image':
canvasImage(views[index], ctx, function () {
canvasAll(views, ++index, ctx, callback)
})
break
case 'text':
canvasText(views[index], ctx, function () {
canvasAll(views, ++index, ctx, callback)
})
break
case 'rect':
canvasRect(views[index], ctx, function () {
canvasAll(views, ++index, ctx, callback)
})
break
}
}
/**
* @description: 画方形 eg: 填充背景
* @param {type}
* @return:
*/
function canvasRect (item, ctx, callback) {
ctx.fillStyle = item.backgroundColor || '#f0f'
ctx.fillRect(item.left, item.top, item.width, item.height);
(typeof callback === 'function') && callback()
}
/**
* @description: 将图片放在canvas对应位置
* @item {Object}
* @canvas {DOM}
* @return: null
*/
function canvasImage (item, ctx, callback) {
ctx.save()
if (item.round) {
var cx = item.left + item.width / 2
var cy = item.top + item.width / 2
ctx.arc(cx, cy, item.width / 2, 0, 2 * Math.PI)
ctx.clip()
}
var img = new Image()
img.src = item.src
img.setAttribute('crossOrigin', 'Anonymous')
img.onload = function () {
ctx.drawImage(img, item.left, item.top, item.width, item.height)
if (item.round) {
ctx.restore()
}
(typeof callback === 'function') && callback()
}
}
/**
* @description: 将文字放在canvas对应位置
* @item {Object}
* @canvas {DOM}
* @return: null
*/
function canvasText (item, ctx, callback) {
ctx.fillStyle = item.color
var fontStyle = (item.fontSize || 12) + 'px ' + (item.fontFamily || 'Microsoft YaHei')
if (item.fontBold) {
fontStyle = 'bold ' + fontStyle
}
ctx.font = fontStyle
ctx.textAlign = 'left'
ctx.textBaseline = 'top'
var txtArr = returnCanvasTextArr(item, ctx)
var maxLineNumber = item.maxLineNumber || 2
if (txtArr.length > maxLineNumber) {
var _len = txtArr[maxLineNumber - 1].length
txtArr[maxLineNumber - 1] = txtArr[maxLineNumber - 1].slice(0, _len - 1) + '...'
}
txtArr.every((conTxt, index) => {
if (index >= maxLineNumber) {
return false
}
ctx.fillText(conTxt, item.left, item.top + index * item.lineHeight)
return true
});
(typeof callback === 'function') && callback()
}
/**
* @description: 返回字符串需要的行
* @item {Object}
* @return: Array
*/
function returnCanvasTextArr (item, ctx) {
var arr = []
var lastIndex = 0
if (ctx.measureText(item.content).width <= item.width) {
arr.push(item.content)
} else {
for (var index = 1; index < item.content.length; index++) {
if (ctx.measureText(item.content.slice(lastIndex, index)).width - item.width >= 0) {
arr.push(item.content.slice(lastIndex, index))
lastIndex = index
}
}
arr.push(item.content.slice(lastIndex, item.content.length))
}
return arr.filter(function (s) {
return s && s.trim()
})
}
/**
* @description: 将canvas转换为图片
* @canvas {DOM} canvas对象
* @callback {function} 成功回调函数
* @return: base64
*/
function canvasTranImg (canvas, json, callback) {
json.format === 'png' ? callback(canvas.toDataURL('image/png')) : callback(canvas.toDataURL('image/jpeg', json.quality || 1.0))
}
调用形式
canvasPosterInit({
width: '607',
height: '700',
quality: 1,
views: [{
type: 'image',
width: '607',
height: '400',
left: 0,
top: 0,
src: 'https://vipadmin.chuanghehui.com/upload/pic/20200214/65fd6ef7b753304aab33359006693da3.jpg'
},
{
type: 'text',
content: '【我的天啊我的天啊我我的天啊我的天啊我的天啊我的天啊我的天啊我的天啊我的天天天天天天】',
width: 580,
fontSize: 20,
fontFamily: 'Arial',
color: '#000',
left: 0,
top: 450,
maxLineNumber: 1,
lineHeight: 30
}]
}, function (img) {
var image = new Image()
image.src = img
image.onload = function () {
document.body.appendChild(image)
}
})