fix: 搜索建议动画不连贯

This commit is contained in:
imsyy 2023-07-29 11:25:40 +08:00
parent 83fcfa7a01
commit 6084f75913
5 changed files with 116 additions and 146 deletions

View File

@ -11,7 +11,7 @@
> 由于 `CDN` 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存 > 由于 `CDN` 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
- [Snavigation](https://nav.imsyy.top) - [Snavigation](https://nav.imsyy.top)
- [Snavigation Dev](https://snavigation.vercel.app) - [Snavigation Dev](https://snavigation-git-dev-imsyy.vercel.app/)
### 功能 ### 功能

View File

@ -31,8 +31,8 @@
</div> </div>
</div> </div>
<!-- 搜索建议 --> <!-- 搜索建议 -->
<SearchSuggestions <Suggestions
ref="searchSuggestionsRef" ref="suggestionsRef"
:keyWord="inputValue" :keyWord="inputValue"
@toSearch="toSearch" @toSearch="toSearch"
/> />
@ -42,7 +42,7 @@
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { statusStore, setStore } from "@/stores"; import { statusStore, setStore } from "@/stores";
import SearchSuggestions from "@/components/SearchSuggestions.vue"; import Suggestions from "@/components/Suggestions.vue";
const set = setStore(); const set = setStore();
const status = statusStore(); const status = statusStore();
@ -57,7 +57,7 @@ const inputClickable = ref(true);
const inputValue = ref(""); const inputValue = ref("");
// //
const searchSuggestionsRef = ref(null); const suggestionsRef = ref(null);
// //
const closeSearchInput = () => { const closeSearchInput = () => {
@ -135,7 +135,7 @@ const pressKeyboard = (event) => {
// 13 // 13
if (keyCode === 13) toSearch(); if (keyCode === 13) toSearch();
// //
searchSuggestionsRef.value?.keyboardEvents(keyCode, event); suggestionsRef.value?.keyboardEvents(keyCode, event);
}; };
onMounted(() => { onMounted(() => {

View File

@ -1,20 +1,65 @@
<template> <template>
<Transition name="fadeUp" mode="out-in"> <Transition name="fadeDown" mode="out-in">
<div <div
v-if=" v-if="
set.showSuggestions && set.showSuggestions &&
status.siteStatus === 'focus' && status.siteStatus === 'focus' &&
searchKeyword !== null searchKeyword !== null
" "
class="search-suggestions" class="suggestions"
:style="{ :style="{ height: `${suggestionsHeights}px` }"
height: suggestionsHeights !== 0 ? `${suggestionsHeights}px` : 'auto', >
}" <!-- 快捷操作 -->
<Transition
name="fade"
mode="out-in"
@after-enter="changeSuggestionsHeights"
@after-leave="changeSuggestionsHeights"
> >
<!-- 搜索建议 -->
<Transition name="fade" mode="out-in">
<div <div
v-show=" v-if="searchKeyword !== null"
class="special-result"
ref="specialallResultsRef"
>
<!-- 快捷翻译 -->
<div
v-if="searchKeywordType === 'text'"
class="s-result"
@click="toSearch(keyWord, 2)"
>
<SvgIcon iconName="icon-translation" />
<span class="text">快捷翻译{{ keyWord }}</span>
</div>
<!-- 直接访问 -->
<div
v-if="searchKeywordType !== 'text'"
class="s-result"
@click="
toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4)
"
>
<SvgIcon
:iconName="`icon-${
searchKeywordType === 'email' ? 'email' : 'link'
}`"
/>
<span class="text">
{{
searchKeywordType === "email" ? "发送邮件至" : "直接访问"
}}{{ searchKeyword }}
</span>
</div>
</div>
</Transition>
<!-- 搜索建议 -->
<Transition
name="fade"
mode="out-in"
@after-enter="changeSuggestionsHeights"
@after-leave="changeSuggestionsHeights"
>
<div
v-if="
searchKeyword !== null && searchKeyword !== null &&
searchKeywordType === 'text' && searchKeywordType === 'text' &&
searchSuggestionsData[0] searchSuggestionsData[0]
@ -22,12 +67,6 @@
class="all-result" class="all-result"
ref="allResultsRef" ref="allResultsRef"
> >
<!-- 快捷翻译 -->
<div class="translation" @click="toSearch(searchKeyword, 2)">
<SvgIcon iconName="icon-translation" />
<span class="text">快捷翻译{{ searchKeyword }}</span>
</div>
<!-- 建议 -->
<div <div
v-for="item in searchSuggestionsData" v-for="item in searchSuggestionsData"
class="s-result" class="s-result"
@ -39,47 +78,6 @@
</div> </div>
</div> </div>
</Transition> </Transition>
<!-- 无搜索建议 -->
<Transition name="fade" mode="out-in">
<div
v-show="searchKeywordType === 'text' && !hasSuggestions"
class="no-result"
>
<SvgIcon iconName="icon-found" className="not-found" />
<div class="all-text">
<span class="text">暂无搜索建议</span>
<span class="tip">请尝试其他关键词</span>
</div>
</div>
</Transition>
<!-- 特殊类型 -->
<Transition name="fade" mode="out-in">
<div
v-show="searchKeywordType !== 'text'"
class="special-result"
ref="specialResultsRef"
>
<!-- 直接访问 -->
<div
class="s-result"
@click="
toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4)
"
>
<SvgIcon iconName="icon-link" />
<span class="text">
{{
searchKeywordType === "email" ? "发送邮件至" : "直接访问"
}}{{ searchKeyword }}
</span>
</div>
<!-- 直接搜索 -->
<div class="s-result" @click="toSearch(searchKeyword, 1)">
<SvgIcon iconName="icon-search" />
<span class="text">搜索{{ searchKeyword }}</span>
</div>
</div>
</Transition>
</div> </div>
</Transition> </Transition>
</template> </template>
@ -101,11 +99,9 @@ const searchKeyword = ref(null);
const searchKeywordType = ref("text"); const searchKeywordType = ref("text");
// //
const searchSuggestionsData = ref([]); const searchSuggestionsData = ref([]);
//
const hasSuggestions = ref(true);
// //
const specialallResultsRef = ref(null);
const allResultsRef = ref(null); const allResultsRef = ref(null);
const specialResultsRef = ref(null);
// //
const suggestionsHeights = ref(0); const suggestionsHeights = ref(0);
// //
@ -120,45 +116,32 @@ const props = defineProps({
// //
const keywordsSearch = debounce((val) => { const keywordsSearch = debounce((val) => {
const searchValue = val?.trim(); const searchValue = val?.trim();
//
if (!searchValue || searchValue === "") {
searchKeyword.value = null;
return false;
}
//
searchKeyword.value = searchValue; searchKeyword.value = searchValue;
//
searchKeywordType.value = identifyInput(searchValue);
// //
if (searchKeywordType.value === "text") { if (searchKeyword.value && searchKeywordType.value === "text") {
if (searchValue) {
console.log(val + "的搜索建议"); console.log(val + "的搜索建议");
// //
searchSuggestionsData.value = [];
getSearchSuggestions(searchValue) getSearchSuggestions(searchValue)
.then((res) => { .then((res) => {
console.log(res); console.log(res);
//
hasSuggestions.value = res[0] ? true : false;
// //
searchSuggestionsData.value = Array.from(res); searchSuggestionsData.value = Array.from(res);
// //
nextTick(() => { nextTick(() => {
const height = allResultsRef.value?.offsetHeight; changeSuggestionsHeights();
suggestionsHeights.value = res[0] ? height : 130;
}); });
}) })
.catch((error) => { .catch((error) => {
//
searchSuggestionsData.value = [];
console.error("处理搜索建议发生错误:", error); console.error("处理搜索建议发生错误:", error);
}); });
} else {
searchKeyword.value = null;
hasSuggestions.value = true;
suggestionsHeights.value = 0;
}
}
//
else {
hasSuggestions.value = true;
//
nextTick(() => {
const height = specialResultsRef.value?.offsetHeight;
suggestionsHeights.value = height ?? 62;
});
} }
}, 300); }, 300);
@ -171,7 +154,7 @@ const keyboardEvents = (keyCode, event) => {
if (keyCode === 38 || keyCode === 40) { if (keyCode === 38 || keyCode === 40) {
// //
event.preventDefault(); event.preventDefault();
if (mainInput && allResultsRef.value && hasSuggestions.value) { if (mainInput && allResultsRef.value && searchSuggestionsData.value[0]) {
const suggestionItems = const suggestionItems =
allResultsRef.value.querySelectorAll(".s-result"); allResultsRef.value.querySelectorAll(".s-result");
if (suggestionItems.length > 0) { if (suggestionItems.length > 0) {
@ -208,6 +191,18 @@ const keyboardEvents = (keyCode, event) => {
} }
}; };
//
const changeSuggestionsHeights = () => {
try {
const allResultsHeight = allResultsRef.value?.offsetHeight;
const specialallResultsHeight = specialallResultsRef.value?.offsetHeight;
suggestionsHeights.value =
(specialallResultsHeight || 0) + (allResultsHeight || 0);
} catch (error) {
console.error("计算高度时出现错误:" + error);
}
};
// //
const toSearch = (val, type = 1) => { const toSearch = (val, type = 1) => {
emit("toSearch", val, type); emit("toSearch", val, type);
@ -216,7 +211,14 @@ const toSearch = (val, type = 1) => {
// //
watch( watch(
() => props.keyWord, () => props.keyWord,
(val) => keywordsSearch(val) (val) => {
//
searchSuggestionsData.value = [];
//
searchKeywordType.value = identifyInput(val);
//
keywordsSearch(val);
}
); );
// //
@ -224,7 +226,7 @@ defineExpose({ keyboardEvents });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.search-suggestions { .suggestions {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -233,13 +235,12 @@ defineExpose({ keyboardEvents });
overflow-y: hidden; overflow-y: hidden;
color: var(--main-text-color); color: var(--main-text-color);
background-color: var(--main-background-light-color); background-color: var(--main-background-light-color);
backdrop-filter: blur(10px); backdrop-filter: blur(30px) saturate(1.25);
border-radius: 16px; border-radius: 16px;
transition: height 0.25s ease, opacity 0.3s ease, transform 0.3s ease; transition: height 0.2s ease, opacity 0.3s ease, transform 0.3s ease;
.all-result, .all-result,
.special-result { .special-result {
.s-result, .s-result {
.translation {
cursor: pointer; cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
@ -263,37 +264,6 @@ defineExpose({ keyboardEvents });
background-color: var(--main-background-light-color); background-color: var(--main-background-light-color);
padding-left: 18px; padding-left: 18px;
} }
&:first-child {
border-radius: 16px 16px 0 0;
}
&:last-child {
border-radius: 0 0 16px 16px;
}
}
}
.no-result {
width: 100%;
height: 130px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.not-found {
font-size: 32px;
margin-bottom: 8px;
}
.all-text {
display: flex;
flex-direction: column;
.text {
font-size: 18px;
display: inline-block;
margin-bottom: 6px;
}
.tip {
font-size: 14px;
opacity: 0.8;
}
} }
} }
} }

View File

@ -44,15 +44,15 @@ body {
opacity: 0; opacity: 0;
} }
.fadeUp-enter-active, .fadeDown-enter-active,
.fadeUp-leave-active { .fadeDown-leave-active {
transition: opacity 0.3s ease, transform 0.3s ease; transition: opacity 0.3s ease, transform 0.3s ease;
} }
.fadeUp-enter-from, .fadeDown-enter-from,
.fadeUp-leave-to { .fadeDown-leave-to {
opacity: 0; opacity: 0;
transform: translateY(10px); transform: translateY(-10px);
} }
.show-enter-active, .show-enter-active,

File diff suppressed because one or more lines are too long