 b668d161a9
			
		
	
	b668d161a9
	
	
	
		
			
			* refactor(frontend): prefix css variables * `MI_UI` -> `MI` * fix * `stickyBottom` * stickyTop
		
			
				
	
	
		
			103 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <!--
 | |
| SPDX-FileCopyrightText: syuilo and misskey-project
 | |
| SPDX-License-Identifier: AGPL-3.0-only
 | |
| -->
 | |
| 
 | |
| <template>
 | |
| <div :class="$style.root" class="_gaps">
 | |
| 	<div
 | |
| 		ref="notesMainContainerEl"
 | |
| 		class="_gaps"
 | |
| 		:class="[$style.scrollBoxMain, { [$style.scrollIntro]: (scrollState === 'intro'), [$style.scrollLoop]: (scrollState === 'loop') }]"
 | |
| 		@animationend="changeScrollState"
 | |
| 	>
 | |
| 		<XNote v-for="note in notes" :key="`${note.id}_1`" :class="$style.note" :note="note"/>
 | |
| 	</div>
 | |
| 	<div v-if="isScrolling" class="_gaps" :class="[$style.scrollBoxSub, { [$style.scrollIntro]: (scrollState === 'intro'), [$style.scrollLoop]: (scrollState === 'loop') }]">
 | |
| 		<XNote v-for="note in notes" :key="`${note.id}_2`" :class="$style.note" :note="note"/>
 | |
| 	</div>
 | |
| </div>
 | |
| </template>
 | |
| 
 | |
| <script lang="ts" setup>
 | |
| import * as Misskey from 'misskey-js';
 | |
| import { onUpdated, ref, shallowRef } from 'vue';
 | |
| import XNote from '@/pages/welcome.timeline.note.vue';
 | |
| import { misskeyApiGet } from '@/scripts/misskey-api.js';
 | |
| import { getScrollContainer } from '@@/js/scroll.js';
 | |
| 
 | |
| const notes = ref<Misskey.entities.Note[]>([]);
 | |
| const isScrolling = ref(false);
 | |
| const scrollState = ref<null | 'intro' | 'loop'>(null);
 | |
| const notesMainContainerEl = shallowRef<HTMLElement>();
 | |
| 
 | |
| misskeyApiGet('notes/featured').then(_notes => {
 | |
| 	notes.value = _notes;
 | |
| });
 | |
| 
 | |
| function changeScrollState() {
 | |
| 	if (scrollState.value !== 'loop') {
 | |
| 		scrollState.value = 'loop';
 | |
| 	}
 | |
| }
 | |
| 
 | |
| onUpdated(() => {
 | |
| 	if (!notesMainContainerEl.value) return;
 | |
| 	const container = getScrollContainer(notesMainContainerEl.value);
 | |
| 	const containerHeight = container ? container.clientHeight : window.innerHeight;
 | |
| 	if (notesMainContainerEl.value.offsetHeight > containerHeight) {
 | |
| 		if (scrollState.value === null) {
 | |
| 			scrollState.value = 'intro';
 | |
| 		}
 | |
| 		isScrolling.value = true;
 | |
| 	}
 | |
| });
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" module>
 | |
| @keyframes scrollIntro {
 | |
| 	0% {
 | |
| 		transform: translate3d(0, 0, 0);
 | |
| 	}
 | |
| 	100% {
 | |
| 		transform: translate3d(0, calc(calc(-100% - 128px) - var(--MI-margin)), 0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| @keyframes scrollConstant {
 | |
| 	0% {
 | |
| 		transform: translate3d(0, -128px, 0);
 | |
| 	}
 | |
| 	100% {
 | |
| 		transform: translate3d(0, calc(calc(-100% - 128px) - var(--MI-margin)), 0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| .root {
 | |
| 	text-align: right;
 | |
| }
 | |
| 
 | |
| .scrollBoxMain {
 | |
| 	&.scrollIntro {
 | |
| 		animation: scrollIntro 30s linear forwards;
 | |
| 	}
 | |
| 	&.scrollLoop {
 | |
| 		animation: scrollConstant 30s linear infinite;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| .scrollBoxSub {
 | |
| 	&.scrollIntro {
 | |
| 		animation: scrollIntro 30s linear forwards;
 | |
| 	}
 | |
| 	&.scrollLoop {
 | |
| 		animation: scrollConstant 30s linear infinite;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| .root:has(.note:hover) .scrollBoxMain,
 | |
| .root:has(.note:hover) .scrollBoxSub {
 | |
| 	animation-play-state: paused;
 | |
| }
 | |
| </style>
 |