From 4a941252f83202f08af3c7b44a98d96f8acda869 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 11 Jul 2026 08:50:03 +0900
Subject: [PATCH] wip
---
.../src/components/MkImageGallery.item.vue | 128 ++++++++++--------
.../src/components/MkImageGallery.vue | 48 +++----
.../frontend/src/components/MkMediaList.vue | 7 +-
3 files changed, 100 insertions(+), 83 deletions(-)
diff --git a/packages/frontend/src/components/MkImageGallery.item.vue b/packages/frontend/src/components/MkImageGallery.item.vue
index 1e2aeb38d5..866d52e3f8 100644
--- a/packages/frontend/src/components/MkImageGallery.item.vue
+++ b/packages/frontend/src/components/MkImageGallery.item.vue
@@ -29,36 +29,50 @@ SPDX-License-Identifier: AGPL-3.0-only
@transitioncancel.self="enableTransition = false"
>
-
+
+
+
+
+
-
+
- {{ image.comment ?? image.filename }}
+ {{ content.comment ?? content.filename }}
@@ -75,12 +89,13 @@ type Rect = Size & {
top: number;
};
-export type Image = {
+export type Content = {
id: string;
+ type: 'image' | 'video';
url: string;
thumbnailUrl: string | null;
- width: number;
- height: number;
+ width?: number | null;
+ height?: number | null;
filename?: string | null;
comment?: string | null;
sourceElement?: HTMLElement | null;
@@ -88,25 +103,25 @@ export type Image = {
export function calculateSourceTransform({
fit,
- imageRenderingRect,
+ contentRenderingRect,
sourceRect,
}: {
fit: string;
- imageRenderingRect: Rect;
+ contentRenderingRect: Rect;
sourceRect: Rect;
}): { x: number; y: number; scale: number } {
const scale = fit === 'cover'
- ? Math.max(sourceRect.width / imageRenderingRect.width, sourceRect.height / imageRenderingRect.height)
- : Math.min(sourceRect.width / imageRenderingRect.width, sourceRect.height / imageRenderingRect.height);
+ ? Math.max(sourceRect.width / contentRenderingRect.width, sourceRect.height / contentRenderingRect.height)
+ : Math.min(sourceRect.width / contentRenderingRect.width, sourceRect.height / contentRenderingRect.height);
- const sourceImageWidth = imageRenderingRect.width * scale;
- const sourceImageHeight = imageRenderingRect.height * scale;
- const sourceImageLeft = sourceRect.left + (sourceRect.width - sourceImageWidth) / 2;
- const sourceImageTop = sourceRect.top + (sourceRect.height - sourceImageHeight) / 2;
+ const sourceContentWidth = contentRenderingRect.width * scale;
+ const sourceContentHeight = contentRenderingRect.height * scale;
+ const sourceContentLeft = sourceRect.left + (sourceRect.width - sourceContentWidth) / 2;
+ const sourceContentTop = sourceRect.top + (sourceRect.height - sourceContentHeight) / 2;
return {
- x: sourceImageLeft - imageRenderingRect.left * scale,
- y: sourceImageTop - imageRenderingRect.top * scale,
+ x: sourceContentLeft - contentRenderingRect.left * scale,
+ y: sourceContentTop - contentRenderingRect.top * scale,
scale,
};
}
@@ -120,7 +135,7 @@ import { deviceKind } from '@/utility/device-kind.js';
import { isTouchUsing } from '@/utility/touch.js';
const props = withDefaults(defineProps<{
- image: Image;
+ content: Content;
activated: boolean;
}>(), {
});
@@ -135,10 +150,9 @@ const emit = defineEmits<{
const rootEl = useTemplateRef('rootEl');
const mainEl = useTemplateRef('mainEl');
-const imageEl = useTemplateRef('imageEl');
-const originalImageLoaded = ref(false);
-const thumbnailImageLoaded = ref(false);
+const originalContentLoaded = ref(false);
+const thumbnailContentLoaded = ref(false);
const enableTransition = ref(false);
const infoShowing = ref(false);
@@ -154,42 +168,44 @@ const headerAreaSize = 0;
const footerAreaSize = 30;
// maxからはみ出す場合は縮小、maxに満たない場合は拡大する(contain)
-function calcImageRenderingSize(image: Image) {
+function calcContentRenderingSize(content: Content) {
+ if (content.width == null || content.height == null || content.width === 0 || content.height === 0) return null;
+
const maxWidth = window.innerWidth - padding * 2;
const maxHeight = window.innerHeight - headerAreaSize - footerAreaSize - padding * 2;
- const widthRatio = maxWidth / image.width;
- const heightRatio = maxHeight / image.height;
+ const widthRatio = maxWidth / content.width;
+ const heightRatio = maxHeight / content.height;
const ratio = widthRatio < heightRatio ? widthRatio : heightRatio;
- const width = image.width * ratio;
- const height = image.height * ratio;
+ const width = content.width * ratio;
+ const height = content.height * ratio;
return { width, height };
}
-const imageRenderingSize = calcImageRenderingSize(props.image);
-const imageRenderingRect = {
- left: (window.innerWidth - imageRenderingSize.width) / 2,
- top: headerAreaSize + (window.innerHeight - headerAreaSize - footerAreaSize - imageRenderingSize.height) / 2,
- width: imageRenderingSize.width,
- height: imageRenderingSize.height,
-};
+const contentRenderingSize = calcContentRenderingSize(props.content);
+const contentRenderingRect = contentRenderingSize != null ? {
+ left: (window.innerWidth - contentRenderingSize.width) / 2,
+ top: headerAreaSize + (window.innerHeight - headerAreaSize - footerAreaSize - contentRenderingSize.height) / 2,
+ width: contentRenderingSize.width,
+ height: contentRenderingSize.height,
+} : null;
const transform = ref({ x: 0, y: 0, scale: 1 });
// 元のimg要素の位置・サイズ(とobject-fitの設定値)を取得して、そこからneutralの位置にアニメーションするためのscaleとtranslationを計算する
function getScaleAndTranslationForSourceElement(): { x: number; y: number; scale: number } {
- const sourceElement = props.image.sourceElement;
- if (sourceElement == null) return { x: 0, y: 0, scale: 1 };
+ const sourceElement = props.content.sourceElement;
+ if (sourceElement == null || contentRenderingRect == null) return { x: 0, y: 0, scale: 1 };
return calculateSourceTransform({
fit: window.getComputedStyle(sourceElement).objectFit,
- imageRenderingRect,
+ contentRenderingRect,
sourceRect: sourceElement.getBoundingClientRect(),
});
}
-if (props.image.sourceElement != null && props.activated) {
+if (props.content.sourceElement != null && props.activated) {
const sourceTransform = getScaleAndTranslationForSourceElement();
transform.value.scale = sourceTransform.scale;
transform.value.x = sourceTransform.x;
@@ -496,10 +512,10 @@ watch(isZooming, () => {
});
//#endregion
-watch(thumbnailImageLoaded, () => {
+watch(thumbnailContentLoaded, () => {
if (rootEl.value == null) return;
- const sourceElement = props.image.sourceElement;
+ const sourceElement = props.content.sourceElement;
if (sourceElement != null && props.activated) {
enableTransition.value = true;
rootEl.value.offsetHeight; // reflow
@@ -541,7 +557,7 @@ function onCLick() {
height: 100%;
}
-.image {
+.content {
display: block;
user-select: none;
position: absolute;
diff --git a/packages/frontend/src/components/MkImageGallery.vue b/packages/frontend/src/components/MkImageGallery.vue
index 12f57a8159..cebef46661 100644
--- a/packages/frontend/src/components/MkImageGallery.vue
+++ b/packages/frontend/src/components/MkImageGallery.vue
@@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only
-
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only