网站界面分析,门户网站建设方案公司,房产这么做网站才多点击量,苏州做网站公司哪家比较好前言
最近在用自己的卡盒小程序的时候#xff0c;发现卡片越来越多#xff0c;有时候要找到某一张来看看笔记要找半天#xff0c;于是自己做了一个搜索功能#xff0c;先看效果#xff1a; 怎么样#xff0c;是不是还挺不错的#xff0c;那么这篇文章就讲讲这样一个搜索…前言
最近在用自己的卡盒小程序的时候发现卡片越来越多有时候要找到某一张来看看笔记要找半天于是自己做了一个搜索功能先看效果 怎么样是不是还挺不错的那么这篇文章就讲讲这样一个搜索展示的功能是如何实现的。
代码实现
首先我们分析这个搜索功能包含哪些需要实现的点
输入关键字匹配对应的卡片展示匹配的卡片上面要把符合搜索条件的所有词给高亮展示出来不然文本多的时候容易看花了。点击匹配到的卡片要跳转到卡片的位置并且闪烁两下这样一看就知道要找卡片在哪了。
分析完成后我们一点一点实现。
匹配关键字
我的小程序目前是纯前端搜索的只是目前是这样所以搜索逻辑也是在前端实现。搜索逻辑如果简单实现的话就是将搜索框的内容与列表中的每一项进行对比看看内容中有没有包含这个字符串的如果有就把这个项给返回回去。
先看代码
const onSearch () {// 检查搜索文本框是否有值if (searchText.value) {// 创建一个正则表达式对象用于在卡片内容中搜索文本// giu 标志表示全局搜索、不区分大小写和支持 Unicodeconst searchTextRegex new RegExp(searchText.value, giu)// 遍历所有的卡片盒子const matchCardBox cardDataStore.cardBoxList.map((cardBox) {// 对每个卡片盒子创建一个新对象包含原始属性和修改后的卡片项目return {...cardBox,// 映射并过滤卡片项目只保留匹配搜索文本的项目cardItems: cardBox.cardItems.map((cardItem) {// 初始化前面和背面内容的匹配数组const frontMatches []const backMatches []let match// 在卡片前面内容中搜索匹配项while ((match searchTextRegex.exec(cardItem.frontContent)) ! null) {// 记录每个匹配项的起始和结束索引frontMatches.push({startIndex: match.index,endIndex: match.index match[0].length,})}// 重置正则表达式的 lastIndex 属性以便重新搜索searchTextRegex.lastIndex 0// 在卡片背面内容中搜索匹配项while ((match searchTextRegex.exec(cardItem.backContent)) ! null) {// 记录每个匹配项的起始和结束索引backMatches.push({startIndex: match.index,endIndex: match.index match[0].length,})}// 检查是否有匹配项前面或背面const isMatched frontMatches.length 0 || backMatches.length 0// 返回一个新的卡片项目对象包含是否匹配和匹配项的位置return {...cardItem,isMatched,frontMatches,backMatches,}})// 过滤掉不匹配的项目.filter((item) item.isMatched),}})// 过滤掉没有匹配项目的卡片盒子filteredCards.value matchCardBox.filter((cardBox) cardBox.cardItems.length 0)} else {// 如果没有搜索文本则清空过滤后的卡片列表filteredCards.value []}
}1. 创建正则表达式
const searchTextRegex new RegExp(searchText.value, giu) searchText.value这是用户输入的搜索文本。 new RegExp(...) 通过传入的搜索文本和标志‘giu’创建一个新的正则表达式对象。 g全局搜索标志表示搜索整个字符串中的所有匹配项而不是在找到第一个匹配项后停止。 i不区分大小写标志表示搜索时忽略大小写差异。 - uUnicode 标志表示启用 Unicode 完整匹配模式这对于处理非 ASCII 字符很重要。
2. 搜索匹配项
let match let match声明一个变量 match它将用于存储 RegExp.exec() 方法找到的匹配项。
while ((match searchTextRegex.exec(cardItem.frontContent)) ! null) { // ... } searchTextRegex.exec(cardItem.frontContent) 在 cardItem.frontContent 卡片的正面内容中执行正则表达式搜索。 如果找到匹配项exec() 方法返回一个数组其中第一个元素match[0]是找到的匹配文本index 属性是匹配项在字符串中的起始位置。 如果没有找到匹配项exec() 方法返回 null。 - while 循环继续执行直到 exec() 方法返回 null表示没有更多的匹配项。
3. 记录匹配项的索引
frontMatches.push({ startIndex: match.index, endIndex: match.index match[0].length, }) 在每次循环迭代中都会找到一个匹配项。startIndex匹配项在 cardItem.frontContent 中的起始位置。endIndex匹配项在 cardItem.frontContent 中的结束位置即起始位置加上匹配文本的长度。frontMatches.push(...)将包含起始和结束索引的对象添加到 frontMatches 数组中。
经过这么一番操作我们就可以获得一个筛选后的数组其中包含了所有匹配的项每个项还有一个二维数组用来记录匹配位置开头结尾的索引
cardItems: (CardItem {id: stringfrontMatches?: { startIndex: number; endIndex: number }[]backMatches?: { startIndex: number; endIndex: number }[]})[]为什么要大费周章的记录这个索引呢那是因为下一步需要用到接下来说说关键词高亮的展示
关键词高亮 关键词高亮需要在字符串的某几个字符中更改它的样式因此我们上一步才需要记录一下需要高亮的字符串开始和结束的位置如此一来我们做这个高亮的组件就不用再执行一次匹配了。那么这个样式要如何实现呢我们需要遍历这个字符串在需要高亮的字增加额外的样式最后再重新拼接成一个字符串。
// Highlight.vue
templateview classflex flex-wrapviewv-for(charWithStyle, index) in styledTextclasstext-sm:keyindex:classcharWithStyle.isMatched ? text-indigo-500 font-semibold : {{ charWithStyle.char }}/view/view
/templatescript langts setup
import { defineProps, computed } from vueinterface Props {text: stringmatches: { startIndex: number; endIndex: number }[]
}const props definePropsProps()const styledText computed(() {const textArray _.toArray(props.text)const returnArr []let index 0let arrIndex 0while (index props.text.length) {let char if (textArray[arrIndex].length 1) {char textArray[arrIndex]} else {char props.text[index]}const isMatched props.matches.some((match) {const endIndex match.endIndexconst startIndex match.startIndexreturn startIndex index index endIndex})returnArr.push({ char, isMatched })index textArray[arrIndex].lengtharrIndex 1}return returnArr
})
/script
这里我没有使用 for of 直接遍历字符串这也是我的一个踩坑点像 emoji 表情这种字符它的长度其实不是 1如果你直接使用 for of 去遍历会把它的结构给拆开最终展示出来的是乱码如果你想正常展示就要用 Array.from(props.text) 的方式将字符串转换成数组再进行遍历这样每个字符就都是完整的。
假设我们打印 console.log([0], .length)console.log(Array.from()[0], Array.from().length)这里我没有使用 Array.from 而是使用了 lodash 中的 toArray是因为看到这篇文章 https://fehey.com/emoji-length 中提到 Array.from(props.text) 创建的数组 textArray 中的每个元素实际上是一个 UTF-16 代码单元的字符串表示而不是完整的 Unicode 字符 Emoji 表情有可能是多个 Emoji 一些额外的字符 来拼接出来的像 ‘’ 就是由 [‘’, ‘’, ‘’, ‘’, ‘’, ‘’, ‘’] 拼接而成的单个 Emoji 长度为 2中间的连接字符长度为 1故返回了 11。 如何获取 ‘’ 的长度为视觉的 1 呢可以使用 lodash 的 toArray 方法_.toArray(‘’).length 1,其内部实现 了对 unicode 字符转换数组的兼容。 正是因为我们第一步中使用正则去匹配字符串的时候是根据表情包字符实际的长度返回的索引值所以我们这里有一个逻辑 let index 0let arrIndex 0while (index props.text.length) {let char if (textArray[arrIndex].length 1) {char textArray[arrIndex]} else {char props.text[index]}
//index textArray[arrIndex].lengtharrIndex 1}如果字符的长度大于一我们就从字符串数组中取值这样表情包就能正常展示了然后维护两个索引一个索引给字符长度大于1的字符用一个给字符长度为1的用根据不同的情况取不同的值这样就能处理好表情包的这种情况了。下面这种很多个表情包的文本也能在正确的位置高亮 请添加图片描述
滚动到指定位置并高亮
这一步就比较简单了直接上代码
import { getCurrentInstance } from vue
const instance getCurrentInstance()
const { safeAreaInsets } uni.getWindowInfo()// 滚动到卡盒位置
const scrollToCardBox (position: top | bottom top) {const query uni.createSelectorQuery().in(instance.proxy)query.select(#card-box-${props.cardBoxIndex}).boundingClientRect((data) {return data}).selectViewport().scrollOffset((data) {return data}).exec((data) {uni.pageScrollTo({scrollTop:data[1].scrollTop data[0].top -safeAreaInsets.top (position top ? 0 : data[0].height),duration: 200,})})
}关于解析在 踩坑无数如何用 uniapp 实现一个丝滑的英语学习卡盒小程序 这篇文章中有详细提到这里不赘述了。
uni.pageScrollTo 有一个回调可以用于滚动到指定位置后执行某个函数那么我们可以在这里设置触发高亮的动画动画的实现如下
view//...:classcardItemStore.scrollToCardItemId props.cardItemData.id ? animation-after-search : .animation-after-search {animation: vague 1s;animation-iteration-count: 2;
}keyframes vague {0%,100% {box-shadow: inset 0 0 0 0 transparent; /* 初始和结束时没有阴影 */}50% {box-shadow: inset 0 0 0 2px #6366f1; /* 中间时刻显示阴影 */}
}这里没有使用边框而是使用了内嵌的阴影避免边框会把容器撑大的问题滚动完成后动态给指定元素一个执行动画的 class,动画触发完成后再移除 class 就 OK 了。效果如下 总结
如果不是遇到了表情包长度问题这个搜索功能的实现还是比较简单的重点是交互和设计是否能够让用户快速定位到想找的内容。目前是纯前端实现而且涉及了很多遍历性能还有待提升不过先实现再优化了。学习卡盒已经上线了大家可以直接搜索到这个搜索功能也发版了欢迎体验。 文章转载自: http://www.morning.rfgkf.cn.gov.cn.rfgkf.cn http://www.morning.lzqnj.cn.gov.cn.lzqnj.cn http://www.morning.sfwcb.cn.gov.cn.sfwcb.cn http://www.morning.byywt.cn.gov.cn.byywt.cn http://www.morning.rsnn.cn.gov.cn.rsnn.cn http://www.morning.rrcrs.cn.gov.cn.rrcrs.cn http://www.morning.rqckh.cn.gov.cn.rqckh.cn http://www.morning.bpp999.com.gov.cn.bpp999.com http://www.morning.sgnjg.cn.gov.cn.sgnjg.cn http://www.morning.kkjhj.cn.gov.cn.kkjhj.cn http://www.morning.bqdpy.cn.gov.cn.bqdpy.cn http://www.morning.muniubangcaishui.cn.gov.cn.muniubangcaishui.cn http://www.morning.ygrkg.cn.gov.cn.ygrkg.cn http://www.morning.qgghj.cn.gov.cn.qgghj.cn http://www.morning.qrmry.cn.gov.cn.qrmry.cn http://www.morning.rrpsw.cn.gov.cn.rrpsw.cn http://www.morning.czcbl.cn.gov.cn.czcbl.cn http://www.morning.rstrc.cn.gov.cn.rstrc.cn http://www.morning.c7493.cn.gov.cn.c7493.cn http://www.morning.drndl.cn.gov.cn.drndl.cn http://www.morning.grbgn.cn.gov.cn.grbgn.cn http://www.morning.qghjc.cn.gov.cn.qghjc.cn http://www.morning.rxxdk.cn.gov.cn.rxxdk.cn http://www.morning.mxnrl.cn.gov.cn.mxnrl.cn http://www.morning.srkzd.cn.gov.cn.srkzd.cn http://www.morning.lxmks.cn.gov.cn.lxmks.cn http://www.morning.fdzzh.cn.gov.cn.fdzzh.cn http://www.morning.ypcd.cn.gov.cn.ypcd.cn http://www.morning.pbxkk.cn.gov.cn.pbxkk.cn http://www.morning.jppb.cn.gov.cn.jppb.cn http://www.morning.nnrqg.cn.gov.cn.nnrqg.cn http://www.morning.rjtmg.cn.gov.cn.rjtmg.cn http://www.morning.swdnr.cn.gov.cn.swdnr.cn http://www.morning.kaakyy.com.gov.cn.kaakyy.com http://www.morning.qhrlb.cn.gov.cn.qhrlb.cn http://www.morning.yqwsd.cn.gov.cn.yqwsd.cn http://www.morning.qbjrf.cn.gov.cn.qbjrf.cn http://www.morning.jyjqh.cn.gov.cn.jyjqh.cn http://www.morning.hxcrd.cn.gov.cn.hxcrd.cn http://www.morning.5-73.com.gov.cn.5-73.com http://www.morning.mqgqf.cn.gov.cn.mqgqf.cn http://www.morning.ysqb.cn.gov.cn.ysqb.cn http://www.morning.gediba.com.gov.cn.gediba.com http://www.morning.smwlr.cn.gov.cn.smwlr.cn http://www.morning.prznc.cn.gov.cn.prznc.cn http://www.morning.pqnkg.cn.gov.cn.pqnkg.cn http://www.morning.bfcrp.cn.gov.cn.bfcrp.cn http://www.morning.jgzmr.cn.gov.cn.jgzmr.cn http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn http://www.morning.rnhh.cn.gov.cn.rnhh.cn http://www.morning.rgsgk.cn.gov.cn.rgsgk.cn http://www.morning.hdzty.cn.gov.cn.hdzty.cn http://www.morning.fwnqq.cn.gov.cn.fwnqq.cn http://www.morning.xxknq.cn.gov.cn.xxknq.cn http://www.morning.fqzz3.cn.gov.cn.fqzz3.cn http://www.morning.gmgnp.cn.gov.cn.gmgnp.cn http://www.morning.hous-e.com.gov.cn.hous-e.com http://www.morning.zgqysw.cn.gov.cn.zgqysw.cn http://www.morning.yktwr.cn.gov.cn.yktwr.cn http://www.morning.ntgrn.cn.gov.cn.ntgrn.cn http://www.morning.lthpr.cn.gov.cn.lthpr.cn http://www.morning.fjglf.cn.gov.cn.fjglf.cn http://www.morning.qwpdl.cn.gov.cn.qwpdl.cn http://www.morning.dhckp.cn.gov.cn.dhckp.cn http://www.morning.ns3nt8.cn.gov.cn.ns3nt8.cn http://www.morning.qynpw.cn.gov.cn.qynpw.cn http://www.morning.drjll.cn.gov.cn.drjll.cn http://www.morning.tnthd.cn.gov.cn.tnthd.cn http://www.morning.mhcys.cn.gov.cn.mhcys.cn http://www.morning.mxnfh.cn.gov.cn.mxnfh.cn http://www.morning.sbrjj.cn.gov.cn.sbrjj.cn http://www.morning.lhyhx.cn.gov.cn.lhyhx.cn http://www.morning.chmkt.cn.gov.cn.chmkt.cn http://www.morning.fylsz.cn.gov.cn.fylsz.cn http://www.morning.ddrdt.cn.gov.cn.ddrdt.cn http://www.morning.tgyqq.cn.gov.cn.tgyqq.cn http://www.morning.sjwiki.com.gov.cn.sjwiki.com http://www.morning.brlgf.cn.gov.cn.brlgf.cn http://www.morning.sjjq.cn.gov.cn.sjjq.cn http://www.morning.nyjgm.cn.gov.cn.nyjgm.cn