Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zoom scrolling enhancement #36

Merged
merged 5 commits into from
Mar 14, 2019
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
143 changes: 140 additions & 3 deletions src/components/Images.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@

<template>
<img
:class="{
dragging,
zoomed: zoomRatio !== 1
}"
:src="data"
:style="{
height: height + 'px',
width: width + 'px'
height: zoomHeight + 'px',
width: zoomWidth + 'px',
marginTop: shiftY + 'px',
marginLeft: shiftX + 'px'
}"
@load="updateImgSize">
@load="updateImgSize"
@wheel="updateZoom"
@dblclick="resetZoom"
@mousedown="dragStart">
</template>

<script>
Expand All @@ -44,6 +53,22 @@ export default {
mixins: [
mime
],
data() {
return {
dragging: false,
shiftX: 0,
shiftY: 0,
zoomRatio: 1
}
},
computed: {
zoomHeight() {
return Math.round(this.height * this.zoomRatio)
},
zoomWidth() {
return Math.round(this.width * this.zoomRatio)
}
},
asyncComputed: {
data() {
if (this.mime !== 'image/svg+xml') {
Expand All @@ -52,10 +77,21 @@ export default {
return this.getBase64FromImage()
}
},
watch: {
active: function(val, old) {
// the item was hidden before and is now the current view
if (val === true && old === false) {
this.resetZoom()
}
}
},
mounted() {
// update image size on window resize
window.addEventListener('resize', debounce(() => {
this.updateImgSize()
}, 100))
// end the dragging if your mouse go out of the content
window.addEventListener('mouseout', this.dragEnd)
},
methods: {
// Updates the dimensions of the modal
Expand All @@ -80,6 +116,90 @@ export default {
async getBase64FromImage() {
const file = await axios.get(this.path)
return `data:${this.mime};base64,${btoa(file.data)}`
},

/**
* Handle zooming
*
* @param {Event} event the scroll event
* @returns {null}
*/
updateZoom(event) {
event.stopPropagation()
event.preventDefault()

// scrolling position relative to the image
const scrollX = event.clientX - this.$el.x - (this.width * this.zoomRatio / 2)
const scrollY = event.clientY - this.$el.y - (this.height * this.zoomRatio / 2)
const scrollPercX = Math.round(scrollX / (this.width * this.zoomRatio) * 100) / 100
const scrollPercY = Math.round(scrollY / (this.height * this.zoomRatio) * 100) / 100
const isZoomIn = event.deltaY < 0

const newZoomRatio = isZoomIn
? Math.min(this.zoomRatio + 0.1, 5) // prevent too big zoom
: Math.max(this.zoomRatio - 0.1, 1) // prevent too small zoom

// do not continue, img is back to its original state
if (newZoomRatio === 1) {
return this.resetZoom()
}

// calc how much the img grow from its current size
// and adjust the margin accordingly
const growX = this.width * newZoomRatio - this.width * this.zoomRatio
const growY = this.height * newZoomRatio - this.height * this.zoomRatio

// compensate for existing margins
this.disableSwipe()
this.shiftX = this.shiftX + Math.round(-scrollPercX * growX)
this.shiftY = this.shiftY + Math.round(-scrollPercY * growY)
this.zoomRatio = newZoomRatio
},

resetZoom() {
this.enableSwipe()
this.zoomRatio = 1
this.shiftX = 0
this.shiftY = 0
},

/**
* Dragging handlers
*
* @param {Event} event the event
*/
dragStart(event) {
event.preventDefault()
const { pageX, pageY } = event

this.dragX = pageX
this.dragY = pageY
this.dragging = true
this.$el.onmouseup = this.dragEnd
this.$el.onmousemove = this.dragHandler
},
dragEnd(event) {
event.preventDefault()

this.dragging = false
this.$el.onmouseup = null
this.$el.onmousemove = null
},
dragHandler(event) {
event.preventDefault()
const { pageX, pageY } = event

if (this.dragging && this.zoomRatio > 1 && pageX > 0 && pageY > 0) {
const moveX = this.shiftX + (pageX - this.dragX)
const moveY = this.shiftY + (pageY - this.dragY)
const growX = this.zoomWidth - this.width
const growY = this.zoomHeight - this.height

this.shiftX = Math.min(Math.max(moveX, -growX / 2), growX / 2)
this.shiftY = Math.min(Math.max(moveY, -growY / 2), growX / 2)
this.dragX = pageX
this.dragY = pageY
}
}
}
}
Expand All @@ -94,6 +214,11 @@ img {
max-height: 100%;
align-self: center;
justify-self: center;
// animate zooming/resize
transition: height 100ms ease,
width 100ms ease,
margin-top 100ms ease,
margin-left 100ms ease;
&:hover {
background-image: linear-gradient(45deg, #{$checkered-color} 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #{$checkered-color} 75%),
Expand All @@ -102,5 +227,17 @@ img {
background-size: 2 * $checkered-size 2 * $checkered-size;
background-position: 0 0, 0 0, -#{$checkered-size} -#{$checkered-size}, $checkered-size $checkered-size;
}
&.zoomed {
position: absolute;
max-height: none;
max-width: none;
z-index: 10010;
cursor: move;
}

&.dragging {
transition: none !important;
cursor: move;
}
}
</style>
10 changes: 10 additions & 0 deletions src/mixins/Mime.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export default {
mime: {
type: String,
required: true
},
canSwipe: {
type: Boolean,
default: true
}
},

Expand Down Expand Up @@ -93,6 +97,12 @@ export default {
this.width = contentWidth
}
}
},
enableSwipe() {
this.$emit('update:canSwipe', true)
},
disableSwipe() {
this.$emit('update:canSwipe', false)
}
}
}
7 changes: 5 additions & 2 deletions src/views/Viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
:has-previous="hasPrevious"
:has-next="hasNext"
:title="currentFileName"
:disable-swipe="disableSwipe"
:enable-swipe="canSwipe"
:size="isMobile ? 'full' : 'large'"
:style="{width: showSidebar ? `calc(100% - ${sidebarWidth}px)` : null}"
@close="close"
Expand Down Expand Up @@ -60,6 +60,7 @@
:mime="currentFile.mime"
:path="getPath(currentFile)"
:active="true"
:can-swipe.sync="canSwipe"
class="file-view"
@loaded="doneLoading"
@error="currentFailed" />
Expand Down Expand Up @@ -119,7 +120,7 @@ export default {
showSidebar: false,
sidebarWidth: 0,

disableSwipe: false,
canSwipe: true,
failed: false,
loading: true,

Expand Down Expand Up @@ -506,6 +507,8 @@ export default {
width: auto !important;
border-radius: 0 !important;
background-color: white;
justify-content: center;
align-items: center;
}

// dark bg while loading to avoid flashing white screen
Expand Down