web界面上的字体兼容方案 进入全屏
line

   做前端的,对web界面基本都抠的很仔细,尤其精确到1px!

   类似边距、宽度、高度等的,调整1px并不难,但是如果遇到不同字体的情况,要处理line-height,保证每种字体下,UI效果都非常美观,这就不是一件简单的事情了!


   也许大家首先能想到的是,字体嘛,默认给页面body节点设置一个font-family列表即可:    

body {
    font-size: 12px;
    font-family:"Microsoft Yahei", "微软雅黑", Tahoma, Arial, Helvetica, STHeiti;
}

   但其实问题并没有这么简单,font-family列表是必然要设置的,但这个列表的具体解析,只有浏览器自己才知道,如果coder们不在这个基础上做点儿什么,是完全不知道某个用户浏览到的这个页面究竟应用到了那种字体、此时的页面排版是否美观、页面有没有出现文字很拥挤的情况、给某个节点设置的背景icon是否对其了?

   等等,各种问题,各种猜测。。。


   实际开发中,这也确实是一个问题,作为专业coder,是需要兼容各个细节的,包括这里的字体控制。


   任务

       利用Javascript编写一个组件,用于检测某用户浏览页面时,浏览器应用到了那种字体(以微软雅黑为例)。

   

   问题

       1、用户机器未安装雅黑字体时,需要用别的字体替代,并且要对其他样式进行修正

       2、用户机器安装了雅黑字体时:

           a、用户机器未开启ClearType时,雅黑字体显示出来会有锯齿,此时依然要将页面字体设置为默认,并同样对其他样式进行修正

           b、用户机器开启了ClearType时,按照正常的模式进行渲染,不需要对其他样式进行修正


   方法

       1、页面开始渲染时,检测用户机器上是否安装了雅黑字体

       2、检测用户机器是否开启了ClearType

       3、如果一切如愿,给html节点增加class="ms-with-yahei",否则增加class="ms-without-yahei"

       4、样式修正:对html.ms-without-yahei下的样式进行复写


   实现

       只要这一切都分析好了,要实现,就很简单了,核心的部分代码就是:

       1、检测用户机器是否安装了某种字体:

           a、IE浏览器中,通过创建<object classid="clsid:3050f819-98b5-11cf-bb82-00aa00bdce0b"></object>,直接访问系统的字体库,读取字体列表,判断某种字体是否存在            

if(qing.browser.ie){
    _dlgHelper = qing.dom.create('object',{
        id      : "sp-font-detect-obj",
        classid : "clsid:3050f819-98b5-11cf-bb82-00aa00bdce0b"
    });
    qing.dom.setStyles(_dlgHelper,{
        "position": "absolute",
        "top": "-10000px",
        "left": "-10000px",
        "width" : "1px",
        "height" : "1px"
    });
    document.body.appendChild(_dlgHelper);
    _isInitialedInIE = true;
}
//IE中,用object的classid来判断字体
if(qing.browser.ie) {
    if(!_isInitialedInIE) {
        init();
    }
    var sysFonts = _dlgHelper.fonts;
    if(sysFonts.count) {
        for(var i = 1,len = sysFonts.count;i <= len;i++){
            if(isInArray(sysFonts(i),familys)) {
                return callback(true);
            }
        }
    }
    return callback(false);
}

        b、非IE的浏览器中,创建一个span标签,再插入一段字符,设置很大的字号和默认字体(预计所有机器都有的“Times New Roman”),获取到span的offsetWidth;再给span追加一个待检测的字体,再获取其offsetWidth;两个width进行比较,如果相同,则表明用户机器没有这种字体,否则表明用户机器确实安装了这种字体!

/**
 * 检查字体宽度
 * @param {Object} family
 */
var checkOffsetWidth = function(family){
    var node = document.createElement("p");
    qing.dom.setStyles(node, {
        "font-family":  family + ", Times New Roman",
        "font-size": '300pt',
        "display": "inline",
        "position": "absolute",
        "top": "-10000px",
        "left": "-10000px"
    });
    qing.dom.addClass(node, "sp-font-detect");
    node.innerHTML = "mmmmmmmmml";
    document.body.appendChild(node);
 
    var width = node.offsetWidth;
    document.body.removeChild(node);
    return width;
};
/**
 * 获取文字实际宽度
 */
var getDefaultWidth = function(){
    if (!_defaultWidth) 
        _defaultWidth = checkOffsetWidth("Times New Roman");
    return _defaultWidth;
};
//非IE浏览器中,用比较宽度的方法来判断
else{
    var familyWidth = 0;
    var defaultWidth = getDefaultWidth();
    for(var j = 0,flen = familys.length;j < flen;j++){
        familyWidth = checkOffsetWidth(familys[j]);
        if(familyWidth !== defaultWidth){
            return callback(true);
        }
    }
    return callback(false);
}

   2、检测用户机器是否开启了ClearType

       a、IE下,直接通过screen.fontSmoothingEnabled获取

       b、非IE下,创建canvas,画一条粗线,然后获取并分析该DataURI数据

