summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAcid Chicken (硫酸鶏) <root@acid-chicken.com>2023-05-20 03:38:07 +0900
committerGitHub <noreply@github.com>2023-05-20 03:38:07 +0900
commitee3f408c7d25accb5812c4f442ba7f4531e4b681 (patch)
treeb3b3e170abf923127c9fa00e09de276ffe4fcb2e
parentdelete /docs (diff)
downloadmisskey-ee3f408c7d25accb5812c4f442ba7f4531e4b681.tar.gz
misskey-ee3f408c7d25accb5812c4f442ba7f4531e4b681.tar.bz2
misskey-ee3f408c7d25accb5812c4f442ba7f4531e4b681.zip
feat: impl IdlingRenderScheduler (#10547)
* feat: impl IdleRender * test: pin time on Chromatic * test: pin time on Chromatic * fix: typo * style: rename * style: rename * chore: back to setTimeout * style: linebreak * refactor: remove unused budget option * refactor: use raw unix time * fix: conflict error * fix: floor * fix: subtract * Revert "fix: subtract" This reverts commit 2ef4afaafc69d2fb8329b04c1b124dfa97b7e863. * Revert "fix: floor" This reverts commit bef8ecdf45c6afc52138921d16e2caca78cfd38d. * Revert "refactor: use raw unix time" This reverts commit 5199e13cb2829f3036101f95445cca3cb9c83703.
-rw-r--r--packages/frontend/.storybook/generate.tsx1
-rw-r--r--packages/frontend/src/components/MkAnalogClock.stories.impl.ts2
-rw-r--r--packages/frontend/src/components/MkAnalogClock.vue34
-rw-r--r--packages/frontend/src/components/MkDigitalClock.stories.impl.ts32
-rw-r--r--packages/frontend/src/components/MkDigitalClock.vue21
-rw-r--r--packages/frontend/src/components/global/MkTime.vue1
-rw-r--r--packages/frontend/src/scripts/idle-render.ts38
7 files changed, 100 insertions, 29 deletions
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index 7c51d4c00c..f442422109 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -397,6 +397,7 @@ function toStories(component: string): string {
Promise.all([
glob('src/components/global/*.vue'),
glob('src/components/Mk{A,B}*.vue'),
+ glob('src/components/MkDigitalClock.vue'),
glob('src/components/MkGalleryPostPreview.vue'),
glob('src/components/MkSignupServerRules.vue'),
glob('src/components/MkUserSetupDialog.vue'),
diff --git a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
index e7fbb47284..0aebdccf4f 100644
--- a/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
+++ b/packages/frontend/src/components/MkAnalogClock.stories.impl.ts
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
+import isChromatic from 'chromatic/isChromatic';
import MkAnalogClock from './MkAnalogClock.vue';
-import isChromatic from 'chromatic';
export const Default = {
render(args) {
return {
diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue
index f12020f810..05caffe7d0 100644
--- a/packages/frontend/src/components/MkAnalogClock.vue
+++ b/packages/frontend/src/components/MkAnalogClock.vue
@@ -39,6 +39,7 @@
-->
<line
+ ref="sLine"
:class="[$style.s, { [$style.animate]: !disableSAnimate && sAnimation !== 'none', [$style.elastic]: sAnimation === 'elastic', [$style.easeOut]: sAnimation === 'easeOut' }]"
:x1="5 - (0 * (sHandLengthRatio * handsTailLength))"
:y1="5 + (1 * (sHandLengthRatio * handsTailLength))"
@@ -73,9 +74,10 @@
</template>
<script lang="ts" setup>
-import { computed, onMounted, onBeforeUnmount } from 'vue';
+import { computed, onMounted, onBeforeUnmount, ref } from 'vue';
import tinycolor from 'tinycolor2';
import { globalEvents } from '@/events.js';
+import { defaultIdlingRenderScheduler } from '@/scripts/idle-render.js';
// https://stackoverflow.com/questions/1878907/how-can-i-find-the-difference-between-two-angles
const angleDiff = (a: number, b: number) => {
@@ -145,6 +147,7 @@ let mAngle = $ref<number>(0);
let sAngle = $ref<number>(0);
let disableSAnimate = $ref(false);
let sOneRound = false;
+const sLine = ref<SVGPathElement>();
function tick() {
const now = props.now();
@@ -160,17 +163,21 @@ function tick() {
}
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
mAngle = Math.PI * (m + s / 60) / 30;
- if (sOneRound) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
+ if (sOneRound && sLine.value) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
sAngle = Math.PI * 60 / 30;
- window.setTimeout(() => {
+ defaultIdlingRenderScheduler.delete(tick);
+ sLine.value.addEventListener('transitionend', () => {
disableSAnimate = true;
- window.setTimeout(() => {
+ requestAnimationFrame(() => {
sAngle = 0;
- window.setTimeout(() => {
+ requestAnimationFrame(() => {
disableSAnimate = false;
- }, 100);
- }, 100);
- }, 700);
+ if (enabled) {
+ defaultIdlingRenderScheduler.add(tick);
+ }
+ });
+ });
+ }, { once: true });
} else {
sAngle = Math.PI * s / 30;
}
@@ -194,20 +201,13 @@ function calcColors() {
calcColors();
onMounted(() => {
- const update = () => {
- if (enabled) {
- tick();
- window.setTimeout(update, 1000);
- }
- };
- update();
-
+ defaultIdlingRenderScheduler.add(tick);
globalEvents.on('themeChanged', calcColors);
});
onBeforeUnmount(() => {
enabled = false;
-
+ defaultIdlingRenderScheduler.delete(tick);
globalEvents.off('themeChanged', calcColors);
});
</script>
diff --git a/packages/frontend/src/components/MkDigitalClock.stories.impl.ts b/packages/frontend/src/components/MkDigitalClock.stories.impl.ts
new file mode 100644
index 0000000000..344f6de47c
--- /dev/null
+++ b/packages/frontend/src/components/MkDigitalClock.stories.impl.ts
@@ -0,0 +1,32 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import isChromatic from 'chromatic/isChromatic';
+import MkDigitalClock from './MkDigitalClock.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkDigitalClock,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkDigitalClock v-bind="props" />',
+ };
+ },
+ args: {
+ now: isChromatic() ? () => new Date('2023-01-01T10:10:30') : undefined,
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkDigitalClock>;
diff --git a/packages/frontend/src/components/MkDigitalClock.vue b/packages/frontend/src/components/MkDigitalClock.vue
index 278dc8a5e7..aea20f2489 100644
--- a/packages/frontend/src/components/MkDigitalClock.vue
+++ b/packages/frontend/src/components/MkDigitalClock.vue
@@ -11,19 +11,21 @@
</template>
<script lang="ts" setup>
-import { onUnmounted, ref, watch } from 'vue';
+import { onMounted, onUnmounted, ref, watch } from 'vue';
+import { defaultIdlingRenderScheduler } from '@/scripts/idle-render.js';
const props = withDefaults(defineProps<{
showS?: boolean;
showMs?: boolean;
offset?: number;
+ now?: () => Date;
}>(), {
showS: true,
showMs: false,
offset: 0 - new Date().getTimezoneOffset(),
+ now: () => new Date(),
});
-let intervalId;
const hh = ref('');
const mm = ref('');
const ss = ref('');
@@ -39,9 +41,9 @@ watch(showColon, (v) => {
}
});
-const tick = () => {
- const now = new Date();
- now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
+const tick = (): void => {
+ const now = props.now();
+ now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
hh.value = now.getHours().toString().padStart(2, '0');
mm.value = now.getMinutes().toString().padStart(2, '0');
ss.value = now.getSeconds().toString().padStart(2, '0');
@@ -52,13 +54,12 @@ const tick = () => {
tick();
-watch(() => props.showMs, () => {
- if (intervalId) window.clearInterval(intervalId);
- intervalId = window.setInterval(tick, props.showMs ? 10 : 1000);
-}, { immediate: true });
+onMounted(() => {
+ defaultIdlingRenderScheduler.add(tick);
+});
onUnmounted(() => {
- window.clearInterval(intervalId);
+ defaultIdlingRenderScheduler.delete(tick);
});
</script>
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 261cc0ee18..dfc3c89798 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -58,7 +58,6 @@ function tick() {
if (props.mode === 'relative' || props.mode === 'detail') {
tick();
-
onUnmounted(() => {
window.clearTimeout(tickId);
});
diff --git a/packages/frontend/src/scripts/idle-render.ts b/packages/frontend/src/scripts/idle-render.ts
new file mode 100644
index 0000000000..ccce8b02bf
--- /dev/null
+++ b/packages/frontend/src/scripts/idle-render.ts
@@ -0,0 +1,38 @@
+class IdlingRenderScheduler {
+ #renderers: Set<FrameRequestCallback>;
+ #rafId: number;
+ #ricId: number;
+
+ constructor() {
+ this.#renderers = new Set();
+ this.#rafId = 0;
+ this.#ricId = requestIdleCallback((deadline) => this.#schedule(deadline));
+ }
+
+ #schedule(deadline: IdleDeadline): void {
+ if (deadline.timeRemaining()) {
+ this.#rafId = requestAnimationFrame((time) => {
+ for (const renderer of this.#renderers) {
+ renderer(time);
+ }
+ });
+ }
+ this.#ricId = requestIdleCallback((arg) => this.#schedule(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 defaultIdlingRenderScheduler = new IdlingRenderScheduler();