forked from cybrespace/pinafore
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
|
@ -9,3 +9,4 @@ node_modules
|
|||
/static/inline-script.js.map
|
||||
/static/emoji-mart-all.json
|
||||
/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-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-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",
|
||||
"page-lifecycle": "^0.1.1",
|
||||
"performance-now": "^2.1.0",
|
||||
"pinch-zoom-element": "^1.1.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"quick-lru": "^2.0.0",
|
||||
"remount": "^0.9.3",
|
||||
|
|
|
@ -11,44 +11,58 @@
|
|||
<div class="media-scroll-item">
|
||||
<div class="media-scroll-item-inner">
|
||||
<div class="media-scroll-item-inner-inner">
|
||||
<MediaInDialog {media} />
|
||||
<PinchZoomable className='media-pinch-zoom' disabled={!pinchZoomMode} >
|
||||
<MediaInDialog {media} />
|
||||
</PinchZoomable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if dots.length > 1}
|
||||
<div class="media-controls">
|
||||
<IconButton
|
||||
className="media-control-button"
|
||||
disabled={scrolledItem === 0}
|
||||
label="Show previous media"
|
||||
href="#fa-angle-left"
|
||||
on:click="prev()"
|
||||
/>
|
||||
{#each dots as dot, i (dot.i)}
|
||||
<IconButton
|
||||
className="media-control-button"
|
||||
pressable={true}
|
||||
label="Show {nth(i)} media"
|
||||
pressed={i === scrolledItem}
|
||||
href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
|
||||
sameColorWhenPressed={true}
|
||||
on:click="onClick(i)"
|
||||
/>
|
||||
{/each}
|
||||
<IconButton
|
||||
className="media-control-button"
|
||||
disabled={scrolledItem === length - 1}
|
||||
label="Show next media"
|
||||
href="#fa-angle-right"
|
||||
on:click="next()"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="media-controls-outside">
|
||||
<IconButton
|
||||
className="media-control-button media-control-button-dummy-spacer"
|
||||
href="#fa-search"
|
||||
/>
|
||||
{#if dots.length > 1}
|
||||
<div class="media-controls">
|
||||
<IconButton
|
||||
className="media-control-button"
|
||||
disabled={scrolledItem === 0}
|
||||
label="Show previous media"
|
||||
href="#fa-angle-left"
|
||||
on:click="prev()"
|
||||
/>
|
||||
{#each dots as dot, i (dot.i)}
|
||||
<IconButton
|
||||
className="media-control-button"
|
||||
pressable={true}
|
||||
label="Show {nth(i)} media"
|
||||
pressed={i === scrolledItem}
|
||||
href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
|
||||
sameColorWhenPressed={true}
|
||||
on:click="onClick(i)"
|
||||
/>
|
||||
{/each}
|
||||
<IconButton
|
||||
className="media-control-button"
|
||||
disabled={scrolledItem === length - 1}
|
||||
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>
|
||||
|
||||
|
||||
</ModalDialog>
|
||||
|
||||
<Shortcut scope='mediaDialog' key="ArrowLeft" on:pressed="prev()" />
|
||||
|
@ -92,14 +106,37 @@
|
|||
padding: 5px;
|
||||
}
|
||||
|
||||
.media-controls-outside {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.media-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
:global(.media-control-button) {
|
||||
margin: 0 5px;
|
||||
:global(.media-pinch-zoom) {
|
||||
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) {
|
||||
|
@ -129,6 +166,7 @@
|
|||
import MediaInDialog from './MediaInDialog.html'
|
||||
import IconButton from '../../IconButton.html'
|
||||
import Shortcut from '../../shortcut/Shortcut.html'
|
||||
import PinchZoomable from './PinchZoomable.html'
|
||||
import { show } from '../helpers/showDialog'
|
||||
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
|
||||
import debounce from 'lodash-es/debounce'
|
||||
|
@ -163,6 +201,9 @@
|
|||
popShortcutScope('mediaDialog')
|
||||
},
|
||||
store: () => store,
|
||||
data: () => ({
|
||||
pinchZoomMode: false
|
||||
}),
|
||||
computed: {
|
||||
length: ({ mediaItems }) => mediaItems.length,
|
||||
dots: ({ length }) => times(length, i => ({ i }))
|
||||
|
@ -171,7 +212,8 @@
|
|||
ModalDialog,
|
||||
MediaInDialog,
|
||||
IconButton,
|
||||
Shortcut
|
||||
Shortcut,
|
||||
PinchZoomable
|
||||
},
|
||||
helpers: {
|
||||
nth (i) {
|
||||
|
@ -229,6 +271,9 @@
|
|||
} else {
|
||||
scroller.scrollLeft = scrollLeft
|
||||
}
|
||||
},
|
||||
togglePinchZoomMode () {
|
||||
this.set({ pinchZoomMode: !this.get().pinchZoomMode })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
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:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||
|
|
Loading…
Reference in New Issue