前言
公司主營業(yè)務(wù):網(wǎng)站設(shè)計制作、成都網(wǎng)站設(shè)計、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出北林免費做網(wǎng)站回饋大家。
大家肯定對@功能不陌生,在如今的各大社交軟件中它是一種不可或缺的功能。實現(xiàn)@人的功能并不復(fù)雜,只需將@人員的id傳給后端,后端下發(fā)通知即可。主要的復(fù)雜點在于一鍵刪除功能與變色功能,web端可以使用現(xiàn)成庫 caret.js
或者 At.js 來實現(xiàn)。但筆者需要在小程序中實現(xiàn)這個功能,而且在 textarea
標簽里實現(xiàn),當然@人名的變色功能自然而然就砍掉了。
準備工作
怎么來實現(xiàn)一鍵刪除呢?首先想到對@人名前后用特殊符號標記+正則來實現(xiàn),但結(jié)果不是很理想,擴展性也比較差,如果還要匹配話題之類的就得多寫一套代碼,所以就試著找其他方法解決。發(fā)現(xiàn) wx.getSelectedTextRange
可以獲取文本框聚焦時的光標,這樣就可以將@人員插入文本指定位置。文本框事件 @input 的可以獲取到變化的數(shù)據(jù)與位置,那就可以根據(jù)變化的位置與變化的數(shù)據(jù)來判斷是否命中@人員,@人員的位置可以通過計算獲取。
// bindinput事件返回值 // value為變化后的值 cursor為變化的位置 keyCode為觸發(fā)的鍵值 const {value, cursor, keyCode} = event.detail // 獲取光標位置,聚焦時生效 wx.getSelectedTextRange({ complete: res => { console.log('光標位置:', res.start, res.end) } })
準備工作做好了就進入實踐環(huán)節(jié),畢竟實踐是檢驗真理的唯一標準。設(shè)計圖呈現(xiàn):通過點擊@按鈕到人員列表頁面,選擇人員后返回,具體如下圖。這里涉及頁面之間的通信問題,可以通過狀態(tài)管理器、數(shù)據(jù)緩存、獲取頁面棧設(shè)置數(shù)據(jù)等來實現(xiàn),本例中使用數(shù)據(jù)緩存。
數(shù)據(jù)組裝
從人員列表返回用 wx.navigateBack
,會觸發(fā) onShow 這個生命周期,所以需要在 onShow 里組裝@數(shù)據(jù)。獲取到的@人員根據(jù)光標位置對文本進行字符串截取組裝,若未獲取到光標位置則直接將@人員添加到文本末尾。然后對@人員數(shù)據(jù)、文本數(shù)據(jù)等進行備份,用于后續(xù)的計算。
initAtFn() { // 獲取@人員數(shù)據(jù) const me = this const initMemberList = wx.getStorageSync('atMemberList') const atMemberArr = initMemberList ? initMemberList : [] // 賦值后清除@人員數(shù)據(jù) wx.removeStorageSync('atMemberList') // 獲取上一次光標的位置 const preCursor = wx.getStorageSync('blurCursor') ? parseInt(wx.getStorageSync('blurCursor')) : me.content.length // 將 @人員數(shù)據(jù) 并入內(nèi)容區(qū)域 if (atMemberArr.length > 0) { // 獲取人員名稱 const atMemberName = `@${atMemberArr[0].name}` // 如果上次光標有記錄 就根據(jù)光標分割字符串 并入@人員名稱 if (preCursor.toString().length !== me.content.length) { const start = me.content.substring(0, preCursor) const end = me.content.substring(preCursor) me.content = `${start}${atMemberName}${end}` } else { me.content += `${atMemberName}` } me.atArr = me.atArr.concat(atMemberArr) // 合并人員 wx.setStorageSync('blurCursor', preCursor + atMemberName.length) }else { wx.setStorageSync('blurCursor', me.content.length) } me.focus = true me.copyContent = me.content me.executeArr = me.getAtMemberPosFn() // 獲取@人員位置 }
計算@人員位置
對@人員數(shù)組進行遍歷,計算@人員在文本中的位置區(qū)間。通過indexOf來獲取起點(這里有一個缺陷,也是需要優(yōu)化的點,當手動輸入的內(nèi)容中有和@人員名字相同的字段時,那么位置靠前的那一個將會生效),終點為起點+名字長度。這里有個問題:如果重復(fù)@相同的人員,刪除時怎么區(qū)分呢?筆者想當然的使用了時間戳,結(jié)果發(fā)現(xiàn)在遍歷中使用時間戳并不準確,只有規(guī)規(guī)矩矩生成唯一值。
計算時收集了人員位置的最值區(qū)間,在這個范圍之外增減文本不會影響@人員的完整性。下面是代碼:
getAtMemberPosFn() { const me = this const [tipArr, left, right] = [ [], [], [] ] // 根據(jù)@人員的數(shù)組來匹配計算所處位置 me.atArr.map(item => { const name = item.name const userId = item.userId // 此處有一個缺陷 如果手輸入的和獲取的@人名字相同 第一個會生效 第二個不會生效 let start = me.copyContent.indexOf(name) if (tipArr.length > 0) { const _arr = tipArr.filter(v => v.name.includes(name)) if (_arr.length > 0) { start = me.copyContent.indexOf(name, _arr[_arr.length - 1].end) } } const end = name.length + start // end left.push(start) right.push(end) // 獲取唯一標識 是用于重復(fù)@的區(qū)分 const guid = me.createGuidFn() const tipObj = { start: start - 1, // @ - 1 end, name, atName: `@${name}`, type: item.userId, userId: userId, code: guid } tipArr.push(tipObj) }) // 獲取區(qū)間左右最值 right.length > 0 ? me.maxAt = Math.max(...right) : me.maxAt = 0 left.length > 0 ? me.minAt = Math.min(...left) : me.minAt = 0 me.atArr = tipArr return tipArr }
一鍵刪除功能
@人員的位置區(qū)間已經(jīng)計算出來了,接下來監(jiān)聽輸入框的內(nèi)容變化實現(xiàn)一鍵刪除功能,當輸入框文本內(nèi)容變化,會觸發(fā) @input 事件,它會返回變化后的值 value ,變化的位置 cursor ,我們將利用這兩個數(shù)據(jù)作為是否 命中@人員的判斷依據(jù) 。將情況分為以下幾種:
變化后的value為空,即清空了輸入框。
數(shù)據(jù)變化的光標位置大于@人員位置最值區(qū)間的最大值,即不影響@人員位置。
當數(shù)據(jù)變化影響@人員時,這里對增加減少內(nèi)容做了區(qū)分處理:
增加時,如果增加位置小于最值的最小值,則直接重新計算位置。如果增加值的位置命中@人員位置,則過濾掉失效人員,再重新計算。這里需要注意,移動端輸入法會有一次性輸入多個字符,變化的位置不再是返回的光標位置,而是以光標位置減去變化前后數(shù)據(jù)的差值。
刪除時,獲取刪除的起始位置 (A,B) ,然后與@人員位置 (start, end) 作比較。 當 !(A < start || B > end) 時,則為命中,將命中的@人員過濾掉即可。
changeFn(txt) { const me = this const { value, cursor, keyCode } = txt.detail // 改變后的值,改變的位置,按鍵 // 如果改變后的值為'', 就直接返回 if(!value) { me.content = value me.copyContent = value me.atArr = [] return false } // 判斷值改變的增減 const changeLen = value.length - me.copyContent.length // 值改變的光標位置 不影響@人員的則不管 if (cursor > me.maxAt) { me.copyContent = me.content return false } // 判斷為 增加值 if (changeLen > 0) { const addCursor = cursor - changeLen // 重新計算增加位置 防止移動端一次性粘貼導(dǎo)致失效問題 me.copyContent = me.content // 增加值的位置 小于左區(qū)間最值 則重新計算位置 if(addCursor < me.minAt) { me.executeArr = me.getAtMemberPosFn() return false } me.executeArr.map(item => { const { start, end, name, code } = item if (addCursor < end && addCursor > start) { // 刪除命中人員,則該人員失效 me.atArr = me.atArr.filter(v => v.code !== code) } }) // 需要重新計算位置 me.executeArr = me.getAtMemberPosFn() } else { let replaceStr = '' // 應(yīng)被刪除的字段 const left = [] // 刪除左值集合 const right = [] // 刪除右值集合 const delLen = cursor - changeLen // 本身刪除的長度 const deleteString = me.copyContent.substring(cursor, delLen) // 本身刪除的字段 [cursor, changeLen) // 獲取應(yīng)被刪除的左右位置 function pushArrEvent(s, e) { left.push(s) right.push(e) } me.executeArr.map(item => { let { start, end, name, code } = item // D大 <= B小 || D小 >= B大 // 命中部分為 刪除部分與@人員的交集 if (!(delLen <= start || cursor >= end)) { // 命中判定,命中位置在名字區(qū)間 左邊/右邊/之間/或者多選中刪除的 if (delLen <= end && cursor >= start) { pushArrEvent(start, end) } else { if (cursor > start) { if (delLen > end) { pushArrEvent(start, delLen) } else { pushArrEvent(start, end) } } else if (cursor < start) { if (delLen > end) { pushArrEvent(cursor, delLen) } else { pushArrEvent(cursor, end) } } else { pushArrEvent(cursor, delLen) } } // 獲取一鍵刪除區(qū)間 const del_left = Math.min(...left) const del_right = Math.max(...right) // 根據(jù)區(qū)間獲取一鍵刪除字段 replaceStr = me.copyContent.substring(del_left, del_right) // 刪除后的賦值 me.content = me.copyContent.substring(0, del_left) + me.copyContent.substring(del_right) // @人員數(shù)組生成 me.atArr = me.atArr.filter(v => v.code !== code) } }) // 執(zhí)行完后 重新賦值計算 me.copyContent = me.content me.executeArr = me.getAtMemberPosFn() } }
添加標簽
我們還差最后一步,那就是給@人名添加標簽,用于顯示時與一般文本做區(qū)分。這里踩了一個坑,用正則替換時,如果名字與名字之間存在包含關(guān)系,則會失效,所以用記錄位置的方式來對文本進行截取組裝。
submitTxtFn() { const copyTxt = this.content const arr = JSON.parse(JSON.stringify(this.atArr)) const atUserIds = [...new Set(arr.map(v=>v.userId))] // 獲取@人員id let targetContent = '' let count = 0 // 給@人員添加wxml標簽,此處用了jyf-Parser富文本解析插件,href里面的值用于點擊傳參 if(arr.length > 0) { arr.forEach((item, index)=>{ let _tip = '' const txt = copyTxt.substring(count, item.start) // 加空格 _tip = `${txt}<a class="link" href="${item.name}" rel="external nofollow" >${item.atName} </a>` targetContent += _tip // 處理最后一個標簽后面的文本 if(index + 1 === arr.length) { if(item.end < copyTxt.length) { targetContent += copyTxt.substring(item.end) } } count = item.end }) }else { targetContent = this.content } // 目標數(shù)據(jù) const targetObj = { content: targetContent, atIds: atUserIds } this.submitData = targetObj return targetObj }
以上就實現(xiàn)了純文本的@功能,通過計算位置來實現(xiàn)的優(yōu)點是具有擴展性,比如一套代碼可以實現(xiàn)#話題功能和@功能共存,只需加個type作為區(qū)分即可。缺點是一鍵刪除時體驗不是很好,并且刪除后不能控制光標位置,不能實現(xiàn)人員名稱變色等。雖然功能比較ZZ,但也比較有趣,所以就分享給大家,如果大家有更好的解決方案,評論區(qū)有請。
完整代碼請移步 語雀
總結(jié)
到此這篇關(guān)于微信小程序純文本實現(xiàn)@功能的文章就介紹到這了,更多相關(guān)小程序@功能內(nèi)容請搜索創(chuàng)新互聯(lián)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持創(chuàng)新互聯(lián)!
當前標題:微信小程序純文本實現(xiàn)@功能
文章源于:http://www.chinadenli.net/article16/jsgdgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、微信小程序、企業(yè)建站、搜索引擎優(yōu)化、域名注冊
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)