mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-01 19:03:40 +00:00
perf(Theme): add theme change animation (#356)
This commit is contained in:
parent
300ec27ec4
commit
b5c135b811
@ -48,8 +48,14 @@ limitations under the License. -->
|
||||
@input="changeTimeRange"
|
||||
/>
|
||||
<span> UTC{{ appStore.utcHour >= 0 ? "+" : "" }}{{ `${appStore.utcHour}:${appStore.utcMin}` }} </span>
|
||||
<span class="ml-5">
|
||||
<el-switch v-model="theme" :active-icon="Moon" :inactive-icon="Sunny" inline-prompt @change="changeTheme" />
|
||||
<span class="ml-5" ref="themeSwitchRef">
|
||||
<el-switch
|
||||
v-model="theme"
|
||||
:active-icon="Moon"
|
||||
:inactive-icon="Sunny"
|
||||
inline-prompt
|
||||
@change="handleChangeTheme"
|
||||
/>
|
||||
</span>
|
||||
<span title="refresh" class="ghost ml-5 cp" @click="handleReload">
|
||||
<Icon iconName="retry" :loading="appStore.autoRefresh" class="middle" />
|
||||
@ -67,18 +73,18 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { Themes } from "@/constants/data";
|
||||
import router from "@/router";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { MetricCatalog } from "@/views/dashboard/data";
|
||||
import type { DashboardItem } from "@/types/dashboard";
|
||||
import router from "@/router";
|
||||
import timeFormat from "@/utils/timeFormat";
|
||||
import { MetricCatalog } from "@/views/dashboard/data";
|
||||
import { ArrowRight, Moon, Sunny } from "@element-plus/icons-vue";
|
||||
import { Themes } from "@/constants/data";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
/*global Indexable */
|
||||
const { t, te } = useI18n();
|
||||
@ -89,6 +95,7 @@ limitations under the License. -->
|
||||
const timeRange = ref<number>(0);
|
||||
const pageTitle = ref<string>("");
|
||||
const theme = ref<boolean>(true);
|
||||
const themeSwitchRef = ref<HTMLElement>();
|
||||
|
||||
const savedTheme = window.localStorage.getItem("theme-is-dark");
|
||||
if (savedTheme === "false") {
|
||||
@ -119,6 +126,35 @@ limitations under the License. -->
|
||||
window.localStorage.setItem("theme-is-dark", String(theme.value));
|
||||
}
|
||||
|
||||
function handleChangeTheme() {
|
||||
const x = themeSwitchRef.value?.offsetLeft ?? 0;
|
||||
const y = themeSwitchRef.value?.offsetTop ?? 0;
|
||||
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
|
||||
// compatibility handling
|
||||
if (!document.startViewTransition) {
|
||||
changeTheme();
|
||||
return;
|
||||
}
|
||||
// api: https://developer.chrome.com/docs/web-platform/view-transitions
|
||||
const transition = document.startViewTransition(() => {
|
||||
changeTheme();
|
||||
});
|
||||
|
||||
transition.ready.then(() => {
|
||||
const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`];
|
||||
document.documentElement.animate(
|
||||
{
|
||||
clipPath: !theme.value ? clipPath.reverse() : clipPath,
|
||||
},
|
||||
{
|
||||
duration: 500,
|
||||
easing: "ease-in",
|
||||
pseudoElement: !theme.value ? "::view-transition-old(root)" : "::view-transition-new(root)",
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getName(list: any[]) {
|
||||
return list.find((d: any) => d.selected) || {};
|
||||
}
|
||||
|
@ -250,3 +250,27 @@ div:has(> a.menu-title) {
|
||||
.el-input-number .el-input__inner {
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
html {
|
||||
&::view-transition-old(root),
|
||||
&::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
&::view-transition-old(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
&::view-transition-new(root) {
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
|
||||
&::view-transition-old(root) {
|
||||
z-index: 999;
|
||||
}
|
||||
&::view-transition-new(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
9
src/types/global.d.ts
vendored
9
src/types/global.d.ts
vendored
@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type {
|
||||
ComponentPublicInstance,
|
||||
ComponentRenderProxy,
|
||||
FunctionalComponent,
|
||||
VNode,
|
||||
VNodeChild,
|
||||
ComponentPublicInstance,
|
||||
FunctionalComponent,
|
||||
PropType as VuePropType,
|
||||
} from "vue";
|
||||
|
||||
@ -39,6 +39,11 @@ declare global {
|
||||
lastBuildTime: string;
|
||||
};
|
||||
|
||||
// Document
|
||||
interface Document {
|
||||
startViewTransition(callback: () => void): any;
|
||||
}
|
||||
|
||||
// vue
|
||||
declare type PropType<T> = VuePropType<T>;
|
||||
declare type VueNode = VNodeChild | JSX.Element;
|
||||
|
Loading…
Reference in New Issue
Block a user