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, "&amp;" ).replace(/</g, "&lt;" ).replace(/>/g, "&gt;" ).replace(/"/g, "&quot;" ).replace(/'/g, "&#39;" ); } 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

Git git git git git git git git git git ~ ~ ~ ~ ~

No reprinting! ~ ~ ~ ~