需求:用户在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";
复制代码