diff --git a/packages/frontend/src/components/MkImageGallery.item.vue b/packages/frontend/src/components/MkImageGallery.item.vue index 1d07b9a5b9..cb1d2a0248 100644 --- a/packages/frontend/src/components/MkImageGallery.item.vue +++ b/packages/frontend/src/components/MkImageGallery.item.vue @@ -48,6 +48,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { makeDoubleTapDetector } from '@/utility/double-tap.js'; import { beginAnimation, easing_easeInOutQuad } from '@/utility/animation.js'; +import { calculateSourceTransform } from '@/components/MkImageGallery.utils.js'; export type Image = { id: string; @@ -399,17 +400,18 @@ onBeforeUnmount(() => { // 元のimg要素の位置・サイズ(とobject-fitの設定値)を取得して、そこからneutralの位置にアニメーションするためのscaleとtranslationを計算する function getScaleAndTranslationForSourceElement(): { x: number; y: number; scale: number } { - const elementStyles = window.getComputedStyle(props.image.sourceElement); - const fit = elementStyles.objectFit; + const sourceElement = props.image.sourceElement; + if (sourceElement == null) return { x: 0, y: 0, scale: 1 }; - const sourceRect = props.image.sourceElement.getBoundingClientRect(); - if (fit === 'contain') { - // TODO - } else if (fit === 'cover') { - // TODO - } else { - // TODO - } + return calculateSourceTransform({ + fit: window.getComputedStyle(sourceElement).objectFit, + neutralSize, + sourceRect: sourceElement.getBoundingClientRect(), + viewportSize: { + width: window.innerWidth, + height: window.innerHeight, + }, + }); } onMounted(async () => { @@ -458,6 +460,6 @@ onMounted(async () => { } .transition { - transition: translate 200ms ease, width 200ms ease, height 200ms ease; + transition: translate 200ms ease, scale 200ms ease; } diff --git a/packages/frontend/src/components/MkImageGallery.utils.ts b/packages/frontend/src/components/MkImageGallery.utils.ts new file mode 100644 index 0000000000..001fbc6085 --- /dev/null +++ b/packages/frontend/src/components/MkImageGallery.utils.ts @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +type Size = { + width: number; + height: number; +}; + +type Rect = Size & { + left: number; + top: number; +}; + +export function calculateSourceTransform({ + fit, + neutralSize, + sourceRect, + viewportSize, +}: { + fit: string; + neutralSize: Size; + sourceRect: Rect; + viewportSize: Size; +}): { x: number; y: number; scale: number } { + const scale = fit === 'cover' + ? Math.max(sourceRect.width / neutralSize.width, sourceRect.height / neutralSize.height) + : Math.min(sourceRect.width / neutralSize.width, sourceRect.height / neutralSize.height); + + const neutralLeft = (viewportSize.width - neutralSize.width) / 2; + const neutralTop = (viewportSize.height - neutralSize.height) / 2; + const sourceImageWidth = neutralSize.width * scale; + const sourceImageHeight = neutralSize.height * scale; + const sourceImageLeft = sourceRect.left + (sourceRect.width - sourceImageWidth) / 2; + const sourceImageTop = sourceRect.top + (sourceRect.height - sourceImageHeight) / 2; + + return { + x: sourceImageLeft - neutralLeft * scale, + y: sourceImageTop - neutralTop * scale, + scale, + }; +}