1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-25 09:45:04 +02:00
This commit is contained in:
syuilo
2026-07-09 22:02:37 +09:00
parent cf3b206ef4
commit d5609abf25
3 changed files with 58 additions and 31 deletions

View File

@@ -12,17 +12,30 @@ SPDX-License-Identifier: AGPL-3.0-only
@pointerup="onPointerup"
@touchstart="onTouchstart"
@touchmove="onTouchmove"
@wheel="onWheel"
>
<div :style="{ transform: `translate3d(${translation.x}px, ${translation.y}px, 0)` }">
<img
ref="imageEl"
:class="$style.image"
:src="image.src"
v-if="!originalImageLoaded"
:class="[$style.image, $style.thumbnail]"
:src="image.thumbnailUrl"
:width="size.width"
:height="size.height"
draggable="false"
@wheel="onWheel"
>
<img
v-if="activated"
ref="imageEl"
:class="[$style.image, $style.original]"
:src="image.url"
:width="size.width"
:height="size.height"
draggable="false"
@load="originalImageLoaded = true"
>
</div>
<div v-if="activated && !originalImageLoaded" :class="$style.loading">
<MkLoading/>
</div>
</div>
</template>
@@ -34,9 +47,10 @@ import { i18n } from '@/i18n.js';
import { makeDoubleTapDetector } from '@/utility/double-tap.js';
import { beginAnimation, easing_easeInOutQuad } from '@/utility/animation.js';
type Image = {
export type Image = {
id: string;
src: string;
url: string;
thumbnailUrl: string;
width: number;
height: number;
sourceElement?: HTMLElement;
@@ -44,6 +58,7 @@ type Image = {
const props = withDefaults(defineProps<{
image: Image;
activated: boolean;
}>(), {
});
@@ -58,6 +73,8 @@ const emit = defineEmits<{
const rootEl = useTemplateRef('rootEl');
const imageEl = useTemplateRef('imageEl');
const originalImageLoaded = ref(false);
const padding = 30;
const ANIMATION_DURATION = 200;
@@ -217,7 +234,7 @@ let pointerVec = { x: 0, y: 0 };
function onPointerdown(ev: PointerEvent) {
pointerEventCache.set(ev.pointerId, ev);
imageEl.value.setPointerCapture(ev.pointerId);
rootEl.value.setPointerCapture(ev.pointerId);
isDragging = true;
lastX = ev.clientX;
@@ -298,7 +315,7 @@ function onPointermove(ev: PointerEvent) {
function onPointerup(ev: PointerEvent) {
pointerEventCache.delete(ev.pointerId);
imageEl.value.releasePointerCapture(ev.pointerId);
rootEl.value.releasePointerCapture(ev.pointerId);
prevTwoTouchPointsDistance = 0;
isDragging = false;
if (currentPointerId === ev.pointerId) {
@@ -396,5 +413,20 @@ onBeforeUnmount(() => {
display: block;
-webkit-touch-callout: none;
user-select: none;
position: absolute;
top: 0;
left: 0;
}
.loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
display: grid;
place-items: center;
pointer-events: none;
}
</style>

View File

@@ -8,9 +8,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="[$style.bg]"></div>
<div ref="mainEl" :class="$style.main">
<div ref="itemsEl" :class="$style.items" :style="{ left: `${imagesOffset}px` }">
<div v-for="image in images" :key="image.src" ref="itemEl" :class="$style.item">
<div v-for="(image, i) in images" :key="image.url" ref="itemEl" :class="$style.item">
<XItem
:image="image"
:activated="activatedIndexes.has(i)"
@close="onItemClose"
@horizontalSwipe="onHorizontalSwipe"
@prev="onPrev"
@@ -24,20 +25,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
import { nextTick, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
import XItem from './MkImageGallery.item.vue';
import type { Image } from './MkImageGallery.item.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { beginAnimation, easing_easeInOutQuad } from '@/utility/animation.js';
type Image = {
id: string;
src: string;
width: number;
height: number;
sourceElement?: HTMLElement;
};
const props = withDefaults(defineProps<{
defaultIndex?: number;
images: Image[];
@@ -48,15 +42,15 @@ const emit = defineEmits<{
(ev: 'closed'): void;
}>();
let currentIndex = props.defaultIndex ?? 0;
const activatedIndexes = ref(new Set<number>());
const currentIndex = ref(props.defaultIndex ?? 0);
watch(currentIndex, (newIndex) => {
activatedIndexes.value.add(newIndex);
}, { immediate: true });
const rootEl = useTemplateRef('rootEl');
const mainEl = useTemplateRef('mainEl');
const itemsEl = useTemplateRef('itemsEl');
const itemEl = useTemplateRef('itemEl');
const zIndex = os.claimZIndex('high');
const screenWidth = ref(window.innerWidth);
const imagesOffset = ref(currentIndex * -window.innerWidth);
const imagesOffset = ref(currentIndex.value * -window.innerWidth);
let currentScrollLeft = imagesOffset.value;
function onHorizontalSwipe(offset: number) {
@@ -64,10 +58,10 @@ function onHorizontalSwipe(offset: number) {
}
function scrollToCurrentIndex() {
currentScrollLeft = currentIndex * -screenWidth.value;
currentScrollLeft = currentIndex.value * -screenWidth.value;
beginAnimation({
from: { value: imagesOffset.value },
to: { value: currentIndex * -screenWidth.value },
to: { value: currentIndex.value * -screenWidth.value },
duration: 300,
easing: easing_easeInOutQuad,
apply: ({ value }) => {
@@ -81,15 +75,15 @@ function onCancelHorizontalSwipe() {
}
function onNext() {
if (currentIndex < props.images.length - 1) {
currentIndex++;
if (currentIndex.value < props.images.length - 1) {
currentIndex.value++;
}
scrollToCurrentIndex();
}
function onPrev() {
if (currentIndex > 0) {
currentIndex--;
if (currentIndex.value > 0) {
currentIndex.value--;
}
scrollToCurrentIndex();
}

View File

@@ -96,7 +96,8 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => {
async function openGallery(id: string) {
const images = props.mediaList.filter(media => previewable(media)).map(media => ({
id: media.id,
src: media.url,
url: media.url,
thumbnailUrl: media.thumbnailUrl,
width: media.properties.width ?? 0,
height: media.properties.height ?? 0,
sourceElement: gallery.value?.querySelector(`.image[data-id="${media.id}"]`) as HTMLElement | undefined,