背景
前端一般都会遇到这个场景,页面使用酷炫的字体,有时候可能会使用图片,但是文字过多,或者想更加灵活的使用这些文字可能就需要引入一些字体了,但往往会有一个问题就是,一般中文字体的大小都是10M左右,这让前端很头痛(shit, 这引用个毛毛),当然也有一些优秀的解决方案,比如fontmin, font-spider,之类的,该工具就是以fontmin为插件完成的一个可视化工具。
前端部分
前端我是使用vue-cli3搭建的项目,但是其实主要的功能其实已经在后端部分完成,前端只是多以展示为主,主要功能分为几个
- 字体转化
- 上传新字体
- 下载源字体
- 下载转化字体 功能上,主要为后端完成,包含转化,上传处理,下载
字体转化
这个是最主要的部分,使用接口文字部分传给后端,让后端处理文字后,生成文字地址返回给前端
上传新字体
前端将文件传给后端,后端返回链接
下载
分两个,一个是源字体下载,即上传后生成的链接下载,一个是转化字体下载,其实就是将生成的字体的链接返回给前端,提供下载
这里考虑到上传和下载源字体的风险比较大,所以做了权限处理,使用的jsonwebtoken,校验使用者是否合法,即我们的常见的登录,将返回的token带入上传和删除的接口中,判断是否有权限删除
注意点
前端处理展示问题的时候有个主意事项,即转化文字后的展示部分因为需要动态显示不同字体,所以需要动态替换当前的font-face,所以生成的文字需要是有哈希值的避免引用的时候有缓存,因为生成的字体替换到css需要下载下来所以我们还需要发起一个请求,下载字体,将字体转化为base64,并将解析格式application/octet-stream转化为font/ttf,否则浏览器不能解析当前的字体文件
// 将文件转化为base64
blobToDataURI (blob, callback) {
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = function (e) {
callback(e.target.result);
}
}
// 获取字体并动态转化
axios({
url: src,
method: 'GET',
responseType: 'blob'
}).then(res => {
this.blobToDataURI(res.data, (e) => {
let a = e.replace('application/octet-stream', 'font/ttf')
this.addCSS(`@font-face {
font-family: 'webFont';
src: url('${a}') format('truetype');
}`)
})
后端部分
后端服务是使用koa搭建的,因为比较简单,主要使用的插件是fontmin(字体转化的核心),koa-body(上传),jsonwebtoken(auth校验),compressing(压缩文件),功能罗列出来后其实就是一一对应实现就行,而大部分的实现其实都有先例或者一般都是插件的使用,所以即便遇到问题也很好解决
- 上传
- 压缩
- 转化
上传
const koaBody = require('koa-body')
const SIZE = 10 // 10M
const uploadConfig = () => {
return koaBody({
multipart: true, // 支持文件上传
// encoding: 'gzip',
formidable: {
// uploadDir: UPLOADPATH, // 设置文件上传目录
keepExtensions: true, // 保持文件的后缀
maxFieldsSize: SIZE * 1024 * 1024 // 文件上传大小
}
})
}
const uploadFn = ()=> {
....
let uploadUrl = path.join(__dirname, './../../fonts', name)
const reader = fs.createReadStream(ctx.request.files.file.path);
// 创建可写流
const upStream = fs.createWriteStream(uploadUrl);
// 可读流通过管道写入可写流
reader.pipe(upStream);
....
}
压缩
// 获取下载处理后的ttf压缩文件地址
const downLoad = () => {
router.post(`${proName}/download`, async (ctx, next) => {
let downloadUrl = path.resolve(__dirname, `./../example.zip`)
let query = ctx.body
let durl = `./../font`
if (query.hash) {
durl = `./../font/${query.hash}`
}
await compressing.zip.compressDir(path.resolve(__dirname, durl), path.resolve(__dirname, '../example.zip'))
.then(() => {
ctx.body = {
res: 'success',
data: downloadUrl
}
})
.catch(err => {
console.error(err);
});
await next()
})
}
字体转化
const dealFont = (query) => {
const fontMinObj = new Fontmin()
let ttfPath = path.resolve(__dirname, `./../../fonts/${query.type}.ttf`) // 获取字体
let random = randomNum() // 生成随机字符串当做hash值
let renameFont = 'font.ttf' // 重命名字体
let ppath = `../font`
if (query.hash) { // 部署到远程服务器需要开启哈希值 否则获取字体会有缓存
renameFont = `font.${random}.ttf`
ppath = `../font/${random}`
}
const fontmin = fontMinObj.src(ttfPath)
.use(Fontmin.glyph({ // 转化的核心
text: query.text,
hinting: false // keep ttf hint info (fpgm, prep, cvt). default = true
}))
.use(rename(renameFont)) // 重命名
.dest(path.resolve(__dirname, ppath)) // 生成到xx目录
.use(Fontmin.ttf2eot()) // 转化为eot
.use(Fontmin.ttf2woff()) // 转化为woff
fontmin.run(function (err, files) {
if (err) {
throw err;
}
console.log('\n', files[0]);
// => { contents: <Buffer 00 01 00 ...> }
});
return new Promise((resolve, reject) => {
resolve(random)
})
}
以上就是核心的部分,第一次做工具类的东西,如有差错或建议还请指教或留言,万分感谢
更多详情请看这里github