VueUse 的 useDark 每个实例挂载时都插一段 style、getComputedStyle 逼整页重算样式, 亮色下它的「没东西要改就跳过」永远不成立。提交列表每行的结果胶囊(useTone)都调它, 一页 30 行就是 30 次整页重算,翻页、换条再来。 改成 shared/composables/dark.ts 的 createGlobalState,auto-import 用 priority 盖掉 @vueuse/core 的同名项,显式 import 的 7 处一并换掉。 生产构建、CPU 降速 6 倍实测:提交列表首屏主线程 2015 → 1092ms(列表出来 1.34 → 0.85 秒), 排名 1950 → 850ms,比赛 1060 → 757ms;右栏换到没看过的一条不再每次卡 100ms。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
import { useDark } from 'shared/composables/dark'
|
|
|
|
/**
|
|
* 圆环从哪儿开始扩散。正常点击就用指针落点;键盘触发(Enter / 空格)时浏览器给的
|
|
* clientX/clientY 是 0,照用会让圆环从屏幕左上角冒出来——那种情况退回按钮自己的中心。
|
|
* `event.detail` 是点击次数,键盘触发时为 0,用它区分最省事。
|
|
*/
|
|
function revealOrigin(event: MouseEvent) {
|
|
if (event.detail > 0) return { x: event.clientX, y: event.clientY }
|
|
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()
|
|
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }
|
|
}
|
|
|
|
/**
|
|
* 暗黑模式切换 + 圆环扩散过渡。
|
|
*
|
|
* 从 Header 里搬出来的:这套动画细节和「顶栏该放什么」没有关系,
|
|
* 哪个页面想再放一个主题开关都能直接用。
|
|
*/
|
|
export function useDarkTransition() {
|
|
const isDark = useDark()
|
|
|
|
function toggleDark(event: MouseEvent) {
|
|
if (!document.startViewTransition) {
|
|
// View Transitions 要 Chrome 111,机房那批 105 没有,直接切、不做动画。
|
|
isDark.value = !isDark.value
|
|
return
|
|
}
|
|
const { x, y } = revealOrigin(event)
|
|
// 半径要取到**最远**那个角的距离。用 hypot(x, y) 只覆盖到左上角,
|
|
// 点在偏左上时右下角会有一块旧画面等圆环扩过去,看着像是没刷新。
|
|
const radius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y))
|
|
document
|
|
.startViewTransition(() => {
|
|
isDark.value = !isDark.value
|
|
})
|
|
.ready.then(() => {
|
|
document.documentElement.animate(
|
|
{
|
|
clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`],
|
|
},
|
|
{
|
|
duration: 400,
|
|
easing: 'ease-in-out',
|
|
pseudoElement: '::view-transition-new(root)',
|
|
},
|
|
)
|
|
})
|
|
.catch(() => {})
|
|
}
|
|
|
|
return { isDark, toggleDark }
|
|
}
|