H5页面&小程序如何实现emoji表情?

emoji表情都非常熟悉了,比如微信的会话窗口可以发表情。
但是仔细看有一个重要的发现,比如朋友给你发一个emoji表情,在聊天会话列表页查看最近消息,会发现有点不同,体现在如下:

  • 列表页看到的就是一个表情,比如大笑?的表情。
  • 列表页看到的是一个文字,比如[发呆],[懵逼]

前者就是默认emoji表情了,后者则是自定义表情。显然你拿着微信的[懵逼]在微博上是显示不出微信的[懵逼]表情的。
本文就讲述在H5和微信小程序下的这两种emoji表情的实现。

自定义emoji表情

自定义,顾名思义就是自己定义的表情,不是通用的表情,按照自己系统的一套解析规则来实现的,只在我的系统里面能识别的。

表情定义

首先你需要定义自己的一套规则的表情,是绝对路径的表情。比如:

export default emojiData = {"买买买": ["1",true,1]
}

这里我们约定表情图片有统一的路径格式,比如:https://wq.360buyimg.com/fd/wx/img/gwq/face/xxx.png。所以"买买买"实际上是对应https://wq.360buyimg.com/fd/wx/img/gwq/face/1.png

这里定义了"买买买"这个表情文案对应的emoji表情的路径,当然自定义表情的文案和图片要一一对应上。
为了便于区分用户普通文案,我们通常自定义表情都会统一加上"[ ]",比如“买买买”最终显示的时候就是"[买买买]"。为了保证之前已经使用过的表情能正常显示,在系统里面[买买买]永远都只能对应这个图片路径,这里定义了一个数组,后面两个参数我们后面解释。

表情转文本

有了文案和图片的对应关系,按照业务需求,将表情渲染在表情面板上即可,这样在页面的表情面板就可以显示表情的图片路径,然后用户选择了某个表情,在文本框则对应显示图片的文案,比如[买买买],当然存储在系统数据库的也是[买买买]文案。

"买买买": ["1",true,1]

这里数组的true,表示当前表情是否还在使用,对于下线的表情,我们在渲染表情面板的时候要去除,但是已经生成的表情在页面中如果有还需要显示。
数据的1,代表该表情显示在表情面板的第几屏,我这里第一屏是0,所以1代表显示在第二屏。

