Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions packages/web-app-preview/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@
v-else-if="activeMediaFileCached.isImage"
:file="activeMediaFileCached"
:current-image-rotation="currentImageRotation"
:current-image-zoom="currentImageZoom"
:current-image-position-x="currentImagePositionX"
:current-image-position-y="currentImagePositionY"
@pan-zoom-change="onPanZoomChanged"
/>
<media-video
v-else-if="activeMediaFileCached.isVideo"
Expand All @@ -61,9 +57,10 @@
:show-image-controls="activeMediaFileCached?.isImage && !activeMediaFileCached?.isError"
:show-delete-button="isDeleteButtonVisible"
:current-image-rotation="currentImageRotation"
:current-image-zoom="currentImageZoom"
@set-rotation="currentImageRotation = $event"
@set-zoom="currentImageZoom = $event"
@set-rotation-right="imageRotateRight"
@set-rotation-left="imageRotateLeft"
@set-zoom="imageZoom"
@set-shrink="imageShrink"
@reset-image="resetImage"
@toggle-full-screen="toggleFullScreenMode"
@toggle-previous="goToPrev"
Expand Down Expand Up @@ -209,14 +206,14 @@ export default defineComponent({

const files = props.activeFiles.filter((file) => {
if (
unref(props.currentFileContext.routeQuery)['q_share-visibility'] === 'hidden' &&
unref(props.currentFileContext.routeQuery)?.['q_share-visibility'] === 'hidden' &&
!(file as IncomingShareResource).hidden
) {
return false
}

if (
unref(props.currentFileContext.routeQuery)['q_share-visibility'] !== 'hidden' &&
unref(props.currentFileContext.routeQuery)?.['q_share-visibility'] !== 'hidden' &&
(file as IncomingShareResource).hidden
) {
return false
Expand Down Expand Up @@ -415,7 +412,6 @@ export default defineComponent({
this.isAutoPlayEnabled = false
}

this.currentImageZoom = 1
this.currentImageRotation = 0
}
},
Expand Down
50 changes: 11 additions & 39 deletions packages/web-app-preview/src/components/MediaControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
class="preview-controls-image-shrink raw-hover-surface p-1"
appearance="raw"
:aria-label="imageShrinkDescription"
@click="imageShrink"
@click="$emit('setShrink')"
>
<oc-icon fill-type="line" name="zoom-out" />
</oc-button>
Expand All @@ -60,7 +60,7 @@
class="preview-controls-image-zoom raw-hover-surface p-1"
appearance="raw"
:aria-label="imageZoomDescription"
@click="imageZoom"
@click="$emit('setZoom')"
>
<oc-icon fill-type="line" name="zoom-in" />
</oc-button>
Expand All @@ -71,7 +71,7 @@
class="preview-controls-rotate-left raw-hover-surface p-1"
appearance="raw"
:aria-label="imageRotateLeftDescription"
@click="imageRotateLeft"
@click="$emit('setRotationLeft')"
>
<oc-icon fill-type="line" name="anticlockwise" />
</oc-button>
Expand All @@ -80,7 +80,7 @@
class="preview-controls-rotate-right raw-hover-surface p-1"
appearance="raw"
:aria-label="imageRotateRightDescription"
@click="imageRotateRight"
@click="$emit('setRotationRight')"
>
<oc-icon fill-type="line" name="clockwise" />
</oc-button>
Expand Down Expand Up @@ -143,31 +143,25 @@ export default defineComponent({
type: Boolean,
default: true
},
currentImageZoom: {
type: Number,
default: 1
},
currentImageRotation: {
type: Number,
default: 0
}
},
emits: [
'setRotation',
'setRotationLeft',
'setRotationRight',
'setShrink',
'setZoom',
'toggleFullScreen',
'toggleNext',
'togglePrevious',
'resetImage',
'deleteResource'
],
setup(props, { emit }) {
setup(props) {
const { $gettext } = useGettext()

const currentZoomDisplayValue = computed(() => {
return `${(props.currentImageZoom * 100).toFixed(0)}%`
})

const ariaHiddenFileCount = computed(() => {
return $gettext('%{ displayIndex } of %{ availableMediaFiles }', {
displayIndex: (props.activeIndex + 1).toString(),
Expand All @@ -181,47 +175,25 @@ export default defineComponent({
})
})

const calculateZoom = (zoom: number, factor: number) => {
return Math.round(zoom * factor * 20) / 20
}
const imageShrink = () => {
emit('setZoom', Math.max(0.1, calculateZoom(props.currentImageZoom, 0.8)))
}
const imageZoom = () => {
const maxZoomValue = calculateZoom(9, 1.25)
emit('setZoom', Math.min(calculateZoom(props.currentImageZoom, 1.25), maxZoomValue))
}
const imageRotateLeft = () => {
emit('setRotation', props.currentImageRotation === -270 ? 0 : props.currentImageRotation - 90)
}
const imageRotateRight = () => {
emit('setRotation', props.currentImageRotation === 270 ? 0 : props.currentImageRotation + 90)
}

const resourceDeleteDescription = computed(() => {
return $gettext('Delete (%{key})', {
key: isMacOs() ? $gettext('⌘ + Backspace') : $gettext('Del')
})
})

return {
currentZoomDisplayValue,
screenreaderFileCount,
ariaHiddenFileCount,
resourceDeleteDescription,
enterFullScreenDescription: $gettext('Enter full screen mode'),
exitFullScreenDescription: $gettext('Exit full screen mode'),
imageShrinkDescription: $gettext('Shrink the image'),
imageZoomDescription: $gettext('Enlarge the image'),
imageShrinkDescription: $gettext('Shrink the image (⇧ + Mouse wheel)'),
imageZoomDescription: $gettext('Enlarge the image (⇧ + Mouse wheel)'),
imageResetDescription: $gettext('Reset'),
imageRotateLeftDescription: $gettext('Rotate the image 90 degrees to the left'),
imageRotateRightDescription: $gettext('Rotate the image 90 degrees to the right'),
previousDescription: $gettext('Show previous media file in folder'),
nextDescription: $gettext('Show next media file in folder'),
imageShrink,
imageZoom,
imageRotateLeft,
imageRotateRight
nextDescription: $gettext('Show next media file in folder')
}
}
})
Expand Down
154 changes: 94 additions & 60 deletions packages/web-app-preview/src/components/Sources/MediaImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@
:src="file.url"
:alt="file.name"
:data-id="file.id"
class="max-w-full max-h-full pt-4 cursor-move object-contain"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cursor-move object-contain are obsolete

:style="`transform: rotate(${currentImageRotation}deg)`"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed anymore

class="max-w-full max-h-full pt-4"
/>
</template>
<script lang="ts">
import { CachedFile } from '../../helpers/types'
import { defineComponent, PropType, onMounted, ref, watch, unref, nextTick } from 'vue'
import {
defineComponent,
PropType,
ref,
watch,
unref,
nextTick,
onMounted,
onBeforeUnmount
} from 'vue'
import type { PanzoomObject, PanzoomOptions } from '@panzoom/panzoom'
import Panzoom from '@panzoom/panzoom'
import { useEventBus } from '@opencloud-eu/web-pkg'

export default defineComponent({
name: 'MediaImage',
Expand All @@ -22,39 +31,68 @@ export default defineComponent({
type: Object as PropType<CachedFile>,
required: true
},
currentImageZoom: {
type: Number,
required: true
},
currentImageRotation: {
type: Number,
required: true
},
currentImagePositionX: {
type: Number,
required: true
},
currentImagePositionY: {
type: Number,
required: true
}
},
emits: ['panZoomChange'],
setup(props, { emit }) {
setup(props) {
const eventBus = useEventBus()

const img = ref<HTMLElement | null>()
const panzoom = ref<PanzoomObject>()
const resetEventToken = ref(null)
const zoomToken = ref(null)
const shrinkToken = ref(null)

const onWheelEvent = (e: WheelEvent) => {
e.preventDefault()
if (!e.shiftKey) {
return false
}

const onPanZoomChange = (event: Event) => {
emit('panZoomChange', event)
if (e.deltaY < 0) {
unref(panzoom).zoomIn({ step: 0.1 })
} else if (e.deltaY > 0) {
unref(panzoom).zoomOut({ step: 0.1 })
}
}

const setTransform = ({ scale, x, y }: { scale: number; x: number; y: number }) => {
let h: number
let v: number

switch (props.currentImageRotation) {
case -270:
case 90:
h = y
v = 0 - x
break
case -180:
case 180:
h = 0 - x
v = 0 - y
break
case -90:
case 270:
h = 0 - y
v = x
break
default:
h = x
v = y
}

unref(panzoom).setStyle(
'transform',
`rotate(${props.currentImageRotation}deg) scale(${scale}) translate(${h}px, ${v}px)`
)
}

const initPanzoom = async () => {
if (unref(panzoom)) {
await nextTick()
;(unref(img) as unknown as HTMLElement).removeEventListener(
'panzoomchange',
onPanZoomChange
)
;(unref(img) as unknown as HTMLElement).removeEventListener('wheel', onWheelEvent)
unref(panzoom)?.destroy()
}

Expand All @@ -65,50 +103,46 @@ export default defineComponent({
animate: false,
duration: 300,
overflow: 'auto',
minScale: 0.5,
maxScale: 10,
setTransform: (_, { scale, x, y }) => {
let h: number
let v: number

switch (props.currentImageRotation) {
case -270:
case 90:
h = y
v = 0 - x
break
case -180:
case 180:
h = 0 - x
v = 0 - y
break
case -90:
case 270:
h = 0 - y
v = x
break
default:
h = x
v = y
}

unref(panzoom).setStyle(
'transform',
`rotate(${props.currentImageRotation}deg) scale(${scale}) translate(${h}px, ${v}px)`
)
}
setTransform: (_, { scale, x, y }) => setTransform({ scale, x, y })
} as PanzoomOptions)
;(unref(img) as unknown as HTMLElement).addEventListener('panzoomchange', onPanZoomChange)
;(unref(img) as unknown as HTMLElement).addEventListener('wheel', onWheelEvent)
}

watch(img, initPanzoom)
onMounted(initPanzoom)
watch(() => props.file, initPanzoom, { immediate: true, deep: true })

watch(
() => props.currentImageRotation,
() => {
if (!unref(panzoom)) {
return
}

setTransform({
scale: unref(panzoom).getScale(),
x: unref(panzoom).getPan().x,
y: unref(panzoom).getPan().y
})
}
)

watch([() => props.currentImageZoom, () => props.currentImageRotation], () => {
unref(panzoom).zoom(props.currentImageZoom)
onMounted(() => {
resetEventToken.value = eventBus.subscribe('app.preview.media.image.reset', () =>
initPanzoom()
)
zoomToken.value = eventBus.subscribe('app.preview.media.image.zoom', () =>
unref(panzoom)?.zoomIn({ step: 0.1 })
)
shrinkToken.value = eventBus.subscribe('app.preview.media.image.shrink', () =>
unref(panzoom)?.zoomOut({ step: 0.1 })
)
})

watch([() => props.currentImagePositionX, () => props.currentImagePositionY], () => {
unref(panzoom).pan(props.currentImagePositionX, props.currentImagePositionY)
onBeforeUnmount(() => {
eventBus.unsubscribe('app.preview.media.image.reset', unref(resetEventToken))
eventBus.unsubscribe('app.preview.media.image.zoom', unref(zoomToken))
eventBus.unsubscribe('app.preview.media.image.shrink', unref(shrinkToken))
})

return {
Expand Down
Loading