使用SVG Sprites(雪碧图)实现本站表情的解决方案
起初,发现自己用的酷安表情包,当时为了极致占用(为了降低网络开销),已经被压缩的非常糊。然后,就想到是否能用SVG来解决这个问题,于是,找到位大佬在Github仓库里提供的SVG格式的酷安表情包,故经过筛选之后取之,基本只留下了圆脸和狗头表情,其他的都略去了。最终筛选出这80张表情。
正当因如何再次缩减表情数量(减少HTTP请求次数)而犯愁的我,经过搜索查找,找到了SVG Sprites(雪碧图)的方案,可以把数个SVG给合并成一个文件,然后通过use
来调用被合并成一个.svg
文件中所对应的symbol
,这样只需要控制合并之后的SVG文件大小就可以了,而且还可以做到随用随加载,且HTTP请求只有一次。这种方案非常满足我的需要,那么就....开搞!
如何合并
找个能做雪碧图的工具(我这次用的是SVG Sprites Generator),把需要的图全部上传之后,会因图片的数量以及大小等因素,需要等它生成一会儿。生成完毕后,把左栏的symbol
区全都复制下来,粘贴到个空文件里面,另存为.svg
(比如说在Windows中,新建个txt文档,然后把左列生成的SVG文本粘贴进去,保存,然后再改后缀名为.svg
)。右栏的use
井号后面的文本,就是原先每个SVG文件的文件名。这样雪碧图就已经是做完了。
如果你的表情文件有"haha.svg"、"huaji.svg"、"doge.svg"的话,后续使用use
调用合并出的雪碧图,xlink:href
的值就是"路径/雪碧图文件名.svg#haha"、"路径/雪碧图文件名.svg#huaji"、"路径/雪碧图文件名.svg#doge"。故在合成雪碧图之前,一定要改文件名为自己喜欢的!(合成雪碧图之前的文件名可以使用中文。)
用于Typecho
之前写过一篇关于本站的表情解析的解决方案,大体上还是用以前的代码,不过之前的代码,前端用的是jQuery实现的,现在把用原生JavaScript的代码和修改好的functions.php
文件的函数部分也重新在下面给一下,方便大家效仿。
// 主题的 functions.php 文件
function parseCommentText($obj){
$pattern = '/@\[(.*?)\]/is';
$obj = preg_replace_callback($pattern, 'forEmotionReplace', $obj);
return trim($obj);
}
function forEmotionReplace($matches){
return '<svg class="emoji-set"><use xlink:href="你的SVG文件路径.svg#'.htmlspecialchars($matches[1]).'"></use></svg>';
}
注:parseCommentText的调用方法:在comments.php
或者是自己的主题的自定义评论结构中的$comments->content
,改为parseCommentText($comments->content);
即可完成调用。
再注:上述代码中$matches
到的文本,即为合成雪碧图之前的每个小表情文件的文件名。
前端部分的关键代码(具体表情框需要怎么实现,取决于自己主题):
const emotions = [
'表情名1', '表情名2'
];
// emotions 里填合成雪碧图之前,每个小表情的文件名,不要文件后缀,直接文件名即可
const owoList = document.getElementById('表情选择框');
emotions.forEach((emotion) => {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("class", "emoji");
svg.setAttribute("alt", `@防止被解析用时请删这里的中文[${emotion}]`);
svg.setAttribute("title", emotion);
const use = document.createElementNS("http://www.w3.org/2000/svg", "use");
use.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", `你的表情文件路径.svg#${emotion}`);
svg.appendChild(use);
owoList.appendChild(svg);
svg.addEventListener('click', () => {
insertAtCaret(document.querySelector('此处需要指定你评论框的textarea'), svg.getAttribute("alt"));
});
});
insertAtCaret方法:
const insertAtCaret = (textarea, text) => {
const cursorPos = textarea.selectionStart;
const front = textarea.value.substring(0, cursorPos);
const end = textarea.value.substring(cursorPos);
textarea.value = front + text + end;
textarea.selectionStart = cursorPos + text.length;
textarea.selectionEnd = textarea.selectionStart;
textarea.focus();
}
剩下的,就是调整CSS,调整一下如何实现表情框,就基本可以了。
另附:简单的伸缩表情栏的DEMO:点我到CodePen查看此DEMO
<button onclick="btnOnclick();">点我出表情栏</button>
<div id="emojiContainer" style="display: none;">省略插入表情的逻辑</div>
<script>
const btnOnclick = () => {
const emojiContainer = document.getElementById('emojiContainer');
if (emojiContainer.style.display === 'none') {
emojiContainer.style.display = 'block';
} else {
emojiContainer.style.display = 'none';
}
};
</script>
后注:一定要自己通过CSS指定表情的宽高,否则表情图有很大概率会因为尺寸过大而被顶出去,造成一种看不到表情的效果(向SVG标签上的class上追加样式即可)。.emoji
是表情选择框的样式,.emoji-set
是评论列表的渲染样式,之所以把这两个分开,是为了更方便写样式,比如说让表情选择框里的表情更大一些,或者是有一些点击样式什么的。下面给出两个合在一起写的基础CSS,具体如下:
.emoji-set,
.emoji {
height: 20px;
width: 20px;
vertical-align: text-bottom;
}
不错哦很高清,看了下体积好像700k,好像还是纯图片体积小点
所以说这种方案还是很不错的
不对...这700kb里面含80张图,平均下来每张图不到10kb
试试应该很不错,合并一个svg,很省。。可以单独出插件了