From the back of a picture and then listen to me for you to weave weave ~~~~~~~~ weave basket
How to realize the input box with expression + text +emoji
What is the essence of emoji + text +emoji??
Look at the picture above and notice the blackboard is broken
The emotical-like display above is an IMG, but its real content is text such as: Smile: : Applaud: consider it a placeholder, our actual server stores text as well.
The following file is an enumeration of IMG emoji placeholders
{" expression ": {" : smile:" : "smile. PNG", ": applaud:" : "applaud. PNG", ": cry:" : "cry. PNG", ": laughing_out_loud:" : "laughing_out_loud.png", ":wronged:": "wronged.png", ":embrace:": "embrace.png", ":shake_hands:": "shake_hands.png", ":thumbs_up:": "thumbs_up.png", ":love:": "love.png", ":rose_flower:": "rose_flower.png", ":anger:": "anger.png" } }Copy the code
How to render the emoticon panel
Img emoticon rendering principle:
I guess you guys already figured Sprite. (No more details! ~ ~ ~ ~)
Sprite figure
The HTML template
css-sprite
[class*="sprite-"] { width:25px; height: 25px; background:url('./css_sprites.png') no-repeat; vertical-align: top; display: inline-block; margin: 5px; background-size:100px auto ; } .sprite-applaud { background-position: -0px -0px; } .sprite-embrace { background-position: -25px -0px; } .sprite-cry { background-position: -0px -25px ! important; } .sprite-love { background-position: -25px -25px; } .sprite-smile { background-position: -50px -0px! important; } .sprite-laughing_out_loud { background-position: -50px -25px; } .sprite-shake_hands { background-position: -0px -50px; } .sprite-wronged { background-position: -25px -50px; } .sprite-rose_flower { background-position: -50px -50px; } .sprite-thumbs_up { background-position: -75px -0; }Copy the code
Emoji rendering principle
I’m going to set the font size for the text and I’m going to set the font size for the text and I’m going to render the text exactly the way I want to render the text and in vue it’s {{}}
Realize the principle of
RenderEmoji methods match placeholders like 🙂 and replace them with Img tags. Then use escHTML to prevent XSS
/** * @export * @param {string} value * @returns {string} */ export function renderEmoji (value) { if (! value) return ""; value = escHTML(value); Object.keys(emojiData).forEach( key => { value = value.replace(new RegExp(key, 'g'), createIcon(key)) }); Function TML (STR = STR + ""; return str.replace(/&/g, "&" ).replace(/</g, "<" ).replace(/>/g, ">" ).replace(/"/g, """ ).replace(/'/g, "'" ); } function createIcon (item) { const value = emojiData[item]+"? max_age=31536000"; return `<img src=${path}${value} style="vertical-align:middle" width="22px" height="22px" />` }Copy the code
-
Chat frame rendering
This method uses V-html =”renderEmoji(message.body.content)” in the chat box to prevent Xss
The essence of this method is to match the placeholder globally and replace the placeholder with an Img tag! ~ ~
-
Input field render
The input box uses the contenteditable=”true” editable area, but with contenteditable=”true” we can’t use v-HTML to render, so what do we do?
Insert the label directly into it. ~ The following code is the core code. You control cursor position and insert img emoticon nodes as well as text nodes
/** * cursorMove(CNT){var edit = this.$refs.userInput; const lastEditRange = this.lastEditRange; edit && edit.focus(); var selection = getSelection(); If (lastEditRange) {/ / there is finally a cursor object, selected objects to remove all the cursor and add the final state of the cursor reduction before selection. RemoveAllRanges (); selection.addRange(lastEditRange); } / / judge selected object if scope is edit box or text node (selection. AnchorNode. NodeName! // create a new cursor object let range = document.createrange (); // The cursor object is scoped as the input box node range.selectNodecontents (edit); If (edit.childnodes.length > 0) {// If (edit.childnodes.length > 0) {// If (edit.childnodes.length > 0) {for (var I = 0; i <= edit.childNodes.length; i++) { if (i == selection.anchorOffset) { edit.insertBefore(cnt, edit.childNodes[i]); Range.setstart (edit, I +1); }}} else {// otherwise insert an emoticon element edit.appendChild(CNT); Range.setstart (edit, edit.childnodes.length); } // Make the cursor start and end overlap rang.collapse (true); / / to clear all the cursor object selection. The selected object removeAllRanges (); // Insert a new cursor object selection.addrange (range); } else {let range = selection. GetRangeAt (0); Var textNode = range.startContainer; var textNode = range.startContainer; Var rangeStartOffset = range.startoffSet; SliceLeftText = textNodeContent. Slice (0,rangeStartOffset) var sliceRightText = textNodeContent.slice(rangeStartOffset) var textLeftNode = document.createTextNode(sliceLeftText); Var textRightNode = document.createTextNode(sliceRightText) // The text node inserts the new emoji content sliceLeftText && at the cursor position edit.insertBefore(textLeftNode, textNode); edit.insertBefore(cnt, textNode); sliceRightText && edit.insertBefore(textRightNode, textNode); / / delete text node textNode. ParentNode. RemoveChild (textNode); console.log('index',[].indexOf.call(cnt.parentNode.childNodes,cnt)) range.setStart(cnt.parentNode,[].indexOf.call(cnt.parentNode.childNodes,cnt)+1); // Cursor start and end overlap rang. collapse(true); / / to clear all the cursor object selection. The selected object removeAllRanges (); // Insert a new cursor object selection.addrange (range); } // The last cursor object is this.lasteditRange = selection.getrangEat (0); }Copy the code
Function support
File drag and drop send function
Editable areas listen for a drop event. ~ ~ ~ ~ ~ ~ ~ ~ ~
dropFIle(e) { const event = e || event const df = event.dataTransfer; if (df.items) { df.items.forEach(async item => { if (item.kind == "file" && item.webkitGetAsEntry().isFile) { var file = item.getAsFile(); If (file.type) {// TODO: file transfer object here write send logic console.log(' file object ',file)}}}); } event.preventDefault(); event.stopPropagation(); },Copy the code
File copy sending function
Text and file copying is currently supported, but img tag copying is also available, which can be edited 🐂🍺! ~ ~ ~ ~ ~ ~
/ / initialize monitor paste event mounted () {this. $refs. UserInput. AddEventListener (" paste ", (e) = > {enclosing handlePaste (e); }); } handlePaste(e) { const event = e || event var items = event.clipboardData.items, len = items.length; for (var i = 0; i < len; i++) { var item = items[i]; if (item.kind == "file") { var file = item.getAsFile(); Log (' copy file to input box triggered ',file)}} if (item.kind === "string" && item.type === "text/plain") { item.getAsString((str) => { var textNode = document.createTextNode(str); this.cursorMove(textNode) }); } } event.preventDefault(); event.stopPropagation(); }Copy the code
Emoticon sending function
This is easy to do! ~~~~ Click the expression panel to select the expression and then ENTER the box ENTER key can be! ~ ~ ~
handleDownKey(event) { if (event.keyCode === 13 && ! event.shiftKey) { // enter && not shift + enter this._submitText(event); event.preventDefault(); return false; } else if (event.keyCode === 27) {// Esc key event.preventDefault(); }}Copy the code
TODO searched globally using portal:
Difficulties encountered during development
No doubt is the cursor position control, but it is really male plus male ah! ~ ~ ~ ~ ~
- getSelection()
- Range object for selection. GetRangeAt ()
- range.setStart()/range.setEnd()
- InsertBefore Inserts the node method
Poke poke here portal! ~~ Some knowledge about Selection and Range
Note: each time you edit, click and select the input box, you need to record the final position of the cursor and calculate the cursor position dynamically each time you insert the expression.
GetSelectRange () {var selection = getSelection(); // Set the last cursor object this.lasteditRange = selection.getrangEat (0); }Copy the code