最近在做一個項目,是在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