From b346b995273d9d28ed11022c7929f2690bfd54a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sun, 9 Apr 2023 14:08:58 +0900 Subject: [PATCH] feat: impl IdleRender --- packages/frontend/.storybook/generate.tsx | 2 + .../components/MkAnalogClock.stories.impl.ts | 9 ++++ .../frontend/src/components/MkAnalogClock.vue | 46 +++++++++++-------- .../src/components/MkDigitalClock.vue | 13 +++--- .../frontend/src/components/global/MkTime.vue | 12 ++--- packages/frontend/src/scripts/idle-render.ts | 41 +++++++++++++++++ 6 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 packages/frontend/src/scripts/idle-render.ts 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();