mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-25 09:45:04 +02:00
wip
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user