需求:用戶在H5裏面答題,而後根據得分狀況生成一個獎狀,獎狀上會顯示用戶的我的信息(微信暱稱、答題時間),獎狀能夠保存,實際效果以下圖所示。web
一、把獎狀背景圖繪製到canvas上,會出現圖片和文字模糊問題;canvas
二、用戶微信暱稱繪製到獎狀上的紅線位置,須要在紅線區域居中,可是用戶暱稱千奇百怪,較難處理;瀏覽器
三、獎狀上的繪製的文字字體大小在移動端須要自適應。bash
一、問題1解決方案:微信
(1)canvas中存在兩種尺寸:字體
canvas 的 width/height 是畫布的寬/高,用來肯定這個畫布的內容大小,相似於 img 標籤中的圖片的寬高屬性;
canvas 的 樣式表裏的 width/height 表明的是最終繪製到瀏覽器上的尺寸,是 canvas 的物理寬度,
相似於 img 標籤中 樣式表中設置的寬高屬性。
複製代碼
(2)圖片和文字模糊緣由flex
canvas 中屬性的寬高並不表明實際的寬度,因此在高分辨率設備下(高清屏),瀏覽器就會放大 canvas,致使模糊;
canvas 的最小單位是像素,一般狀況下,設備的一個像素能夠繪製一個點,
可是當 window.ddevicePixelRatio > 1 |的時候,設備就會用大於1的像素個數繪製 canvas
上的一個點,這時候若是還按照原始的畫布大小去繪製圖片或者文字時,看到的線條就比較粗,出現模糊的現象。
複製代碼
(3)根據 devicePixelRatio 的值,放大 canvas 的內容大小,去掉模糊效果ui
const canvasBg = new Image(); //省略獎狀圖片地址
let canvas = document.getElementById("myCanvas");
canvasBg.onload = () => {
let ctx = canvas.getContext("2d");
let getPixelRatio = function (context) {
let backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
}
let ratio = getPixelRatio(ctx); //獲取設備物理像素與設備獨立像素的比例
let imgW = canvasBg.width;
var imgH = canvasBg.height;
canvas.width = $(window).width() * ratio; //放大畫布內容的寬度
canvas.height = imgH * $(window).width() / imgW * ratio; //放大畫布內容的高度
let cW = $('#myCanvas').width();
let cH = $('#myCanvas').height();
canvas.style.width = "100%";
ctx.drawImage(canvasBg, 0, 0, cW, imgH * cW / imgW); //把獎狀模板繪製到canvas上,自適應獎狀背景圖,不拉伸或壓縮圖片
複製代碼
二、問題2解決方案:編碼
(1)解決思路:spa
先計算獎狀上紅線的寬度,而後在計算出用戶暱稱的寬度,對於超過紅線寬度的用戶暱稱作特殊處理,
截斷超過紅線的字符串,用省略號替代
複製代碼
//截取字符串方法,參數1爲傳入的用戶暱稱,參數2爲要截取的字符串長度
function cutstr(str, len) {
var str_length = 0,
str_cut = new String(),
str_len = str.length;
var charAtStr;
for (var i = 0; i < str_len; i++) {
//獲取str字符串指定位置的字符
charAtStr = str.charAt(i);
str_length++;
//中文字符的長度經編碼以後大於4,其餘字符小於4,所以中文字符佔兩位,其餘字符佔一位
if (escape(charAtStr).length > 4) {
str_length++;
}
str_cut = str_cut.concat(charAtStr);
if (str_length >= len) {
str_cut = str_cut.concat("..");
return str_cut;
}
}
//若是給定字符串小於指定長度,則返回源字符串;
if (str_length < len) {
return str;
}
}
}
let userInfo = {
userName: "xxx張小二xxx"
};
let paddingLeft = 0; //居中的字符串距離 canvas 左邊界的距離
//ctx.measureText(userInfo.userName).width 爲字符串的寬度
//146 * $('#myCanvas').width() / 727 * ratio 根據比例算出紅線的寬度
if (ctx.measureText(userInfo.userName).width > 146 * $('#myCanvas').width() / 727 * ratio) {
userInfo.userName = cutstr(userInfo.userName, 8);
}
//魔鬼數字爲固定尺寸獎狀上的某些元素的寬度,而後在根據比例求出
paddingLeft = 68 * $('#myCanvas').width() / 360 * ratio + (146 * $('#myCanvas').width() / 727 * ratio - ctx.measureText(userInfo.userName).width) / 2;
//繪製用戶暱稱到獎狀上
ctx.fillText(userInfo.userName, paddingLeft, 210 * $('#myCanvas').height() / 640 * ratio);
複製代碼
三、問題3解決方案:
(1)解決思路
利用rem設置字體大小,使用淘寶的 flexible 插件計算出移動設備根節點的font-size,
而後在某一固定分辨率的設備上計算出當前用戶暱稱對應的字體大小,而後根據比例求出其餘設備對應的字體大小
複製代碼
ctx.fillStyle = "red";
let fontSize = getComputedStyle(window.document.documentElement)['font-size'];
//0.41 爲在某個肯定分辨率下調試出的字體rem值
ctx.font = fontSize.replace(/px/, "") * 0.41 * ratio + "px Arial";
複製代碼