summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2025-05-11 15:43:16 +0900
committerGitHub <noreply@github.com>2025-05-11 15:43:16 +0900
commit3df421da1a27d76f11f3b43c8b1fc06f98ccdad6 (patch)
tree5d5cf1133c154c55483acef26c193e772a5340ea
parent🎨 (diff)
downloadmisskey-3df421da1a27d76f11f3b43c8b1fc06f98ccdad6.tar.gz
misskey-3df421da1a27d76f11f3b43c8b1fc06f98ccdad6.tar.bz2
misskey-3df421da1a27d76f11f3b43c8b1fc06f98ccdad6.zip
refactor(frontend): MkMarquee のコードの可読性の向上 (#16017)
* fix(frontend/MkMarquee): Composition API に移行 * move animation to scoped environment * refactor --------- Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com>
-rw-r--r--packages/frontend/src/components/MkMarquee.vue112
-rw-r--r--packages/frontend/src/components/MkMarqueeText.vue89
-rw-r--r--packages/frontend/src/pages/welcome.entrance.a.vue6
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-federation.vue6
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-rss.vue6
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-user-list.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetRssTicker.vue6
7 files changed, 104 insertions, 127 deletions
diff --git a/packages/frontend/src/components/MkMarquee.vue b/packages/frontend/src/components/MkMarquee.vue
deleted file mode 100644
index 4a89d21b92..0000000000
--- a/packages/frontend/src/components/MkMarquee.vue
+++ /dev/null
@@ -1,112 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<script lang="ts">
-import { h, onMounted, onUnmounted, ref, watch } from 'vue';
-
-export default {
- name: 'MarqueeText',
- props: {
- duration: {
- type: Number,
- default: 15,
- },
- repeat: {
- type: Number,
- default: 2,
- },
- paused: {
- type: Boolean,
- default: false,
- },
- reverse: {
- type: Boolean,
- default: false,
- },
- },
- setup(props) {
- const contentEl = ref<HTMLElement>();
-
- function calc() {
- if (contentEl.value == null) return;
- const eachLength = contentEl.value.offsetWidth / props.repeat;
- const factor = 3000;
- const duration = props.duration / ((1 / eachLength) * factor);
-
- contentEl.value.style.animationDuration = `${duration}s`;
- }
-
- watch(() => props.duration, calc);
-
- onMounted(() => {
- calc();
- });
-
- onUnmounted(() => {
- });
-
- return {
- contentEl,
- };
- },
- render({
- $slots, $style, $props: {
- duration, repeat, paused, reverse,
- },
- }) {
- return h('div', { class: [$style.wrap] }, [
- h('span', {
- ref: 'contentEl',
- class: [
- paused
- ? $style.paused
- : undefined,
- $style.content,
- ],
- }, Array(repeat).fill(
- h('span', {
- class: $style.text,
- style: {
- animationDirection: reverse
- ? 'reverse'
- : undefined,
- },
- }, $slots.default()),
- )),
- ]);
- },
-};
-</script>
-
-<style lang="scss" module>
-.wrap {
- overflow: clip;
- animation-play-state: running;
-
- &:hover {
- animation-play-state: paused;
- }
-}
-.content {
- display: inline-block;
- white-space: nowrap;
- animation-play-state: inherit;
-}
-.text {
- display: inline-block;
- animation-name: marquee;
- animation-timing-function: linear;
- animation-iteration-count: infinite;
- animation-duration: inherit;
- animation-play-state: inherit;
-}
-.paused .text {
- animation-play-state: paused;
-}
-@keyframes marquee {
- 0% { transform:translateX(0); }
- 100% { transform:translateX(-100%); }
-}
-</style>
diff --git a/packages/frontend/src/components/MkMarqueeText.vue b/packages/frontend/src/components/MkMarqueeText.vue
new file mode 100644
index 0000000000..a2c365afe9
--- /dev/null
+++ b/packages/frontend/src/components/MkMarqueeText.vue
@@ -0,0 +1,89 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.wrap">
+ <span
+ ref="contentEl"
+ :class="[$style.content, {
+ [$style.paused]: paused,
+ [$style.reverse]: reverse,
+ }]"
+ >
+ <span v-for="key in repeat" :key="key" :class="$style.text">
+ <slot></slot>
+ </span>
+ </span>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, useTemplateRef, watch } from 'vue';
+
+const props = withDefaults(defineProps<{
+ duration?: number;
+ repeat?: number;
+ paused?: boolean;
+ reverse?: boolean;
+}>(), {
+ duration: 15,
+ repeat: 2,
+ paused: false,
+ reverse: false,
+});
+
+const contentEl = useTemplateRef('contentEl');
+
+function calcDuration() {
+ if (contentEl.value == null) return;
+ const eachLength = contentEl.value.offsetWidth / props.repeat;
+ const factor = 3000;
+ const duration = props.duration / ((1 / eachLength) * factor);
+ contentEl.value.style.animationDuration = `${duration}s`;
+}
+
+watch(() => props.duration, calcDuration);
+
+onMounted(calcDuration);
+</script>
+
+<style lang="scss" module>
+.wrap {
+ overflow: clip;
+ animation-play-state: running;
+
+ &:hover {
+ animation-play-state: paused;
+ }
+}
+
+.content {
+ display: inline-block;
+ white-space: nowrap;
+ animation-play-state: inherit;
+}
+
+.text {
+ display: inline-block;
+ animation-name: marquee;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-duration: inherit;
+ animation-play-state: inherit;
+}
+
+.paused .text {
+ animation-play-state: paused;
+}
+
+.reverse .text {
+ animation-direction: reverse;
+}
+
+@keyframes marquee {
+ 0% { transform: translateX(0); }
+ 100% { transform: translateX(-100%); }
+}
+</style>
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index d131c17340..c2cf937c71 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -17,13 +17,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkVisitorDashboard/>
</div>
<div v-if="instances && instances.length > 0" :class="$style.federation">
- <MarqueeText :duration="40">
+ <MkMarqueeText :duration="40">
<MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window">
<!--<MkInstanceCardMini :instance="instance"/>-->
<img v-if="instance.iconUrl" :class="$style.federationInstanceIcon" :src="getInstanceIcon(instance)" alt=""/>
<span class="_monospace">{{ instance.host }}</span>
</MkA>
- </MarqueeText>
+ </MkMarqueeText>
</div>
</div>
</template>
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
import XTimeline from './welcome.timeline.vue';
-import MarqueeText from '@/components/MkMarquee.vue';
+import MkMarqueeText from '@/components/MkMarqueeText.vue';
import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
import misskeysvg from '/client-assets/misskey.svg';
import { misskeyApiGet } from '@/utility/misskey-api.js';
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index 16e72fa227..7248e8826b 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="$style.transition_change_leaveTo"
mode="default"
>
- <MarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
+ <MkMarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
<span v-for="instance in instances" :key="instance.id" :class="[$style.item, { [$style.colored]: colored }]" :style="{ background: colored ? instance.themeColor : null }">
<img :class="$style.icon" :src="getInstanceIcon(instance)" alt=""/>
<MkA :to="`/instance-info/${instance.host}`" :class="$style.host" class="_monospace">
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
<span></span>
</span>
- </MarqueeText>
+ </MkMarqueeText>
</Transition>
</template>
<template v-else-if="display === 'oneByOne'">
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
-import MarqueeText from '@/components/MkMarquee.vue';
+import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { useInterval } from '@@/js/use-interval.js';
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue
index 4da89a181e..7db0d5267d 100644
--- a/packages/frontend/src/ui/_common_/statusbar-rss.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue
@@ -13,11 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="$style.transition_change_leaveTo"
mode="default"
>
- <MarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
+ <MkMarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
<span v-for="item in items" :class="$style.item">
<a :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a><span :class="$style.divider"></span>
</span>
- </MarqueeText>
+ </MkMarqueeText>
</Transition>
</template>
<template v-else-if="display === 'oneByOne'">
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
-import MarqueeText from '@/components/MkMarquee.vue';
+import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { useInterval } from '@@/js/use-interval.js';
import { shuffle } from '@/utility/shuffle.js';
diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
index c5bee51162..13139a1064 100644
--- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="$style.transition_change_leaveTo"
mode="default"
>
- <MarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
+ <MkMarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
<span v-for="note in notes" :key="note.id" :class="$style.item">
<img :class="$style.avatar" :src="note.user.avatarUrl" decoding="async"/>
<MkA :class="$style.text" :to="notePage(note)">
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
<span :class="$style.divider"></span>
</span>
- </MarqueeText>
+ </MkMarqueeText>
</Transition>
</template>
<template v-else-if="display === 'oneByOne'">
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
-import MarqueeText from '@/components/MkMarquee.vue';
+import MkMarqueeText from '@/components/MkMarqueeText.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { useInterval } from '@@/js/use-interval.js';
import { getNoteSummary } from '@/utility/get-note-summary.js';
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index b5be4d35c2..7fe7c6111a 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div v-else>
<Transition :name="$style.change" mode="default" appear>
- <MarqueeText :key="key" :duration="widgetProps.duration" :reverse="widgetProps.reverse">
+ <MkMarqueeText :key="key" :duration="widgetProps.duration" :reverse="widgetProps.reverse">
<span v-for="item in items" :key="item.link" :class="$style.item">
<a :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a><span :class="$style.divider"></span>
</span>
- </MarqueeText>
+ </MkMarqueeText>
</Transition>
</div>
</div>
@@ -31,7 +31,7 @@ import { ref, watch, computed } from 'vue';
import * as Misskey from 'misskey-js';
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
-import MarqueeText from '@/components/MkMarquee.vue';
+import MarqueeText from '@/components/MkMarqueeText.vue';
import type { GetFormResultType } from '@/utility/form.js';
import MkContainer from '@/components/MkContainer.vue';
import { shuffle } from '@/utility/shuffle.js';