/**
 * 是否开启了clearType
 *@function isClearTypeOn
 *@return {Boolean} 如果支持,显示true;否则返回false
 */
var isClearTypeOn = function() {
    if(typeof screen.fontSmoothingEnabled!="undefined")
        return screen.fontSmoothingEnabled;
    else
        try {
            var f=document.createElement("canvas");
            f.width="35";
            f.height="35";
            f.style.display="none";
            document.body.appendChild(f);
            var o=f.getContext("2d");
            o.textBaseline="top";
            o.font="32px Arial";
            o.fillStyle="black";
            o.strokeStyle="black";
            o.fillText("E",0,0);
            for(var r=8;r<=32;r++)
                for(var u=1;u<=32;u++) {
                    var q=o.getImageData(u,r,1,1).data[3];
                    if(q != 255 && q != 0) {
                        document.body.removeChild(f);
                        return true
                    }
                }
            document.body.removeChild(f);
            return false
        } catch(y) {
            return null
        }
};

   3、在css中定义“其他字体情况下,样式的修复方案”

body, button, input, select, textarea , pre {
    font-size: 12px;
    font-family:"Microsoft Yahei", "微软雅黑", Tahoma, Arial, Helvetica, STHeiti;
    _font-family:Tahoma,Arial,Helvetica,STHeiti;
}
html.mod-without-msyahei body {
    font-family: Tahoma,Arial,Helvetica,STHeiti;
}
/****************系统默认支持雅黑的处理**********************/
.mod-without-msyahei .mod-blogitem .a-expand-reason{
    background-position-y:-15px;
}
.mod-without-msyahei .mod-blogitem .a-collapse-reason{
    background-position-y:3px;
}
.mod-without-msyahei .mod-blogitem .box-tag .q-tag{
    _padding:5px 3px 1px;
}
.mod-without-msyahei .mod-blogitem .item-head .q-private {
    line-height:23px;
    _padding-top:3px;
}

   4、在页面上,调用detect方法进行字体检测,并进行样式纠偏

/**
 * 字体监测
 * @return {[type]}
 */
var _fontDetect = function(){
    qext.FontDetect.detect(['微软雅黑','Microsoft Yahei'],function(isExist){
        //获得html节点
        var htmlElm = qing.dom.query('html')[0];
        //开启clearType并存在雅黑字体
        if(isExist){
            qing.dom.addClass(htmlElm,'mod-with-msyahei');
        }else{
            qing.dom.addClass(htmlElm,'mod-without-msyahei');
        }
    });
};


   这样,就能保证整个页面在不同的字体情况下,UI展现都尽量保持一致性!


   源码:web界面兼容的字体方案

阿里巴巴-钉钉-开放平台,能力开放&开发者运营岗位招聘中, 期待你的加入!
钉钉开放,让应用开发更简单
充分开放,是钉钉的重要方向!除致力于为开发者打造丰富的开放API, 更易接入的场景化能力包, 完备的应用开发工具之外, 还需要持续构建开放能力的布道、开发者生态运营体系,包括培训、沙龙、大会、社区合作等等。业务在快速发展,我们也还需要更多优秀的小伙伴加入!

评论区域

line
  • Alien 2014-02-23 22:57:00 回复
    回复 fsy0718 : 当然可以啊
    fsy0718 said:
    很强大,明天测试一下,然后用到实际项目中,可否
  • fsy0718 2014-02-23 22:55:33 回复
    很强大,明天测试一下,然后用到实际项目中,可否
  • Alien 2013-12-23 14:17:34 回复
    回复 barretlee : Times字体基本上每台机器上都有;除非用户手动给删掉了。用比较offset的方法,误差较小
    barretlee said:
    非IE下检测是否有某字体,不能确定该字体是否和 “Times New Roman” 的 offsetWidth 相同吧?
  • barretlee 2013-12-23 14:15:11 回复
    非IE下检测是否有某字体,不能确定该字体是否和 “Times New Roman” 的 offsetWidth 相同吧?
  • Alien 2012-10-08 08:32:34 回复
    回复 ray : 我是基于tangram进行开发的,其实就是用到了几个DOM操作的方法,可以将其改为jQuery,或者自己定义的方法。代码就这么几行,很简单的。
    ray said:
    请问这个方案是不是还需要调用某个JS库的?是YUI?
  • ray 2012-10-04 11:24:38 回复
    请问这个方案是不是还需要调用某个JS库的?是YUI?