feat: 'tag' outside the viewport is automatically moved to the viewable area
This commit is contained in:
parent
87d19b0446
commit
9362cb704f
@ -1,10 +1,19 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from "vue"
|
import { type PropType, computed, ref, watch, nextTick } from "vue"
|
||||||
|
import { RouterLink, useRoute } from "vue-router"
|
||||||
import { ElScrollbar } from "element-plus"
|
import { ElScrollbar } from "element-plus"
|
||||||
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue"
|
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue"
|
||||||
import { useSettingsStore } from "@/store/modules/settings"
|
import { useSettingsStore } from "@/store/modules/settings"
|
||||||
import Screenfull from "@/components/Screenfull/index.vue"
|
import Screenfull from "@/components/Screenfull/index.vue"
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tagRefs: {
|
||||||
|
type: Object as PropType<InstanceType<typeof RouterLink>[]>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
|
||||||
@ -20,6 +29,7 @@ const scroll = ({ scrollLeft }: { scrollLeft: number }) => {
|
|||||||
currentScrollLeft = scrollLeft
|
currentScrollLeft = scrollLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 鼠标滚轮滚动时触发 */
|
||||||
const wheelScroll = ({ deltaY }: WheelEvent) => {
|
const wheelScroll = ({ deltaY }: WheelEvent) => {
|
||||||
if (/^-/.test(deltaY.toString())) {
|
if (/^-/.test(deltaY.toString())) {
|
||||||
scrollTo("left")
|
scrollTo("left")
|
||||||
@ -28,24 +38,70 @@ const wheelScroll = ({ deltaY }: WheelEvent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 点击滚动 */
|
/** 获取可能需要的宽度 */
|
||||||
const scrollTo = (direction: "left" | "right") => {
|
const getWidth = () => {
|
||||||
let scrollLeft = 0
|
|
||||||
/** 可滚动内容的长度 */
|
/** 可滚动内容的长度 */
|
||||||
const scrollbarContentRefWidth = scrollbarContentRef.value!.clientWidth
|
const scrollbarContentRefWidth = scrollbarContentRef.value!.clientWidth
|
||||||
/** 滚动可视区宽度 */
|
/** 滚动可视区宽度 */
|
||||||
const scrollbarRefWidth = scrollbarRef.value!.wrapRef!.clientWidth
|
const scrollbarRefWidth = scrollbarRef.value!.wrapRef!.clientWidth
|
||||||
/** 最后剩余可滚动的宽度 */
|
/** 最后剩余可滚动的宽度 */
|
||||||
const lastDistance = scrollbarContentRefWidth - scrollbarRefWidth - currentScrollLeft
|
const lastDistance = scrollbarContentRefWidth - scrollbarRefWidth - currentScrollLeft
|
||||||
|
|
||||||
|
return { scrollbarContentRefWidth, scrollbarRefWidth, lastDistance }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 左右滚动 */
|
||||||
|
const scrollTo = (direction: "left" | "right", distance: number = translateDistance) => {
|
||||||
|
let scrollLeft = 0
|
||||||
|
const { scrollbarContentRefWidth, scrollbarRefWidth, lastDistance } = getWidth()
|
||||||
// 没有横向滚动条,直接结束
|
// 没有横向滚动条,直接结束
|
||||||
if (scrollbarRefWidth > scrollbarContentRefWidth) return
|
if (scrollbarRefWidth > scrollbarContentRefWidth) return
|
||||||
if (direction === "left") {
|
if (direction === "left") {
|
||||||
scrollLeft = Math.max(0, currentScrollLeft - translateDistance)
|
scrollLeft = Math.max(0, currentScrollLeft - distance)
|
||||||
} else {
|
} else {
|
||||||
scrollLeft = Math.min(currentScrollLeft + translateDistance, currentScrollLeft + lastDistance)
|
scrollLeft = Math.min(currentScrollLeft + distance, currentScrollLeft + lastDistance)
|
||||||
}
|
}
|
||||||
scrollbarRef.value!.setScrollLeft(scrollLeft)
|
scrollbarRef.value!.setScrollLeft(scrollLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 移动到目标位置 */
|
||||||
|
const moveTo = () => {
|
||||||
|
const tagRefs = props.tagRefs
|
||||||
|
for (let i = 0; i < tagRefs.length; i++) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (route.path === tagRefs[i].$props.to.path) {
|
||||||
|
// @ts-ignore
|
||||||
|
const el: HTMLElement = tagRefs[i].$el
|
||||||
|
const offsetWidth = el.offsetWidth
|
||||||
|
const offsetLeft = el.offsetLeft
|
||||||
|
const { scrollbarRefWidth } = getWidth()
|
||||||
|
// 当前 tag 在可视区域左边时
|
||||||
|
if (offsetLeft < currentScrollLeft) {
|
||||||
|
const distance = currentScrollLeft - offsetLeft
|
||||||
|
scrollTo("left", distance)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 当前 tag 在可视区域右边时
|
||||||
|
const width = scrollbarRefWidth + currentScrollLeft - offsetWidth
|
||||||
|
if (offsetLeft > width) {
|
||||||
|
const distance = offsetLeft - width
|
||||||
|
scrollTo("right", distance)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
nextTick(moveTo)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const showScreenfull = computed(() => {
|
const showScreenfull = computed(() => {
|
||||||
return settingsStore.showScreenfull
|
return settingsStore.showScreenfull
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getCurrentInstance, onMounted, ref, watch } from "vue"
|
import { getCurrentInstance, onMounted, ref, watch } from "vue"
|
||||||
import { type RouteRecordRaw, useRoute, useRouter } from "vue-router"
|
import { type RouteRecordRaw, RouterLink, useRoute, useRouter } from "vue-router"
|
||||||
import { type ITagView, useTagsViewStore } from "@/store/modules/tags-view"
|
import { type ITagView, useTagsViewStore } from "@/store/modules/tags-view"
|
||||||
import { usePermissionStore } from "@/store/modules/permission"
|
import { usePermissionStore } from "@/store/modules/permission"
|
||||||
import ScrollPane from "./ScrollPane.vue"
|
import ScrollPane from "./ScrollPane.vue"
|
||||||
@ -13,6 +13,8 @@ const route = useRoute()
|
|||||||
const tagsViewStore = useTagsViewStore()
|
const tagsViewStore = useTagsViewStore()
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
|
const tagRefs = ref<InstanceType<typeof RouterLink>[]>([])
|
||||||
|
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const top = ref(0)
|
const top = ref(0)
|
||||||
const left = ref(0)
|
const left = ref(0)
|
||||||
@ -161,8 +163,9 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="tags-view-container">
|
<div class="tags-view-container">
|
||||||
<ScrollPane class="tags-view-wrapper">
|
<ScrollPane class="tags-view-wrapper" :tagRefs="tagRefs">
|
||||||
<router-link
|
<router-link
|
||||||
|
ref="tagRefs"
|
||||||
v-for="tag in tagsViewStore.visitedViews"
|
v-for="tag in tagsViewStore.visitedViews"
|
||||||
:key="tag.path"
|
:key="tag.path"
|
||||||
:class="isActive(tag) ? 'active' : ''"
|
:class="isActive(tag) ? 'active' : ''"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user