feat: add pinch-zoom to media dialog (#933)
* feat: add pinch-zoom to media dialog * fix zoom buttons
This commit is contained in:
parent
4c430bd1c9
commit
6d2b3ec072
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,3 +9,4 @@ node_modules
|
||||||
/static/inline-script.js.map
|
/static/inline-script.js.map
|
||||||
/static/emoji-mart-all.json
|
/static/emoji-mart-all.json
|
||||||
/src/inline-script/checksum.js
|
/src/inline-script/checksum.js
|
||||||
|
yarn-error.log
|
||||||
|
|
|
@ -40,6 +40,7 @@ module.exports = [
|
||||||
{ id: 'fa-circle', src: 'src/thirdparty/font-awesome-svg-png/white/svg/circle.svg' },
|
{ id: 'fa-circle', src: 'src/thirdparty/font-awesome-svg-png/white/svg/circle.svg' },
|
||||||
{ id: 'fa-circle-o', src: 'src/thirdparty/font-awesome-svg-png/white/svg/circle-o.svg' },
|
{ id: 'fa-circle-o', src: 'src/thirdparty/font-awesome-svg-png/white/svg/circle-o.svg' },
|
||||||
{ id: 'fa-angle-left', src: 'src/thirdparty/font-awesome-svg-png/white/svg/angle-left.svg' },
|
{ id: 'fa-angle-left', src: 'src/thirdparty/font-awesome-svg-png/white/svg/angle-left.svg' },
|
||||||
{ id: 'fa-angle-right', src: 'src/thirdparty/font-awesome-svg-png/white/svg/angle-right.svg' }
|
{ id: 'fa-angle-right', src: 'src/thirdparty/font-awesome-svg-png/white/svg/angle-right.svg' },
|
||||||
|
{ id: 'fa-search-minus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search-minus.svg' },
|
||||||
|
{ id: 'fa-search-plus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search-plus.svg' }
|
||||||
]
|
]
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
"p-any": "^1.1.0",
|
"p-any": "^1.1.0",
|
||||||
"page-lifecycle": "^0.1.1",
|
"page-lifecycle": "^0.1.1",
|
||||||
"performance-now": "^2.1.0",
|
"performance-now": "^2.1.0",
|
||||||
|
"pinch-zoom-element": "^1.1.0",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"quick-lru": "^2.0.0",
|
"quick-lru": "^2.0.0",
|
||||||
"remount": "^0.9.3",
|
"remount": "^0.9.3",
|
||||||
|
|
|
@ -11,44 +11,58 @@
|
||||||
<div class="media-scroll-item">
|
<div class="media-scroll-item">
|
||||||
<div class="media-scroll-item-inner">
|
<div class="media-scroll-item-inner">
|
||||||
<div class="media-scroll-item-inner-inner">
|
<div class="media-scroll-item-inner-inner">
|
||||||
<MediaInDialog {media} />
|
<PinchZoomable className='media-pinch-zoom' disabled={!pinchZoomMode} >
|
||||||
|
<MediaInDialog {media} />
|
||||||
|
</PinchZoomable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if dots.length > 1}
|
<div class="media-controls-outside">
|
||||||
<div class="media-controls">
|
<IconButton
|
||||||
<IconButton
|
className="media-control-button media-control-button-dummy-spacer"
|
||||||
className="media-control-button"
|
href="#fa-search"
|
||||||
disabled={scrolledItem === 0}
|
/>
|
||||||
label="Show previous media"
|
{#if dots.length > 1}
|
||||||
href="#fa-angle-left"
|
<div class="media-controls">
|
||||||
on:click="prev()"
|
<IconButton
|
||||||
/>
|
className="media-control-button"
|
||||||
{#each dots as dot, i (dot.i)}
|
disabled={scrolledItem === 0}
|
||||||
<IconButton
|
label="Show previous media"
|
||||||
className="media-control-button"
|
href="#fa-angle-left"
|
||||||
pressable={true}
|
on:click="prev()"
|
||||||
label="Show {nth(i)} media"
|
/>
|
||||||
pressed={i === scrolledItem}
|
{#each dots as dot, i (dot.i)}
|
||||||
href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
|
<IconButton
|
||||||
sameColorWhenPressed={true}
|
className="media-control-button"
|
||||||
on:click="onClick(i)"
|
pressable={true}
|
||||||
/>
|
label="Show {nth(i)} media"
|
||||||
{/each}
|
pressed={i === scrolledItem}
|
||||||
<IconButton
|
href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
|
||||||
className="media-control-button"
|
sameColorWhenPressed={true}
|
||||||
disabled={scrolledItem === length - 1}
|
on:click="onClick(i)"
|
||||||
label="Show next media"
|
/>
|
||||||
href="#fa-angle-right"
|
{/each}
|
||||||
on:click="next()"
|
<IconButton
|
||||||
/>
|
className="media-control-button"
|
||||||
</div>
|
disabled={scrolledItem === length - 1}
|
||||||
{/if}
|
label="Show next media"
|
||||||
|
href="#fa-angle-right"
|
||||||
|
on:click="next()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<IconButton
|
||||||
|
className="media-control-button"
|
||||||
|
pressable={true}
|
||||||
|
pressed={pinchZoomMode}
|
||||||
|
label={pinchZoomMode ? 'Disable pinch-zoom mode' : 'Enable pinch-zoom mode'}
|
||||||
|
href="#fa-search"
|
||||||
|
on:click="togglePinchZoomMode()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
|
||||||
<Shortcut scope='mediaDialog' key="ArrowLeft" on:pressed="prev()" />
|
<Shortcut scope='mediaDialog' key="ArrowLeft" on:pressed="prev()" />
|
||||||
|
@ -92,14 +106,37 @@
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-controls-outside {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.media-controls {
|
.media-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 10px auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.media-control-button) {
|
:global(.media-pinch-zoom) {
|
||||||
margin: 0 5px;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.media-control-button-dummy-spacer) {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
:global(.media-control-button) {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 320px) {
|
||||||
|
:global(.icon-button.media-control-button) {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports (scroll-snap-align: start) {
|
@supports (scroll-snap-align: start) {
|
||||||
|
@ -129,6 +166,7 @@
|
||||||
import MediaInDialog from './MediaInDialog.html'
|
import MediaInDialog from './MediaInDialog.html'
|
||||||
import IconButton from '../../IconButton.html'
|
import IconButton from '../../IconButton.html'
|
||||||
import Shortcut from '../../shortcut/Shortcut.html'
|
import Shortcut from '../../shortcut/Shortcut.html'
|
||||||
|
import PinchZoomable from './PinchZoomable.html'
|
||||||
import { show } from '../helpers/showDialog'
|
import { show } from '../helpers/showDialog'
|
||||||
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
|
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
|
||||||
import debounce from 'lodash-es/debounce'
|
import debounce from 'lodash-es/debounce'
|
||||||
|
@ -163,6 +201,9 @@
|
||||||
popShortcutScope('mediaDialog')
|
popShortcutScope('mediaDialog')
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
|
data: () => ({
|
||||||
|
pinchZoomMode: false
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
length: ({ mediaItems }) => mediaItems.length,
|
length: ({ mediaItems }) => mediaItems.length,
|
||||||
dots: ({ length }) => times(length, i => ({ i }))
|
dots: ({ length }) => times(length, i => ({ i }))
|
||||||
|
@ -171,7 +212,8 @@
|
||||||
ModalDialog,
|
ModalDialog,
|
||||||
MediaInDialog,
|
MediaInDialog,
|
||||||
IconButton,
|
IconButton,
|
||||||
Shortcut
|
Shortcut,
|
||||||
|
PinchZoomable
|
||||||
},
|
},
|
||||||
helpers: {
|
helpers: {
|
||||||
nth (i) {
|
nth (i) {
|
||||||
|
@ -229,6 +271,9 @@
|
||||||
} else {
|
} else {
|
||||||
scroller.scrollLeft = scrollLeft
|
scroller.scrollLeft = scrollLeft
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
togglePinchZoomMode () {
|
||||||
|
this.set({ pinchZoomMode: !this.get().pinchZoomMode })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
115
src/routes/_components/dialog/components/PinchZoomable.html
Normal file
115
src/routes/_components/dialog/components/PinchZoomable.html
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<div class="pinch-zoom {disabled ? 'pinch-zoom-disabled' : ''} {className ? className : ''}" >
|
||||||
|
<pinch-zoom class="pinch-zoom-inner" ref:node>
|
||||||
|
<slot></slot>
|
||||||
|
</pinch-zoom>
|
||||||
|
<IconButton
|
||||||
|
className="pinch-zoom-button pinch-zoom-button-zoom-out"
|
||||||
|
muted={true}
|
||||||
|
label="Zoom out"
|
||||||
|
href="#fa-search-minus"
|
||||||
|
on:click="zoomOut()"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
className="pinch-zoom-button pinch-zoom-button-zoom-in"
|
||||||
|
muted={true}
|
||||||
|
label="Zoom in"
|
||||||
|
href="#fa-search-plus"
|
||||||
|
on:click="zoomIn()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.pinch-zoom {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.pinch-zoom-disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.pinch-zoom-inner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.icon-button.pinch-zoom-button) {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 110;
|
||||||
|
bottom: 10px;
|
||||||
|
background: var(--mask-opaque-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.pinch-zoom-button-zoom-in) {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.pinch-zoom-button-zoom-out) {
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
:global(.pinch-zoom-button-zoom-in) {
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.pinch-zoom-button-zoom-out) {
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 320px) {
|
||||||
|
:global(.icon-button.pinch-zoom-button) {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.pinch-zoom-disabled .pinch-zoom-button) {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import IconButton from '../../IconButton.html'
|
||||||
|
import 'pinch-zoom-element/dist/pinch-zoom.js'
|
||||||
|
import { observe } from 'svelte-extras'
|
||||||
|
|
||||||
|
const ZOOM_INCREMENT = 0.1
|
||||||
|
|
||||||
|
export default {
|
||||||
|
oncreate () {
|
||||||
|
this.observe('disabled', disabled => {
|
||||||
|
if (disabled) {
|
||||||
|
this.resetZoom()
|
||||||
|
}
|
||||||
|
}, { init: false })
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
disabled: false
|
||||||
|
}),
|
||||||
|
components: {
|
||||||
|
IconButton
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
observe,
|
||||||
|
zoomIn () {
|
||||||
|
this.zoomBy(ZOOM_INCREMENT)
|
||||||
|
},
|
||||||
|
zoomOut () {
|
||||||
|
this.zoomBy(-ZOOM_INCREMENT)
|
||||||
|
},
|
||||||
|
zoomBy (increment) {
|
||||||
|
let { node } = this.refs
|
||||||
|
let scale = node.scale || 1
|
||||||
|
node.scaleTo(scale + increment, {
|
||||||
|
originX: '50%',
|
||||||
|
originY: '50%'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetZoom () {
|
||||||
|
let { node } = this.refs
|
||||||
|
node.setTransform({
|
||||||
|
scale: 1,
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
12
yarn.lock
12
yarn.lock
|
@ -5604,6 +5604,13 @@ pify@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||||
|
|
||||||
|
pinch-zoom-element@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pinch-zoom-element/-/pinch-zoom-element-1.1.0.tgz#0e12c3f7bd63631e45596d1417a15759f80a4e9c"
|
||||||
|
integrity sha512-jzF7Uad61b1+BnaEktbtOl025WaLDbwVgpcG4qive1OLwuFNU2Itt2c5tD6pp1sx4XgKhLzqjVgQ9qmCr3y6Ew==
|
||||||
|
dependencies:
|
||||||
|
pointer-tracker "^2.0.3"
|
||||||
|
|
||||||
pinkie-promise@^1.0.0:
|
pinkie-promise@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-1.0.0.tgz#d1da67f5482563bb7cf57f286ae2822ecfbf3670"
|
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-1.0.0.tgz#d1da67f5482563bb7cf57f286ae2822ecfbf3670"
|
||||||
|
@ -5685,6 +5692,11 @@ pngjs@^3.3.1:
|
||||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
|
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
|
||||||
integrity sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==
|
integrity sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==
|
||||||
|
|
||||||
|
pointer-tracker@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/pointer-tracker/-/pointer-tracker-2.0.3.tgz#6e66be8330e9f4e83c81d76e1d78e42eb4f89c00"
|
||||||
|
integrity sha512-PURBF4oc45JPECuguX6oPL3pJU5AlF0Nb/4sZdmqzPNAkV4LGL9MJMqb0smWDtmQ0F0KpbxEJn4/Lf5ugN1keQ==
|
||||||
|
|
||||||
posix-character-classes@^0.1.0:
|
posix-character-classes@^0.1.0:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
|
|
Loading…
Reference in a new issue