diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index dd40bac2cc..3e9c048fae 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -397,6 +397,8 @@ function toStories(component: string): string {
// glob('src/{components,pages,ui,widgets}/**/*.vue')
Promise.all([
glob('src/components/global/*.vue'),
+ glob('src/components/MkAnalogClock.vue'),
+ glob('src/components/MkDigitalClock.vue'),
glob('src/components/MkGalleryPostPreview.vue'),
glob('src/pages/user/home.vue'),
])
diff --git a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
index 05190aa268..7552db90d9 100644
--- a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
+++ b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
+import isChromatic from 'chromatic';
import MkAnalogClock from './MkAnalogClock.vue';
export const Default = {
render(args) {
@@ -22,6 +23,14 @@ export const Default = {
template: '',
};
},
+ args: {
+ now: isChromatic() ? () => new Date('2023-01-01T10:10:30') : undefined,
+ },
+ decorators: [
+ () => ({
+ template: '
',
+ }),
+ ],
parameters: {
layout: 'fullscreen',
},
diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue
index 1218202616..7a5b4bec08 100644
--- a/packages/frontend/src/components/MkAnalogClock.vue
+++ b/packages/frontend/src/components/MkAnalogClock.vue
@@ -39,6 +39,7 @@
-->
diff --git a/packages/frontend/src/components/MkDigitalClock.vue b/packages/frontend/src/components/MkDigitalClock.vue
index 278dc8a5e7..f5b717c442 100644
--- a/packages/frontend/src/components/MkDigitalClock.vue
+++ b/packages/frontend/src/components/MkDigitalClock.vue
@@ -11,7 +11,8 @@
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 99169512db..b1fad81aba 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -11,6 +11,7 @@
import { onUnmounted } from 'vue';
import { i18n } from '@/i18n';
import { dateTimeFormat } from '@/scripts/intl-const';
+import { defaultIdleRender } from '@/scripts/idle-render.js';
const props = withDefaults(defineProps<{
time: Date | string | number | null;
@@ -45,21 +46,16 @@ const relative = $computed(() => {
i18n.ts._ago.future);
});
-let tickId: number;
-
-function tick() {
+function tick(): void {
now = props.origin ?? (new Date()).getTime();
- const ago = (now - _time) / 1000/*ms*/;
- const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;
-
- tickId = window.setTimeout(tick, next);
}
if (props.mode === 'relative' || props.mode === 'detail') {
tick();
+ defaultIdleRender.add(tick);
onUnmounted(() => {
- window.clearTimeout(tickId);
+ defaultIdleRender.delete(tick);
});
}
diff --git a/packages/frontend/src/scripts/idle-render.ts b/packages/frontend/src/scripts/idle-render.ts
new file mode 100644
index 0000000000..09a14cc68c
--- /dev/null
+++ b/packages/frontend/src/scripts/idle-render.ts
@@ -0,0 +1,41 @@
+// eslint-disable-next-line import/no-default-export
+export default class IdleRender {
+ #renderers: Set;
+ #budget: number;
+ #rafId: number;
+ #ricId: number;
+
+ constructor(budget = 0) {
+ this.#renderers = new Set();
+ this.#budget = budget;
+ this.#rafId = 0;
+ this.#ricId = requestIdleCallback((deadline) => this.#render(deadline));
+ }
+
+ #render(deadline: IdleDeadline): void {
+ if (deadline.timeRemaining() > this.#budget) {
+ this.#rafId = requestAnimationFrame((time) => {
+ for (const renderer of this.#renderers) {
+ renderer(time);
+ }
+ });
+ }
+ this.#ricId = requestIdleCallback((arg) => this.#render(arg));
+ }
+
+ add(renderer: FrameRequestCallback): void {
+ this.#renderers.add(renderer);
+ }
+
+ delete(renderer: FrameRequestCallback): void {
+ this.#renderers.delete(renderer);
+ }
+
+ dispose(): void {
+ this.#renderers.clear();
+ cancelAnimationFrame(this.#rafId);
+ cancelIdleCallback(this.#ricId);
+ }
+}
+
+export const defaultIdleRender = new IdleRender();