function getFaceList(panelIndex) {let arr = [];for (let txt in emojiData) {if (emojiData[txt][2] == panelIndex) { // 找到对应面板的显示。if (emojiData[txt][1]) {// 保证该表情未下线let s = getFaceUrl(emojiData[txt]);s = `<a href="javascript:;" title="${txt}"> <img data-src="${s}"></a>`;arr.push(s);}}}return arr;
}

这里是渲染表情面板,根据渲染的面板索引来渲染数据,只有该表情未下线才能渲染到面板里面。

function getFaceUrl(emoji) {return `https://wq.360buyimg.com/fd/wx/img/gwq/face/${emoji[0]}.png`
}

文本转图片

后台查询的文案当中可能含有表情,在页面显示的时候,我们就需要将文本转成图片。

function text2pic(txt) {if (!txt) {return '';}txt = txt.replace(/\[([^\]]+)\]/g, function (v1, v2) {//v1: [买买买]   v2:买买买let d = emojiData[v2];return d ? '<img class="quan_icon_emoji" src="' + getFaceUrl(d) + '" />' : v1;});return txt;
}

这里将需要显示的文本全量搜索[xxx]的内容,然后和我们自定义的emoji表情去匹配,如果匹配上了,就说明是一个自定义表情,那么我们就输出一个img图片标签。

当然这里也区别不了这个表情到底是你在面板选择的emoji[买买买],还是你自行在文本框输入的文案[买买买]。你也会发现微信的[懵逼]实际上是区分表情和文本了,如果我们也需要这么做的话,在传入后台的时候就不能单单只是传入[买买买]这个文案了,比如[买买买],那我们之前的识别也需要按这个规则来了。有兴趣和需要的可以试下。

由于这里将普通文本转换成了富文本,所以特别要注意xss的问题。在转换表情前做一次xss处理。确保不出现xss问题,然后在将富文本内容渲染到页面。

系统默认emoji表情

上述自定义表情可以解决我们系统当中需要有特殊意义的emoji表情,比如[买买买]的表情,就是我们业务的特殊表情,实际上并没有这个emoji。

但是系统emoji要注意,它在不同的系统中可能显示的样式会有点差别,不过放心大笑终究是大笑,在哪个系统下都不会变成大哭,如果要求不那么高的感觉问题不大,只要知道有这个显示差异就行了。

Unicode码emoji表情

大笑?的Unicode:\uD83D\uDE04

在用户的输入法输入默认emoji表情,在存储后台的时候,我们需要处理下,将其转换成unicode码存储,比如调用方法emoji2Unicode来转换。

function emoji2Unicode (emoji) {let str = '';if (emoji && emoji.length > 0) {for (const char of emoji) {const index = char.codePointAt(0);if (index > 65535) {const h ='\\u' +(Math.floor((index - 0x10000) / 0x400) + 0xd800).toString(16);const c ='\\u' + ((index - 0x10000) % 0x400 + 0xdc00).toString(16);str = str + h + c;} else {str += char;}}}return str;
}

后台如果存储为unicode码的emoji了,返回到前端的时候,不需要特殊使用innerHTML或者vue的v-html来显示。当做普通文本显示即可。比如如下两种都可以显示为表情?:

document.getElementById("emojiWrap").innerHTML='\uD83D\uDE04';
document.getElementById("emojiWrap").innerText='\uD83D\uDE04';

如果我们调用后台是传参unicode码,比如\uD83D\uDE04,那么前端查询返回的时候,也需要是文本\uD83D\uDE04,这里后台数据库需要支持unicode字符存储,本文先不做讨论,有兴趣的可以翻阅。

HTML实体存储emoji表情

除了使用unicode码存储emoji表情之外,我们还可以采取html实体存储的方式来实现。

用户手机输入法输入表情之后,我们需要转换成实体存储。

emoji ==> html entity
function emoji2HtmlEntity (str) {const patt = /[\ud800-\udbff][\udc00-\udfff]/g;str = str.replace(patt, function (char) {let H, L, code;if (char.length === 2) {H = char.charCodeAt(0); // 取出高位L = char.charCodeAt(1); // 取出低位code = (H - 0xd800) * 0x400 + 0x10000 + L - 0xdc00; // 转换算法return '&#' + code + ';';} else {return char;}});return str;
}

这里将表情转换为了实体存储,这个html entity对于数据库存储没有要求,只要能存储字符串就OK。比如如下例子:

emoji2HtmlEntity('?')  ==>  &#128516;

将这个html实体&#128516存储在后台,然后前端查询的时候,原样返回即可。

html entity ==> emoji

通常html实体我们也可以直接渲染,比如:

document.getElementById("emojiWrap").innerHTML='&#128516';

但是我们还是不能使用innerText来显示html entity,只会显示文本原样内容,另外在微信小程序中又不好显示了,所以我们可以统一采取将html entity转换成unicode码来方式来显示。

如下:he.js

const regexInvalidEntity = /&#(?:[xX][^a-fA-F0-9]|[^0-9xX])/;
const regexDecode = /&(Gt|GT|ii);|&(gt|lt)(?!;)([=a-zA-Z0-9]?)|&#([0-9]+)(;?)|&#[xX]([a-fA-F0-9]+)(;?)|&([0-9a-zA-Z]+)/g;
const decodeMapNumeric = { '0': '\uFFFD', '128': '\u20AC', '130': '\u201A', '131': '\u0192', '132': '\u201E', '133': '\u2026', '134': '\u2020', '135': '\u2021', '136': '\u02C6', '137': '\u2030', '138': '\u0160', '139': '\u2039', '140': '\u0152', '142': '\u017D', '145': '\u2018', '146': '\u2019', '147': '\u201C', '148': '\u201D', '149': '\u2022', '150': '\u2013', '151': '\u2014', '152': '\u02DC', '153': '\u2122', '154': '\u0161', '155': '\u203A', '156': '\u0153', '158': '\u017E', '159': '\u0178' };
const invalidReferenceCodePoints = [1, 2, 3, 4, 5, 6, 7, 8, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 64976, 64977, 64978, 64979, 64980, 64981, 64982, 64983, 64984, 64985, 64986, 64987, 64988, 64989, 64990, 64991, 64992, 64993, 64994, 64995, 64996, 64997, 64998, 64999, 65000, 65001, 65002, 65003, 65004, 65005, 65006, 65007, 65534, 65535, 131070, 131071, 196606, 196607, 262142, 262143, 327678, 327679, 393214, 393215, 458750, 458751, 524286, 524287, 589822, 589823, 655358, 655359, 720894, 720895, 786430, 786431, 851966, 851967, 917502, 917503, 983038, 983039, 1048574, 1048575, 1114110, 1114111];const stringFromCharCode = String.fromCharCode;const object = {};
const hasOwnProperty = object.hasOwnProperty;
const has = function (object, propertyName) {return hasOwnProperty.call(object, propertyName);
};const contains = function (array, value) {let index = -1;const length = array.length;while (++index < length) {if (array[index] == value) {return true;}}return false;
};const merge = function (options, defaults) {if (!options) {return defaults;}const result = {};let key;for (key in defaults) {// A `hasOwnProperty` check is not needed here, since only recognized// option names are used anyway. Any others are ignored.result[key] = has(options, key) ? options[key] : defaults[key];}return result;
};const parseError = function (message) {throw Error('Parse error: ' + message);
};
const codePointToSymbol = function (codePoint, strict) {let output = '';if ((codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint > 0x10FFFF) {if (strict) {parseError('character reference outside the permissible Unicode range');}return '\uFFFD';}if (has(decodeMapNumeric, codePoint)) {if (strict) {parseError('disallowed character reference');}return decodeMapNumeric[codePoint];}if (strict && contains(invalidReferenceCodePoints, codePoint)) {parseError('disallowed character reference');}if (codePoint > 0xFFFF) {codePoint -= 0x10000;output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800);codePoint = 0xDC00 | codePoint & 0x3FF;}output += stringFromCharCode(codePoint);return output;
};const decode = function (html, options) {options = merge(options, decode.options);const strict = options.strict;if (strict && regexInvalidEntity.test(html)) {parseError('malformed character reference');}return html.replace(regexDecode, function ($0, $1, $2, $3, $4, $5, $6, $7, $8) {let codePoint;let semicolon;let decDigits;let hexDigits;if ($4) {// Decode decimal escapes, e.g. `												

H5小程序中实现emoji表情相关推荐

  1. 微信小程序中使用emoji表情相关说明

    本帖将聚合一些跟emoji表情有关的知识:前端传过来的昵称和备注信息一定要经过严格的正则表达式过滤,放置出现XSS等攻击,另外emoji字体表情库应该使用base64_encode编码,拿信息的时候b ...

  2. php转换emoji表情为图片输出小程序,微信小程序中使用emoji表情相关说明

    本帖将聚合一些跟emoji表情有关的知识:相关文章:"i爱记账" 小程序后端开发小结 第7条经验前端传过来的昵称和备注信息一定要经过严格的正则表达式过滤,放置出现XSS等攻击,另外 ...

  3. 微信小程序开发系列(五)——小程序中存储emoji表情符

    我的小程序整个架构是 app+ asp.net + mysql 因为在"短信定时提醒"中的"随手记"里想存储表情符,所以还是经过了一番折腾的. 首先上网查遍了e ...

  4. 小程序中如何实现表情组件

    先上效果图(无图无真相) 1. 第一步准备表情包素材 我这里用的微博的表情包可以点击下面的链接查看具体JSON格式这里不展示 表情包文件weibo-emotions.js 2. 第二步编写表情组件(基 ...

  5. 小程序中如何使用Emoji表情

    在小程序开发的过程中,类似商城.社区之类的项目,大多数都遇到过使用emoji表情的需求,我在网上查找到一些文章,给了我一些灵感,以下就是使用表情的步骤. 参考文章链接--->https://bl ...

  6. 微信小程序中嵌套html_微信小程序:web-view嵌套H5实现微信支付功能解决方案及填坑...

    ab7117c7d4947210c39e126a01d23ede.jpg 最近一个多月加班比较严重,偶尔休息一天也是在补睡眠+陪家人,比较长时间没有来进行总结记录了.今天不加班,开始为这段时间做的东西 ...

  7. 在h5页面中调起支付宝小程序中的某一个页面以及URLScheme 之 支付宝

    在h5页面中调起支付宝小程序中的某一个页面 直接上代码: window.location.href = 'alipays://platformapi/startapp?appId=2021001181 ...

  8. 小程序中使用web-view链接H5网页

    ##加粗样式小程序中使用web-view链接H5网页 1.小程序中,封装统一的接口请求方法(以便在每个接口中都携带 cookie,放在 header 中):const request = parame ...

  9. 微信小程序云开发--实现微信小程序中访问外部h5网页

    小程序中需要在一些位置添加广告,链接到外部的h5网页. 整体实现思路:定义一个广告组件,一个用来展示外部网页的page outUrl, 在组件中使用wx.navigateTo()等跳转到页面outUr ...

最新文章

  1. jpa分页查询_spring data jpa 居然提供了这么多查询方式!
  2. 从这篇YouTube论文,剖析强化学习在工业级场景推荐系统中的应用
  3. [NHibernate] Guid 作主键速度超慢的背后
  4. 读了几篇boosting文献的收获
  5. 算法设计与分析———动态规划———最大子段和
  6. 2018/12/18 Mac 版 VMWare配置VMNet8 成功版
  7. eureka hostname作用_springcloud使用Eureka实现服务治理替代dubbo加zookeeper
  8. 每天学一点flash(56)循环的小实验
  9. mysql+cls()_MySQL性能优化之show processlist(一)
  10. 魅族消息推送服务器,内部员工透露魅族早已完成统一推送服务适配
  11. 数据仓库与数据挖掘的个人总结
  12. 19年程序员薪酬报告:平均年薪超70万,40岁后普遍遭遇收入天花板
  13. 带饭省下的钱充3个月话费还嫌多!
  14. 手机上, 除了游戏, 还能做什么?
  15. WTL 绘制 圆角对话框 自绘对话框
  16. 云虚拟服务器登录,云虚拟服务器登录
  17. Pyspark Python worker exited unexpectedly (crashed) java.io.EOFException
  18. Reverse Attention for Salient Object Detection
  19. js获取不到模板DOM元素
  20. 关于WIN10 edge浏览器报错 找不到DNS地址 错误代码:INET_E_RESOURCE_NOT_FOUND 的解决方法

热门文章

  1. PHP slideup,jQuery 的 slideUp 和 slideDown实现 展开和收缩效果
  2. 修改注册表导致无法登录到你的账户
  3. 《程序员》 -- 如何提高团队协作的效率
  4. 没有密码,怎么打开Excel?
  5. python基础学习-老王开枪
  6. 2022浙江最新水利水电施工安全员模拟考试试题及答案
  7. 禁用IE和火狐的缓存(转)
  8. Android 6.0 动态权限申请简单简洁优雅的处理方式
  9. VS C# WinForm程序覆盖安装配置
  10. 【007】工信部第一份关于操作系统实时性的测评报告