This repository has been archived on 2020-11-22. You can view files and clone it, but cannot push or open issues/pull-requests.
mangareader/frontend/src/views/Reader.vue

220 lines
5.5 KiB
Vue

<template>
<div class="reader" v-shortkey="{left: ['arrowleft'], right: ['arrowright']}" @shortkey="navigation">
<div class="page-container">
<img ref="currentImage" :style="getRealPageStyle()" :src="info.pages[page]" @load="setScroll(); setOrientation()">
<!-- prefetching -->
<img style="display: none;" :src="info.pages[page-1]">
<img style="display: none;" :src="info.pages[page+1]">
</div>
<div class="sidebar" :style="{ display: showSidebar ? '' : 'none' }">
<h3>{{ info.title }}</h3>
<router-link :to="'/series/' + info.series">All Volumes</router-link>
<hr/>
<div class="sidebar-item">{{ page + 1 }}/{{ info.pages.length }}</div>
<div class="page-display-settings-wrapper">
<div class="page-display-settings-item">
<label for="page-width">Width</label>
<input id="page-width" v-model.number="pageDisplay.width" type="number" min="5" max="100" step="5">
</div>
<div class="page-display-settings-item">
<label for="page-height">Height</label>
<input id="page-height" v-model.number="pageDisplay.height" type="number" min="5" max="100" step="5">
</div>
<div>
<button class="sidebar-item" id="display-mode" v-on:click="cyclePageDisplayMode()">Fit: {{ pageDisplay.mode }}</button>
</div>
</div>
<button class="sidebar-item" v-on:click="changeDirection">{{ direction.toUpperCase() }}</button>
<button class="hide" v-on:click="showSidebar = false">Hide</button>
</div>
<div class="sidebar-enabler" :style="{ display: showSidebar ? 'none' : '' }" v-on:click="showSidebar = true"></div>
</div>
</template>
<script>
import API from '@/api-client.js'
export default {
name: 'Reader',
data () {
return {
info: {
pages: []
},
scrollPositions: [],
pageDisplay: {
width: 60,
height: 100,
mode: 'width'
},
showSidebar: true,
direction: 'rtl',
orientation: 'vertical'
}
},
computed: {
page: {
get () {
if (this.$route.hash) {
return this.$route.hash.substr(1) - 1
} else {
return 0
}
},
set (value) {
this.$router.push({ hash: '#' + (value + 1) })
}
}
},
mounted () {
API.getVolumeInfo(this.$route.params.id, info => (this.info = info))
if (localStorage.pageDisplay) {
this.pageDisplay = JSON.parse(localStorage.pageDisplay)
}
},
watch: {
pageDisplay: {
handler (newPageDisplay) {
localStorage.pageDisplay = JSON.stringify(newPageDisplay)
},
deep: true
}
},
methods: {
setPage (page) {
// save scroll position
this.scrollPositions[this.page] = window.pageYOffset
// set page
if (page >= 0 && page < this.info.pages.length) {
this.page = page
}
},
setScroll () {
// load saved scroll posotion, if available, reset to 0 otherwise
if (this.scrollPositions[this.page]) {
window.scrollTo(0, this.scrollPositions[this.page])
} else {
window.scrollTo(0, 0)
}
},
navigation (event) {
if ((event.srcKey === 'right' && this.direction === 'ltr') || (event.srcKey === 'left' && this.direction === 'rtl')) {
this.setPage(this.page + 1)
} else if ((event.srcKey === 'left' && this.direction === 'ltr') || (event.srcKey === 'right' && this.direction === 'rtl')) {
this.setPage(this.page - 1)
} else {
throw new Error('cannot nagivate: invalid parameters')
}
},
changeDirection () {
if (this.direction === 'rtl') {
this.direction = 'ltr'
} else if (this.direction === 'ltr') {
this.direction = 'rtl'
} else {
throw new Error('cannot change reading direction: invalid direction currently set')
}
},
setOrientation () {
const image = this.$refs.currentImage
if (image.width < image.height) {
this.orientation = 'vertical'
} else if (image.width > image.height) {
this.orientation = 'horizontal'
} else if (image.width === image.height) {
this.orientation = 'square'
}
},
getRealPageStyle () {
let width = 'auto'
let height = 'auto'
if (this.pageDisplay.mode === 'width') {
if (this.orientation === 'horizontal') {
width = '100%'
} else {
width = this.pageDisplay.width + '%'
}
} else if (this.pageDisplay.mode === 'height') {
height = this.pageDisplay.height + 'vh'
}
return {
width: width,
height: height
}
},
cyclePageDisplayMode () {
const pageDisplayModes = ['width', 'height', 'auto']
this.pageDisplay.mode = pageDisplayModes[(pageDisplayModes.indexOf(this.pageDisplay.mode) + 1) % pageDisplayModes.length]
}
}
}
</script>
<style scoped lang="scss">
.reader {
display: flex;
position: relative;
}
.page-container {
flex-grow: 1;
}
img {
vertical-align: top;
}
.sidebar-enabler {
position: absolute;
top: 0px;
right: 0px;
height: 100%;
width: 250px;
}
.sidebar {
flex: 0 0 250px;
background-color: #eee;
height: 100vh;
position: sticky;
top: 0;
}
.sidebar-item {
margin: 0.5em 0;
}
button.hide {
bottom: 1em;
right: 1em;
position: absolute;
}
.page-display-settings-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: center;
.page-display-settings-item {
margin: 10px;
width: 33%;
input {
width: 100%;
}
}
}
</style>