fix(settings, top-bar): chromium transfrom (#1271)
Some checks are pending
CI / Test (lts/*, ubuntu-latest) (push) Waiting to run
CI / Test (lts/*, windows-latest) (push) Waiting to run
CI / Test (lts/-1, ubuntu-latest) (push) Waiting to run
CI / Test (lts/-1, windows-latest) (push) Waiting to run

Co-authored-by: Hakadao <a578457889743@gmail.com>
This commit is contained in:
star knight
2025-02-04 22:20:51 +08:00
committed by GitHub
parent eae2b58e4e
commit ebdad9c225
3 changed files with 210 additions and 14 deletions

View File

@@ -1,7 +1,9 @@
<script setup lang="ts">
import { onClickOutside } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { settings } from '~/logic'
import { createTransformer } from '~/utils/transformer'
import type { MenuItem } from './types'
import { MenuType } from './types'
@@ -21,6 +23,17 @@ const settingsMenu = {
}
const activatedMenuItem = ref<MenuType>(MenuType.General)
const title = ref<string>(t('settings.title'))
const window = ref<HTMLDivElement>()
createTransformer(window, {
x: '50%',
y: '50%',
notrigger: true,
centerTarget: {
x: true,
y: true,
},
})
const scrollbarRef = ref()
watch(
@@ -104,6 +117,8 @@ function setCurrentTitle() {
title.value = item.title
})
}
onClickOutside(window, handleClose)
</script>
<template>
@@ -112,11 +127,12 @@ function setCurrentTitle() {
class="fixed w-full h-full top-0 left-0"
@click="handleClose"
/>
<div
id="settings-window" pos="fixed top-1/2 left-1/2" w="90%" h="90%"
id="settings-window"
ref="window"
pos="fixed top-1/2 left-1/2" w="90%" h="90%"
max-w-1000px max-h-900px transform="~ translate-x--1/2 translate-y--1/2 gpu"
flex justify-between items-center
flex="~ justify-between items-center"
>
<aside
:class="{ group: !settings.touchScreenOptimization }"
@@ -169,7 +185,7 @@ function setCurrentTitle() {
--un-shadow: var(--bew-shadow-4), var(--bew-shadow-edge-glow-2);
backdrop-filter: var(--bew-filter-glass-2);
"
relative overflow="x-hidde" w-full h-full bg="$bew-elevated-alt"
relative overflow="x-hidden" w-full h-full bg="$bew-elevated-alt"
shadow rounded="$bew-radius" border="1 $bew-border-color" transform-gpu
>
<header

View File

@@ -10,6 +10,7 @@ import { settings } from '~/logic'
import api from '~/utils/api'
import { getUserID, isHomePage, isInIframe } from '~/utils/main'
import emitter from '~/utils/mitt'
import { createTransformer } from '~/utils/transformer'
import BewlyOrBiliPageSwitcher from './components/BewlyOrBiliPageSwitcher.vue'
import ChannelsPop from './components/ChannelsPop.vue'
@@ -50,8 +51,6 @@ const isLogin = ref<boolean>(true)
const logo = ref<HTMLElement>() as Ref<HTMLElement>
const avatarImg = ref<HTMLImageElement>() as Ref<HTMLImageElement>
const avatarShadow = ref<HTMLImageElement>() as Ref<HTMLImageElement>
const favoritesPopRef = ref<any>()
const momentsPopRef = ref()
const scrollTop = ref<number>(0)
const oldScrollTop = ref<number>(0)
@@ -198,7 +197,7 @@ const upload = setupTopBarItemHoverEvent('upload')
// More
const more = setupTopBarItemHoverEvent('more')
const topBarItemElements = {
const topBarItemElements: Record<keyof typeof popupVisible, Ref<HTMLElement | undefined>> = {
channels,
userPanel: avatar,
notifications,
@@ -207,6 +206,29 @@ const topBarItemElements = {
history,
watchLater,
upload,
more,
}
const channelsTransformer = setupTopBarItemTransformer('channels')
const avatarTransformer = setupTopBarItemTransformer('userPanel')
const notificationsTransformer = setupTopBarItemTransformer('notifications')
const momentsTransformer = setupTopBarItemTransformer('moments')
const favoritesTransformer = setupTopBarItemTransformer('favorites')
const historyTransformer = setupTopBarItemTransformer('history')
const watchLaterTransformer = setupTopBarItemTransformer('watchLater')
const uploadTransformer = setupTopBarItemTransformer('upload')
const moreTransformer = setupTopBarItemTransformer('more')
function setupTopBarItemTransformer(key: keyof typeof popupVisible) {
const transformer = createTransformer(topBarItemElements[key], {
x: '0px',
y: '60px',
centerTarget: {
x: true,
},
})
return transformer
}
function setupTopBarItemHoverEvent(key: keyof typeof popupVisible) {
@@ -280,8 +302,9 @@ watch(() => popupVisible.favorites, (newVal, oldVal) => {
return
if (newVal) {
nextTick(() => {
if (favoritesPopRef.value)
favoritesPopRef.value.refreshFavoriteResources()
if (favoritesTransformer.value)
// @ts-expect-error allow
favoritesTransformer.value.refreshFavoriteResources()
})
}
})
@@ -519,7 +542,8 @@ defineExpose({
<Transition name="slide-in">
<ChannelsPop
v-show="popupVisible.channels"
v-if="popupVisible.channels"
ref="channelsTransformer"
class="bew-popover"
pos="!left-0 !top-70px"
transform="!translate-x-0"
@@ -618,7 +642,7 @@ defineExpose({
<Transition name="slide-in">
<MomentsPop
v-show="popupVisible.moments"
ref="momentsPopRef"
ref="momentsTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -645,7 +669,7 @@ defineExpose({
<KeepAlive>
<FavoritesPop
v-if="popupVisible.favorites"
ref="favoritesPopRef"
ref="favoritesTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -672,6 +696,7 @@ defineExpose({
<Transition name="slide-in">
<HistoryPop
v-if="popupVisible.history"
ref="historyTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -697,6 +722,7 @@ defineExpose({
<Transition name="slide-in">
<WatchLaterPop
v-if="popupVisible.watchLater"
ref="watchLaterTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -733,6 +759,7 @@ defineExpose({
<Transition name="slide-in">
<MorePop
v-show="popupVisible.more"
ref="moreTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -768,6 +795,7 @@ defineExpose({
<Transition name="slide-in">
<UploadPop
v-if="popupVisible.upload"
ref="uploadTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -805,6 +833,7 @@ defineExpose({
<Transition name="slide-in">
<NotificationsPop
v-if="popupVisible.notifications"
ref="notificationsTransformer"
class="bew-popover"
@click.stop="() => {}"
/>
@@ -858,9 +887,10 @@ defineExpose({
<Transition name="slide-in">
<UserPanelPop
v-if="popupVisible.userPanel"
class="bew-popover"
ref="avatarTransformer"
:user-info="userInfo"
after:h="!0"
class="bew-popover"
pos="!left-auto !right-0" transform="!translate-x-0"
@click.stop="() => {}"
/>
@@ -891,7 +921,7 @@ defineExpose({
.slide-in-leave-to,
.slide-in-enter-from {
--uno: "transform important:translate-y-4 opacity-0";
--uno: "transform !top-full opacity-0";
}
.slide-out-enter-active,

150
src/utils/transformer.ts Normal file
View File

@@ -0,0 +1,150 @@
import type { MaybeElement } from '@vueuse/core'
import { unrefElement, useElementVisibility } from '@vueuse/core'
import type { CSSProperties } from 'vue'
interface TransformerCenter {
x?: boolean
y?: boolean
}
export interface Transformer {
x: number | string
y: number | string
centerTarget?: TransformerCenter
notrigger?: boolean
}
function checkChromium(): boolean {
return navigator.userAgent.includes('Chrome')
}
/**
* Covert transform to top and left style, if no chromium, use transform
* @param trigger
* @param transformer
*/
export function createTransformer(trigger: Ref<MaybeElement>, transformer: Transformer) {
const target = ref<MaybeElement>()
const isChromium = checkChromium()
const style = ref<CSSProperties>({})
watch(trigger, () => {
if (transformer.notrigger) {
target.value = unrefElement(trigger)
}
}, { immediate: true })
function update() {
let x = '0px'
let y = '0px'
if (typeof transformer.x === 'number') {
x = `${transformer.x}px`
}
else {
x = transformer.x
}
if (typeof transformer.y === 'number') {
y = `${transformer.y}px`
}
else {
y = transformer.y
}
if (target.value && transformer.centerTarget && isChromium) {
const el = unrefElement(target.value)
const targetRect = el!.getBoundingClientRect()
if (transformer.centerTarget.x) {
x = `calc(${transformer.x} - ${targetRect.width / 2}px)`
}
if (transformer.centerTarget.y) {
y = `calc(${transformer.y} - ${targetRect.height / 2}px)`
}
}
if (isChromium) {
style.value = {
// transition: 'none !important',
transform: 'none !important',
top: y,
left: x,
}
}
else {
// nothing, use inherit transform
// style.value = {
// transform: `translate(${x}, ${y})`,
// }
}
}
function generateStyle(originStyle: string | undefined | null): string {
const s = (originStyle || '')
.split(';')
.map((item) => {
const [key, value] = item.split(':').map(item => item.trim())
if (!key || !value) {
return {}
}
return {
[key]: value,
}
})
.reduce((acc, item) => {
return {
...acc,
...item,
}
}, {})
for (const key in style.value) {
// @ts-expect-error ignore
s[key] = style.value[key]
}
return Object.keys(s).map(key => `${key}:${s[key]}`).join(';')
}
if (isChromium) {
// v-show
const targetVisibility = useElementVisibility(() => unrefElement(target))
watch(targetVisibility, (visible) => {
if (visible) {
const targetElement = unrefElement(target)
if (targetElement) {
update()
const style = targetElement.getAttribute('style')
targetElement.setAttribute('style', generateStyle(style))
}
}
}, { flush: 'pre' })
// v-show
// useEventListener(() => unrefElement(target), 'transitionstart', () => {
// update()
// const style = unrefElement(target)?.getAttribute('style')
// unrefElement(target)?.setAttribute('style', generateStyle(style))
// })
// useEventListener(() => unrefElement(target), 'transitionend', () => {
// const style = unrefElement(target)?.getAttribute('style')
// unrefElement(target)?.setAttribute('style', generateStyle(style))
// })
// v-if
watch(() => unrefElement(target), (targetElement) => {
if (targetElement) {
update()
const style = targetElement.getAttribute('style')
targetElement.setAttribute('style', generateStyle(style))
}
}, { flush: 'pre' })
}
return target
}