最近在做一个项目,是在mobile网页上生成海报,海报中有用户圆形头像、图标、图片、文字等信息,相对来说也比较复杂的一个海报。
问题:
问题描述:在用canvas画圆形头像时,向画布右侧移动时,右边半个圆被切割了。
原因:给头像的画布位置有限,移出了画布的区域自然就被切割了,一开始用的是fill()去填充,总是会被切割。
改之前的代码:
img.onload = function () {
let width = 1.6 * textPx;
let height = 1.6 * textPx;
let min = Math.min(width, height)
let circle = {
x: Math.floor(min / 2),
y: Math.floor(min / 2),
r: Math.floor(min / 2)
}
ctx.fillStyle = ctx.createPattern(img, 'no-repeat')
ctx.beginPath()
//开始路径画圆,剪切处理
console.log("r:=====", circle.r)
ctx.moveTo(0, 0)
ctx.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2);
//恢复状态
ctx.fill();//用这段代码去画圆形头像总是被切割
}
改之后的代码:
img.onload = function () {
console.log("timeout doing 小主播头像")
ctx.save()
let width = 118;
let height = 118;
let min = Math.min(width, height)
let circle = {
x: Math.floor(min / 2),
y: Math.floor(min / 2),
r: Math.floor(min / 2)
}
ctx.fillStyle = ctx.createPattern(img, 'no-repeat')
ctx.beginPath()
//开始路径画圆,剪切处理
ctx.arc(circle.x + 129, circle.y + 87, circle.r, 0, Math.PI * 2, false);
ctx.clip()
ctx.drawImage(img, 129, 87, 2 * circle.r, 2 * circle.r)//改成这种方式可以画圆形头像,且不被切割
console.log("dataURL00000:===", canvas.toDataURL('image/jpg'))
canvas2imgWrap.style.display = 'block'
_this.dataImgSrc = canvas.toDataURL('image/png')
if (clientWidth > 375) {
dataImgWrap.style.marginLeft = 40 + 'px'
}
dataImgWrap.setAttribute('width', canvasWidth + 'px');
dataImgWrap.setAttribute('height', canvasHeight + 'px');
canvas2imgWrap.setAttribute('width', '100%');
canvas2imgWrap.setAttribute('height', '100%');
canvas.style.display = 'none'
}
问题描述:因为图片加载需要时间,文字所需的时间比图片短,导致文字先画在了画布上,背景图后画,这就导致了背景图盖住了文字和其它的图片,导致只能看到部分绘画的图片。
解决方法是写了一个回调,在背景图画完之后再画其它的内容。因为其它内容需要覆盖在背景图片上。
代码如下:
createCanvas() {
let _this = this;
let canvas = document.getElementById("canvas_wrap")
let dataImgWrap = document.getElementById("dataImgWrap")
let canvas2imgWrap = document.getElementById("canvas2img")
canvas.style.display = "block"
let ctx = canvas.getContext("2d");
let clientWidth = document.documentElement.clientWidth;
let textPx = clientWidth * 180 / 750
//根据设计图中的canvas画布的占比进行设置
let canvasWidth = 375;
let canvasHeight = 636
console.log("canvasWidth:", canvasWidth, 'canvasHeight:===', canvasHeight)
canvas.setAttribute('width', canvasWidth + 'px');
canvas.setAttribute('height', canvasHeight + 'px');
/* ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvasWidth, canvasHeight); */
//canvas背景图
function drawBg(callback) {
let bgImg = new Image();
bgImg.onload = function () {
// resolve(bgImg)
let bg = ctx.createPattern(bgImg, 'no-repeat');
ctx.fillStyle = bg;
ctx.fillRect(0, 0, canvasWidth, canvasHeight)
console.log("timeout forward")
}
bgImg.src = canvasBg;
setTimeout(function () {
(callback && typeof (callback) === "function") && callback();
}, 1500)
console.log("timeout after")
}
drawBg(function () {
console.log("timeout doing")
let img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
// img.src = _this.detailData.anchorHeadImg + '?' + (+new Date())
img.src = localAvatar; //为什么用本地图片后canvas背景图不显示???
img.onload = function () {
console.log("timeout doing 小主播头像")
ctx.save()
let width = 118;
let height = 118;
let min = Math.min(width, height)
let circle = {
x: Math.floor(min / 2),
y: Math.floor(min / 2),
r: Math.floor(min / 2)
}
ctx.fillStyle = ctx.createPattern(img, 'no-repeat')
ctx.beginPath()
//开始路径画圆,剪切处理
console.log("r:=====", circle.r)
ctx.arc(circle.x + 129, circle.y + 87, circle.r, 0, Math.PI * 2, false);
ctx.clip()
ctx.drawImage(img, 129, 87, 2 * circle.r, 2 * circle.r)
/* ctx.fillStyle = "#fff"
ctx.arc(circle.x + 110, circle.y + 90, circle.r + 10, 0, Math.PI * 2, false); */
console.log("dataURL00000:===", canvas.toDataURL('image/jpg'))
canvas2imgWrap.style.display = 'block'
_this.dataImgSrc = canvas.toDataURL('image/png')
if (clientWidth > 375) {
dataImgWrap.style.marginLeft = 40 + 'px'
// canvas2imgWrap.style.marginLeft = 7 + 'px'
}
dataImgWrap.setAttribute('width', canvasWidth + 'px');
dataImgWrap.setAttribute('height', canvasHeight + 'px');
canvas2imgWrap.setAttribute('width', '100%');
canvas2imgWrap.setAttribute('height', '100%');
canvas.style.display = 'none'
}
//作品数
ctx.font = "bold 16px microsoft";
ctx.fillStyle = "#333";
ctx.fillText(_this.detailData.workCount + "个", 298, 40)
ctx.font = "16px microsoft";
ctx.fillStyle = "#666";
ctx.fillText("作品数", 298, 65)
ctx.font = "bold 16px microsoft";
ctx.fillStyle = "#333";
ctx.fillText(_this.detailData.workReadAmountCount, 298, 90)
ctx.font = "16px microsoft";
ctx.fillStyle = "#666";
ctx.fillText("人气", 298, 115)
// ctx.font = 0.4 * textPx;
ctx.font = "bold 20px microsoft";
ctx.fillStyle = "#333";
ctx.fillText("小少年", 157, 250)
//认证小主播
let authImg = new Image();
authImg.src = _this.detailData.anchorIsAuthed === 1 ? posterAuth : '';
authImg.onload = function () {
ctx.drawImage(authImg, 137, 260, 100, 24)
console.log("timeout doing 认证小主播")
}
//小主播简介
ctx.font = "12px microsoft"
ctx.fillStyle = "#fff";
// ctx.fillText(_this.detailData.anchorItdc, 165, 100)
let t = "这里是小主播简介,这里是小主播。简介这里是小主播简介这里是,真的是这样的吗?哈哈哈哈!!小主播简介这里是小主播简介这里是小主播简介这里是小主播简介这里是小主播简介这里是小主播简介这里是小主播简介这里是小主播简介"
// let t = _this.detailData.anchorItdc
_this.drawText(ctx, t, 30, 330, 330)
//二维码
// console.log("url:=======", this.$nuxt.$route.path)
let erweimaImg = new Image()
erweimaImg.setAttribute('crossOrigin', 'anonymous');
let erweima_URL = config.erweima_url + _this.$nuxt.$route.path
erweimaImg.onload = function () {
let erweimaPhoto = document.getElementById("canvasImg")
ctx.drawImage(erweimaPhoto, 37, 505, 68, 68);
// console.log("dataURL11111:===", canvas.toDataURL('image/png'))
}
console.log("erweimaurl:=======", erweima_URL)
erweimaImg.src = erweima_URL + '?' + (+new Date())
//小主播养成计划图片
let titleImg = new Image();
titleImg.src = posterTitle;
titleImg.onload = function () {
ctx.drawImage(titleImg, 125, 517, 170, 17)
console.log("timeout doing 养成计划")
}
ctx.font = "14px microsoft";
ctx.fillStyle = "#333";
ctx.fillText("长按识别二维码", 125, 550)
//中少网图片
let logoImg = new Image();
logoImg.src = posterLogo;
logoImg.onload = function () {
ctx.drawImage(logoImg, 97, 610, 180, 21)
console.log("timeout doing 中少网logo")
}
})
},
drawText(context, t, x, y, w) {
var chr = t.split("");
var temp = "";
var row = [];
context.font = "14px microsoft";
context.fillStyle = "#fff";
context.textBaseline = "middle";
for (var a = 0; a < chr.length; a++) {
if (context.measureText(temp).width < w && context.measureText(temp + (chr[a])).width <= w) {
temp += chr[a];
}//context.measureText(text).width 测量文本text的宽度
else {
row.push(temp);
temp = chr[a];
}
}
row.push(temp);
/* for (var b = 0; b < row.length; b++) {
context.fillText(row[b], x, y + (b + 1) * 24);//字体20,间隔24。类似行高
} */
// 只显示2行,加...
for (var b = 0; b < 4; b++) {
var str = row[b];
if (b == 3) {
str = str.substring(0, str.length - 1) + '...';
}
context.fillText(str, x, y + (b + 1) * 24);
}
},
之所以没用promise,是因为本人的promise不是特别熟,看来必须把promise给学精了,加油!!!!
最终效果:
参考链接:
https://blog.csdn.net/u012011360/article/details/80169529
https://blog.csdn.net/fxss5201/article/details/79691923
https://www.cnblogs.com/padding1015/p/9717845.html
https://www.w3school.com.cn/tags/html_ref_canvas.asp