mirror of
https://github.com/gotson/komga.git
synced 2026-01-17 21:51:43 +01:00
Adding Scaling and padding to the Webreader's webtoon mode (#266)
* feat(webreader): adding scale type original to webtoon mode * fix(webreader): correcting scale type used for continuous reader * feat(webreader): adding setting for side padding to webtoon mode * feat(webreader): adding shortcut for side padding and new values * refactor: update padding percentage data type to number array * fix: correcting type issues after previous refactor * fix: images not showing if page has no dimension width and height on pages are not guaranteed * refactor: add new enum values to ScaleTypeText previous behavior worked only because the values of ScaleType and ContinuousScaleType are the same * refactor: simplify conditions fullWidthReader was only used for the continuous reader settings inside a reader type are not conditional * refactor: rearrange shortcuts continuous reader did not have specific settings this rearranges shortcuts for the help menu Co-authored-by: Gauthier Roebroeck <gauthier.roebroeck@gmail.com>
This commit is contained in:
parent
90e5fc945a
commit
6faeb2ab2e
7 changed files with 163 additions and 16 deletions
|
|
@ -7,9 +7,10 @@
|
|||
:key="`page${i}`"
|
||||
:alt="`Page ${page.number}`"
|
||||
:src="shouldLoad(i) ? page.url : undefined"
|
||||
:height="page.height / (page.width / $vuetify.breakpoint.width)"
|
||||
:width="$vuetify.breakpoint.width"
|
||||
:height="calcHeight(page)"
|
||||
:width="calcWidth(page)"
|
||||
:id="`page${page.number}`"
|
||||
style="margin: 0 auto;"
|
||||
v-intersect="onIntersect"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
import { ContinuousScaleType } from '@/types/enum-reader'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'ContinuousReader',
|
||||
|
|
@ -60,6 +62,14 @@ export default Vue.extend({
|
|||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
scale: {
|
||||
type: String as () => ContinuousScaleType,
|
||||
required: true,
|
||||
},
|
||||
sidePadding: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
pages: {
|
||||
|
|
@ -97,6 +107,9 @@ export default Vue.extend({
|
|||
if (this.animations) return undefined
|
||||
return { duration: 0 }
|
||||
},
|
||||
totalSidePadding (): number {
|
||||
return this.sidePadding * 2
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onScroll (e: any) {
|
||||
|
|
@ -114,6 +127,28 @@ export default Vue.extend({
|
|||
shouldLoad (page: number): boolean {
|
||||
return page == 0 || this.seen[page] || Math.abs((this.currentPage - 1) - page) <= 2
|
||||
},
|
||||
calcHeight (page: PageDtoWithUrl): number | undefined {
|
||||
switch (this.scale) {
|
||||
case ContinuousScaleType.WIDTH:
|
||||
if (page.height && page.width)
|
||||
return page.height / (page.width / (this.$vuetify.breakpoint.width - (this.$vuetify.breakpoint.width * this.totalSidePadding) / 100))
|
||||
return undefined
|
||||
case ContinuousScaleType.ORIGINAL:
|
||||
return page.height || undefined
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
calcWidth (page: PageDtoWithUrl): number | undefined {
|
||||
switch (this.scale) {
|
||||
case ContinuousScaleType.WIDTH:
|
||||
return this.$vuetify.breakpoint.width - (this.$vuetify.breakpoint.width * this.totalSidePadding) / 100
|
||||
case ContinuousScaleType.ORIGINAL:
|
||||
return page.width || undefined
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
centerClick () {
|
||||
this.$emit('menu')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { ScaleType } from '@/types/enum-reader'
|
||||
import { ContinuousScaleType, ScaleType } from '@/types/enum-reader'
|
||||
import { ReadingDirection } from '@/types/enum-books'
|
||||
|
||||
export const ScaleTypeText = {
|
||||
[ScaleType.SCREEN]: 'Fit screen',
|
||||
[ScaleType.HEIGHT]: 'Fit height',
|
||||
[ScaleType.WIDTH]: 'Fit width',
|
||||
[ContinuousScaleType.WIDTH]: 'Fit width',
|
||||
[ScaleType.ORIGINAL]: 'Original',
|
||||
[ContinuousScaleType.ORIGINAL]: 'Original',
|
||||
}
|
||||
|
||||
export const ReadingDirectionText = {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ export const shortcutsSettings = [
|
|||
new Shortcut('Webtoon',
|
||||
(ctx: any) => ctx.changeReadingDir(ReadingDirection.WEBTOON)
|
||||
, 'w'),
|
||||
new Shortcut('Toggle double pages',
|
||||
(ctx: any) => ctx.toggleDoublePages()
|
||||
, 'd'),
|
||||
new Shortcut('Cycle scale',
|
||||
(ctx: any) => ctx.cycleScale()
|
||||
, 'c'),
|
||||
|
|
|
|||
7
komga-webui/src/functions/shortcuts/continuous-reader.ts
Normal file
7
komga-webui/src/functions/shortcuts/continuous-reader.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { Shortcut } from '@/types/shortcuts'
|
||||
|
||||
export const shortcutsSettingsContinuous = [
|
||||
new Shortcut('Cycle side padding',
|
||||
(ctx: any) => ctx.cycleSidePadding()
|
||||
, 'p'),
|
||||
]
|
||||
|
|
@ -32,3 +32,9 @@ export const shortcutsVertical = [
|
|||
ctx.verticalNext()
|
||||
}, 'ArrowDown', '↓'),
|
||||
]
|
||||
|
||||
export const shortcutsSettingsPaged = [
|
||||
new Shortcut('Toggle double pages',
|
||||
(ctx: any) => ctx.toggleDoublePages()
|
||||
, 'd'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,3 +4,10 @@ export enum ScaleType {
|
|||
HEIGHT = 'height',
|
||||
ORIGINAL = 'original'
|
||||
}
|
||||
|
||||
export enum ContinuousScaleType {
|
||||
WIDTH = 'width',
|
||||
ORIGINAL = 'original'
|
||||
}
|
||||
|
||||
export const PaddingPercentage: number[] = [0, 5, 10, 15, 20, 25, 30, 35, 40]
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@
|
|||
:pages="pages"
|
||||
:page.sync="page"
|
||||
:animations="animations"
|
||||
:scale="continuousScale"
|
||||
:sidePadding="sidePadding"
|
||||
@menu="toggleToolbars()"
|
||||
@jump-previous="jumpToPrevious()"
|
||||
@jump-next="jumpToNext()"
|
||||
|
|
@ -162,6 +164,24 @@
|
|||
</settings-select>
|
||||
</v-list-item>
|
||||
|
||||
<div v-if="continuousReader">
|
||||
<v-subheader class="font-weight-black text-h6">Webtoon</v-subheader>
|
||||
<v-list-item>
|
||||
<settings-select
|
||||
:items="continuousScaleTypes"
|
||||
v-model="continuousScale"
|
||||
label="Scale type"
|
||||
/>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<settings-select
|
||||
:items="paddingPercentages"
|
||||
v-model="sidePadding"
|
||||
label="Side padding"
|
||||
/>
|
||||
</v-list-item>
|
||||
</div>
|
||||
|
||||
<div v-if="!continuousReader">
|
||||
<v-subheader class="font-weight-black text-h6">Paged</v-subheader>
|
||||
<v-list-item>
|
||||
|
|
@ -253,13 +273,21 @@ import Vue from 'vue'
|
|||
import { Location } from 'vue-router'
|
||||
import PagedReader from '@/components/readers/PagedReader.vue'
|
||||
import ContinuousReader from '@/components/readers/ContinuousReader.vue'
|
||||
import { ScaleType } from '@/types/enum-reader'
|
||||
import { ContinuousScaleType, PaddingPercentage, ScaleType } from '@/types/enum-reader'
|
||||
import { ReadingDirectionText, ScaleTypeText } from '@/functions/reader'
|
||||
import { shortcutsLTR, shortcutsRTL, shortcutsVertical } from '@/functions/shortcuts/paged-reader'
|
||||
import {
|
||||
shortcutsLTR,
|
||||
shortcutsRTL,
|
||||
shortcutsSettingsPaged,
|
||||
shortcutsVertical,
|
||||
} from '@/functions/shortcuts/paged-reader'
|
||||
import { shortcutsMenus, shortcutsSettings } from '@/functions/shortcuts/bookreader'
|
||||
import { shortcutsAll } from '@/functions/shortcuts/reader'
|
||||
import { shortcutsSettingsContinuous } from '@/functions/shortcuts/continuous-reader'
|
||||
|
||||
const cookieFit = 'webreader.fit'
|
||||
const cookieContinuousReaderFit = 'webreader.continuousReaderFit'
|
||||
const cookieContinuousReaderPadding = 'webreader.continuousReaderPadding'
|
||||
const cookieReadingDirection = 'webreader.readingDirection'
|
||||
const cookieDoublePages = 'webreader.doublePages'
|
||||
const cookieSwipe = 'webreader.swipe'
|
||||
|
|
@ -303,6 +331,8 @@ export default Vue.extend({
|
|||
swipe: true,
|
||||
animations: true,
|
||||
scale: ScaleType.SCREEN,
|
||||
continuousScale: ContinuousScaleType.WIDTH,
|
||||
sidePadding: 0,
|
||||
readingDirection: ReadingDirection.LEFT_TO_RIGHT,
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
|
|
@ -320,6 +350,14 @@ export default Vue.extend({
|
|||
text: ScaleTypeText[x],
|
||||
value: x,
|
||||
})),
|
||||
continuousScaleTypes: Object.values(ContinuousScaleType).map(x => ({
|
||||
text: ScaleTypeText[x],
|
||||
value: x,
|
||||
})),
|
||||
paddingPercentages: Object.values(PaddingPercentage).map(x => ({
|
||||
text: x === 0 ? 'None' : `${x}%`,
|
||||
value: x,
|
||||
})),
|
||||
backgroundColors: [
|
||||
{ text: 'White', value: 'white' },
|
||||
{ text: 'Black', value: 'black' },
|
||||
|
|
@ -332,7 +370,7 @@ export default Vue.extend({
|
|||
this.supportedMediaTypes.push('image/webp')
|
||||
}
|
||||
})
|
||||
this.shortcuts = this.$_.keyBy([...shortcutsSettings, ...shortcutsMenus, ...shortcutsAll], x => x.key)
|
||||
this.shortcuts = this.$_.keyBy([...shortcutsSettings, ...shortcutsSettingsPaged, ...shortcutsSettingsContinuous, ...shortcutsMenus, ...shortcutsAll], x => x.key)
|
||||
window.addEventListener('keydown', this.keyPressed)
|
||||
},
|
||||
async mounted () {
|
||||
|
|
@ -351,6 +389,12 @@ export default Vue.extend({
|
|||
this.loadFromCookie(cookieFit, (v) => {
|
||||
this.scale = v
|
||||
})
|
||||
this.loadFromCookie(cookieContinuousReaderFit, (v) => {
|
||||
this.continuousScale = v
|
||||
})
|
||||
this.loadFromCookie(cookieContinuousReaderPadding, (v) => {
|
||||
this.sidePadding = parseInt(v)
|
||||
})
|
||||
this.loadFromCookie(cookieBackground, (v) => {
|
||||
this.backgroundColor = v
|
||||
})
|
||||
|
|
@ -390,6 +434,12 @@ export default Vue.extend({
|
|||
continuousReader (): boolean {
|
||||
return this.readingDirection === ReadingDirection.WEBTOON
|
||||
},
|
||||
fullWidthReader (): boolean {
|
||||
if (this.continuousReader) {
|
||||
return this.continuousScale === ContinuousScaleType.WIDTH
|
||||
}
|
||||
return this.scale === ScaleType.WIDTH
|
||||
},
|
||||
progress (): number {
|
||||
return this.page / this.pagesCount * 100
|
||||
},
|
||||
|
|
@ -417,9 +467,15 @@ export default Vue.extend({
|
|||
default:
|
||||
nav.push(...shortcutsAll)
|
||||
}
|
||||
let settings = [...shortcutsSettings]
|
||||
if (this.continuousReader) {
|
||||
settings.push(...shortcutsSettingsContinuous)
|
||||
} else {
|
||||
settings.push(...shortcutsSettingsPaged)
|
||||
}
|
||||
return {
|
||||
'Reader Navigation': nav,
|
||||
'Settings': shortcutsSettings,
|
||||
'Settings': settings,
|
||||
'Menus': shortcutsMenus,
|
||||
}
|
||||
},
|
||||
|
|
@ -444,6 +500,28 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
},
|
||||
continuousScale: {
|
||||
get: function (): ContinuousScaleType {
|
||||
return this.settings.continuousScale
|
||||
},
|
||||
set: function (scale: ContinuousScaleType): void {
|
||||
if (Object.values(ContinuousScaleType).includes(scale)) {
|
||||
this.settings.continuousScale = scale
|
||||
this.$cookies.set(cookieContinuousReaderFit, scale, Infinity)
|
||||
}
|
||||
},
|
||||
},
|
||||
sidePadding: {
|
||||
get: function (): number {
|
||||
return this.settings.sidePadding
|
||||
},
|
||||
set: function (padding: number): void {
|
||||
if (PaddingPercentage.includes(padding)) {
|
||||
this.settings.sidePadding = padding
|
||||
this.$cookies.set(cookieContinuousReaderPadding, padding, Infinity)
|
||||
}
|
||||
},
|
||||
},
|
||||
backgroundColor: {
|
||||
get: function (): string {
|
||||
return this.settings.backgroundColor
|
||||
|
|
@ -590,12 +668,27 @@ export default Vue.extend({
|
|||
this.sendNotification(`Changing Reading Direction to: ${text}`)
|
||||
},
|
||||
cycleScale () {
|
||||
if (this.continuousReader) return
|
||||
const enumValues = Object.values(ScaleType)
|
||||
const i = (enumValues.indexOf(this.settings.scale) + 1) % (enumValues.length)
|
||||
this.scale = enumValues[i]
|
||||
const text = ScaleTypeText[this.scale]
|
||||
this.sendNotification(`Cycling Scale: ${text}`)
|
||||
if (this.continuousReader) {
|
||||
const enumValues = Object.values(ContinuousScaleType)
|
||||
const i = (enumValues.indexOf(this.settings.continuousScale) + 1) % (enumValues.length)
|
||||
this.continuousScale = enumValues[i]
|
||||
const text = ScaleTypeText[this.continuousScale]
|
||||
this.sendNotification(`Cycling Scale: ${text}`)
|
||||
} else {
|
||||
const enumValues = Object.values(ScaleType)
|
||||
const i = (enumValues.indexOf(this.settings.scale) + 1) % (enumValues.length)
|
||||
this.scale = enumValues[i]
|
||||
const text = ScaleTypeText[this.scale]
|
||||
this.sendNotification(`Cycling Scale: ${text}`)
|
||||
}
|
||||
},
|
||||
cycleSidePadding () {
|
||||
if (this.continuousReader) {
|
||||
const i = (PaddingPercentage.indexOf(this.settings.sidePadding) + 1) % (PaddingPercentage.length)
|
||||
this.sidePadding = PaddingPercentage[i]
|
||||
const text = this.sidePadding === 0 ? 'None' : `${this.sidePadding}%`
|
||||
this.sendNotification(`Cycling Side Padding: ${text}`)
|
||||
}
|
||||
},
|
||||
toggleDoublePages () {
|
||||
if (this.continuousReader) return
|
||||
|
|
|
|||
Loading…
Reference in a new issue