UNPKG

8.14 kBJavaScriptView Raw
1// Emoji 表情管理
2var Emoji = function Emoji(params) {
3 /* --------------------
4 Model
5 -------------------- */
6 var defaults = {
7 data: null,
8
9 mask: null,
10 maskClass: 'emoji-mask',
11 maskActiveClass: 'active',
12 isClickMaskHide: true,
13
14 containerClass: 'emoji',
15 containerActiveClass: 'active',
16
17 submitClass: 'emoji-edit-submit',
18
19 textareaClass: 'emoji-edit-input',
20
21 iconClass: 'emoji-edit-icon', // 展开和收缩按钮
22
23 faceClass: 'emoji-face', // 表情
24 faceNameAttr: 'title',
25 faceIdAttr: 'data-emoji',
26 deleteClass: 'emoji-face-delete', // 删除表情按钮
27
28 carrouselClass: 'emoji-carrousel'
29 /*
30 callbacks
31 onChange:function(value, s, e)
32 onClickMask: function(s, e)
33 onClickSubmit: function(value, s, e)
34 */
35 };
36 params = params || {};
37 for (var def in defaults) {
38 if (params[def] === undefined) {
39 params[def] = defaults[def];
40 }
41 }
42
43 // Emoji
44 var s = this;
45
46 // Params
47 s.params = params;
48
49 // Mask
50 s.mask = typeof s.params.mask === 'string' ? document.querySelector(s.params.mask) : s.params.mask;
51 if (!s.mask) {
52 console.log('SeedsUI Error:未找到Emoji的mask元素,请检查传入参数是否正确');
53 return;
54 }
55
56 // Container
57 s.container = s.mask.querySelector('.' + s.params.containerClass);
58 if (!s.container) {
59 console.log('SeedsUI Error:未找到Emoji的container元素,请检查传入参数是否正确');
60 return;
61 }
62
63 // Textarea
64 s.textarea = s.mask.querySelector('.' + s.params.textareaClass + ' textarea');
65 if (!s.textarea) {
66 console.log('SeedsUI Error:未找到Emoji的textarea元素,请检查传入参数是否正确');
67 return;
68 }
69 if (s.textarea.tagName !== 'TEXTAREA') {
70 console.log('SeedsUI Error:Emoji的textarea元素必须为一个textarea');
71 return;
72 }
73
74 // Icon
75 s.icon = s.mask.querySelector('.' + s.params.iconClass);
76 if (!s.textarea) {
77 console.log('SeedsUI Error:未找到Emoji的icon元素,请检查传入参数是否正确');
78 return;
79 }
80
81 // Carrousel
82 s.carrousel = s.mask.querySelector('.' + s.params.carrouselClass);
83 if (!s.textarea) {
84 console.log('SeedsUI Error:未找到Emoji的carrousel元素,请检查传入参数是否正确');
85 return;
86 }
87 /* --------------------
88 Method
89 -------------------- */
90 // 设置数据
91 s.setData = function (data) {
92 s.params.data = data;
93 };
94 // 显隐
95 s.showMask = function () {
96 s.mask.classList.add(s.params.maskActiveClass);
97 };
98 s.hideMask = function () {
99 s.mask.classList.remove(s.params.maskActiveClass);
100 };
101 s.destroyMask = function () {
102 s.mask.parentNode.removeChild(s.mask);
103 };
104 s.showContainer = function () {
105 s.container.classList.add(s.params.containerActiveClass);
106 };
107 s.hideContainer = function () {
108 s.container.classList.remove(s.params.containerActiveClass);
109 };
110 s.show = function () {
111 s.showMask();
112 s.showContainer();
113 };
114 s.hide = function () {
115 s.hideMask();
116 s.hideContainer();
117 };
118 // 将文件转成图片
119 s.parse = function (str) {
120 var emojiExpr = /(\[[\u4E00-\u9FA5]*\])/gm;
121 var parseStr = str;
122 while (emojiExpr.exec(str)) {
123 if (s.params.data[RegExp.$1]) {
124 parseStr = parseStr.replace(RegExp.$1, '<span ' + s.params.faceIdAttr + '=' + s.params.data[RegExp.$1] + '></span>');
125 }
126 }
127 return parseStr;
128 };
129 // 监听位置
130 s.cursorOffset = 0;
131
132 // 插入表情文字
133 s.insertFace = function (emojiName) {
134 // 设置value
135 var value = s.textarea.value;
136 var valueBefore = value.substr(0, s.cursorOffset);
137 var valueAfter = value.substr(s.cursorOffset, value.length);
138 var valueInsert = emojiName;
139 s.cursorOffset = s.cursorOffset + emojiName.length;
140 s.textarea.value = valueBefore + valueInsert + valueAfter;
141 // 设置光标位置
142 // s.textarea.focus()
143 // s.setCaretPosition(s.textarea, s.cursorOffset)
144 return s.textarea.value;
145 };
146 // 删除表情文字
147 s.deleteFace = function () {
148 // 设置value
149 var value = s.textarea.value;
150 var valueBefore = value.substr(0, s.cursorOffset);
151 var valueAfter = value.substr(s.cursorOffset, value.length);
152 var isDeleted = false;
153 if (!valueBefore) return value;
154 // 匹配光标前的最后一个表情
155 for (var face in s.params.data) {
156 if (valueBefore.lastIndexOf(face) !== -1 && valueBefore.lastIndexOf(face) === valueBefore.length - face.length) {
157 valueBefore = valueBefore.substring(0, valueBefore.lastIndexOf(face));
158 isDeleted = true;
159 break;
160 }
161 }
162 // 如果没有匹配到表情, 则删除最后一个字符
163 if (!isDeleted) {
164 valueBefore = valueBefore.substring(0, valueBefore.length - 1);
165 }
166 s.cursorOffset = valueBefore.length;
167 s.textarea.value = valueBefore + valueAfter;
168 // 设置光标位置
169 // s.textarea.focus()
170 // s.setCaretPosition(s.textarea, s.cursorOffset)
171 return s.textarea.value;
172 };
173 // 设置光标位置
174 s.setCaretPosition = function (input, pos) {
175 if (!input) return;
176 if (input.createTextRange) {
177 var range = input.createTextRange();
178 range.move('character', pos);
179 range.select();
180 } else {
181 if (input.selectionStart) {
182 input.focus();
183 input.setSelectionRange(pos, pos);
184 } else {
185 input.focus();
186 }
187 }
188 };
189 /* --------------------
190 Controller
191 -------------------- */
192 s.preventDefault = function (e) {
193 e.preventDefault();
194 };
195 s.events = function (detach) {
196 var action = detach ? 'removeEventListener' : 'addEventListener';
197 s.mask[action]('click', s.onClick, false);
198 // ios内核bug兼容
199 if (navigator.userAgent.toLowerCase().match(/cpu iphone os (.*?) like mac os/)) {
200 // 获取焦点
201 s.textarea[action]('focus', s.onFocus, false);
202 // 去除获取焦点时的滚动条
203 s.mask[action]('touchmove', s.preventDefault, false);
204 // 失去焦点
205 s.textarea[action]('blur', s.onBlur, false);
206 }
207 // 获得光标位置
208 document[action]('selectionchange', s.onSelectionChange, false);
209 s.textarea[action]('input', s.onInput, false);
210 };
211 s.attach = function () {
212 s.events();
213 };
214 s.detach = function () {
215 s.events(false);
216 };
217 s.onClick = function (e) {
218 var target = e.target;
219 if (target.classList.contains(s.params.faceClass)) {
220 // 点击表情
221 var value = s.insertFace(target.getAttribute(s.params.faceNameAttr));
222 if (s.params.onChange) s.params.onChange(value, s);
223 } else if (target.classList.contains(s.params.iconClass)) {
224 // 点击展开收缩图标
225 if (s.carrousel.style.display === 'none') {
226 setTimeout(function () {
227 s.carrousel.style.display = 'block';
228 s.icon.classList.add('active');
229 }, 100);
230 // 解决ios12输入法遮挡的问题
231 s.textarea.disabled = true;
232 } else {
233 s.carrousel.style.display = 'none';
234 s.icon.classList.remove('active');
235 }
236 } else if (target.classList.contains(s.params.maskClass)) {
237 // 点击遮罩
238 if (s.params.onClickMask) s.params.onClickMask(s, e);
239 if (s.params.isClickMaskHide) s.hide();
240 } else if (target.classList.contains(s.params.submitClass)) {
241 // 点击提交
242 if (s.params.onClickSubmit) s.params.onClickSubmit(s.textarea.value, s, e);
243 if (s.params.isClickMaskHide) s.hide();
244 } else if (target.classList.contains(s.params.deleteClass)) {
245 // 点击删除
246 s.deleteFace();
247 } else if (target.tagName === 'TEXTAREA') {
248 // 点击文本框
249 s.icon.classList.remove('active');
250 s.carrousel.style.display = 'none';
251 s.textarea.disabled = false;
252 s.textarea.focus();
253 }
254 e.stopPropagation();
255 };
256 // 兼容iphonex迅飞输入法快捷选词栏遮住输入框的问题
257 s.onFocus = function () {
258 setTimeout(function () {
259 document.body.scrollTop = document.body.scrollHeight;
260 }, 300);
261 };
262 // 兼容ios12输入法把页面顶上去, 不回弹的问题
263 s.onBlur = function () {
264 setTimeout(function () {
265 document.body.scrollTop = 0;
266 }, 10);
267 };
268 // 获得光标位置
269 s.onSelectionChange = function (e) {
270 if (Object.prototype.toString.call(e.target.activeElement) === '[object HTMLTextAreaElement]') {
271 s.cursorOffset = s.textarea.selectionStart;
272 }
273 };
274 s.onInput = function (e) {
275 s.cursorOffset = e.target.selectionStart;
276 };
277 /* --------------------
278 Init
279 -------------------- */
280 s.init = function () {
281 s.attach();
282 };
283 s.init();
284};
285
286export default Emoji;
\No newline at end of file