FontCut ✂ 字体裁剪Chrome插件

FontCut.jpg

基于opentype.js 实现了一个字体裁剪Chrome插件,能够预览字体并导出otf和woff格式的字体子集。

目前font-carrier,fontkit,fontmin等等字体裁剪方案只能借助服务端能力,着实不便。其实这些项目大多也是基于opentype.js的。
opentype.js 能够解析 ttf otf woff 三种文件格式解析为一个 font 类,本身提供了在浏览器运行的能力。

捕获.PNG

字体科普

ttf 与 otf

OpenType字体的Outline描述方法主要有TrueType和Postscript,前者的字库文件后缀名一般为ttf,后者的后缀名一般为otf。
OTF通过提供许多TTF无法提供的功能来扩展TTF。例如,OTF的格式允许最多存储65,000个字符。OTF和TTF之间的主要区别是高级排版功能。OTF具有诸如连字和替代字符(也称为字形)之类的装饰。
ligatures-640x72.png

WOFF

摘录维基百科中的介绍:

Web开放字体格式(Web Open Font Format,简称WOFF)是一种网页所采用的字体格式标准。此字体格式发展于2009年,由万维网联盟的Web字体工作小组标准化,现在已经是推荐标准。此字体格式不但能够有效利用压缩来减少文件大小,并且不包含加密也不受DRM(数字著作权管理)限制。

WOFF 1.0使用zlib压缩,文件大小一般比TTF小40%。而WOFF 2.0使用Brotli压缩,文件大小比上一版小30%。

CSS @font-face

字体格式可以选择以下几种:"woff", "woff2", "truetype", "opentype", "embedded-opentype", and "svg"。(IOS 不支持 woff2)

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome.woff2') format('woff2'),
       url('/fonts/awesome.woff') format('woff'),
       url('/fonts/awesome.ttf') format('truetype'),
       url('/fonts/awesome.eot') format('embedded-opentype');
}

加载顺序

字体变体的指定顺序很重要。 浏览器将选取其支持的第一种格式。所以如果需要兼容的浏览器使用 WOFF2,则应将 WOFF2 声明置于 WOFF 之上,依此类推。

字体的加载

字体延迟加载带有一个可能会延迟文本渲染的重要隐藏影响:浏览器必须构建渲染树(它依赖 DOM 和 CSSOM 树),然后才能知道需要使用哪些字体资源来渲染文本。 因此,字体请求的处理将远远滞后于其他关键资源请求的处理,并且在提取资源之前,可能会阻止浏览器渲染文本。

font-crp.png

如果我们知道页面用了那些文字,我们还可以利用新功能:<link rel="preload">。该功可以在关键渲染路径中提早触发对网络字体的请求,而不必等待创建 CSSOM。

<head>
  <!-- Other tags... -->
  <link rel="preload" href="/fonts/awesome-l.woff2" as="font">
</head>

项目地址

GitHub 地址

后续开发计划

使用otfcc或其他C语言字体解析库编译成 webassembly 加快解析速度。

参考文档:

一种使用倒排索引和位图的快速SKU选择方法

使用所有有货的SKU组合生成该SKU到该组合的索引。如 [A-B,B-C] 即生成 {A:[1],B:[1,2],C:[2]}
映射到视图时只需要判断当前选中项的SKU索引的交集(默认为全集),例如选中B,则[1,2,3][1,2]的交集[1,2],然后依次遍历SKU,判断SKU对应的索引与当前选中索引是否有交集。如果有则可选,没有则置灰。

索引可以使用位图数据结构,在判断是否为交集使用位运算提升性能。

   const sku = [];
    const revertIndex = {};
    const size = 8;
    // 随机sku
    for (let i = 0; i < size; i++) {
        sku[i] = new Set();
        for (let j = 0; j < size; j++) {
            const v = j * size + RandomNumBoth(0, size);
            sku[i].add(v)
        }
    }

    for (let i = 0; i < sku.length; i++) {
        sku[i].forEach((e) => {
            if (!revertIndex[e]) revertIndex[e] = 0;
            revertIndex[e] |= 1 << i
        })
    }

判断是否选中使用 (revertIndex[(line-1)*size + e -1] & union)

下面是一个简单的例子,取消选择和选中逻辑是一样的,所以就暂时没写:

See the Pen SKU Demo by zhangchen (@zhangchen915) on CodePen.

Preact isomophic style 实现 CSS 同构

logo.svg

实现SSR不仅需要输出HTML字符串,还要包括关键渲染路径的CSS插入到HTML中,如果样式需要浏览器加载完 CSS 后才会加上,这个样式添加的过程就会造成页面的闪动,SSR也就没有意义了。

在服务端我们不能使用style-loader,因为它只能在浏览器环境将CSS插入到页面中,我们采用isomophic style load在服务器端输出 html 字符串的同时,也将页面用到的样式抠出来,然后插入到 html 字符串当中,将结果一同传送到客户端。

下面是webpack中用到了loader部分。

    var insertCss = require(${stringifyRequest(this, `!${insertCss}`)});
    var content = typeof css === 'string' ? [[module.id, css, '']] : css;
    
    exports = module.exports = css.locals || {};
    exports._insertCss = function() { return insertCss(css) };
    exports._toString = css.toString;
    exports._module_id = id

我们实现同构 css 函数 insertCss,这里只展示服务端部分,函数接收style参数,这是css-loader生成的style对象,包括

  const [module, css, media, sourceMap] = style[0]

  // Generate Id based on length of css , because css module give different id between browser and node
  const dev = process.env.NODE_ENV === 'development'
  const id = dev ? module : style.locals._module_id

  // Server Side
  if (typeof window === 'undefined' || typeof document === 'undefined') {
    return {id, css}
  }

最后我们在服务端实现一个insertCss函数收集用到的style,然后使用<StyleContext.Provider value={insertCss}>将插入该函数注入。

const isomorphicStyle = new Map();
const insertCss = (styles) => styles.forEach(style => {
    const {id,css} = style._insertCss();
    isomorphicStyle.set(id,css)
});

我们使用 withStyles 方法装饰组件,使用useContext拿到服务端中的insertCss函数,然后调用该函数将页面导入的style 对象传进去,该函数返回移除样式的方法removeCss ,我们在componentWillUnmount 是移除CSS。

constructor(props) {
      super(props)
      const insertCss = useContext(StyleContext)
      this.removeCss = insertCss(styles)
    }

    componentWillUnmount() {
      if (this.removeCss) setTimeout(this.removeCss, 0)
    }

项目地址

https://github.com/zhangchen915/preact-isomorphic-style-loader
安装 npm i preact-isomorphic-style-loader

iOS Safari 中 Fix 定位元素在滚动时闪烁的解决

如果你不得不在滚动时固定元素,则可能是iOS Safari(和其他移动设备)存在问题。元素通常会闪烁,然后消失,直到滚动完全停止为止。

只需通过添加transform: translate3d(0,0,0);来强制开启GPU加速。

如果要在固定元素中设置元素的样式,则需要将transform3d hack应用于嵌套元素,以使其不会闪烁/消失。

.Element-header {
  transform: translate3d(0,0,0);
}

.Element-header--fixed {
  top: 0;
  position: fixed;
}

原文地址: https://muffinman.io/ios-safari-scroll-position-fixed/