前言
本以为不熟悉的nestjs会出的幺蛾子更大,没想到前端页面才更容易出幺蛾子
五花八门的问题
继上文我们拿到小说内容后,目前最大的问题就是小说的展示,翻页等功能
小说排版问题
-
使用 CSS 的
columns
属性实现内容分栏布局。保证容器高度占满屏幕,实现类似电子书的阅读效果。column-width
: 定义每列的宽度。column-gap
: 设置列间距。
-
如果希望实现平滑翻页,可以加上
transition: 0.4s ease-in
<template>
<div ref="wrapperRef" class="book-wrapper" >
<div ref="contentRef" class="book">
<div v-text="content" />
<div ref="endRef" class="book-end">
读完啦!
</div>
</div>
<div class="book-bottom flex items-center">
<div class="flex-1 text-right font-size-12 color-gray-7">
{{ current }}/{{ total }}
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.book-wrapper {
height: 100%;
width: 100dvw;
position: fixed;
inset: 0;
overflow: hidden;
}
.book {
columnGap: 16px;
columns: calc(100% - 32px) 1;
}
</style>
页数计算
- 根据容器宽度和endRef的
offsetLeft
计算总页数。 - 监听翻页操作(如点击或滑动事件),通过调整容器的
translateX
实现翻页。 - 利用变量current来存储当前页,动态计算
translateX
总页数是个很不稳定的因素,屏幕大小、字体、行高、间距等都会影响,没法作为进度存储,总体思路上还是通过current/total来存储进度的,在获取进度和提交进度是再根据当前计算的total*progress来换算,目前没有具体测试过这种方式的可行性,以及是否存在偏差
const width = wrapperRef.value?.clientWidth - configs.value.gap
let total = Math.ceil(endRef.value?.offsetLeft / width)
const translateX = computed(() => {
return (current.value - 1) * width * -1
})
点击、滑动事件监听
- 左右滑动翻页
- 上下滑动添加书签
let startX, endX, startY, endY
function onTouchStart(e) {
// 记录触摸开始的位置
startX = e.touches[0].clientX
startY = e.touches[0].clientY
}
function onTouchEnd(e) {
endX = e.changedTouches[0].clientX // 记录触摸结束的位置
endY = e.changedTouches[0].clientY
const diffX = startX - endX // 计算水平滑动的距离
const diffY = startY - endY // 计算水平滑动的距离
// 左右滑动翻页,上下滑动添加书签
if (Math.abs(diffX) >= Math.abs(diffY) && Math.abs(diffX) > 30) { // 设定滑动的最小距离(阈值)
next(diffX > 0 ? 1 : -1)
}
else if (Math.abs(diffX) < Math.abs(diffY) && Math.abs(diffY) > 30) {
onToggleMark()
}
}
- 点击左侧右侧翻页
- 点击中间展示底部设置栏顶部navbar
const bottomPopRef = ref()
const topPopRef = ref()
function onClick(e) {
const clickX = e.clientX // 获取点击的X坐标
const middle = wrapperRef.value.clientWidth / 2 // 页面中间的X坐标
if (clickX < middle / 2) {
next(-1)
}
else if (clickX > middle + middle / 2) {
// 点击在页面的右侧
next(1)
}
else {
// 点击在页面的中间
bottomPopRef.value.onToggle()
topPopRef.value.onToggle()
}
}
段落缩进问题
下载的小说,格式千奇百怪,为了符合自身的阅读习惯,开始思考如何让页面尽量整齐一些
- 为每段加上p标签,利用css属性
text-indent: 2em;
来实现首行缩进
content.value = content
.replace('<', '<')
.replace('>', '>')
.replace('script', '')
.split('\n')
.map(line => `<p>${line.replace(/^\s*/g, '')}</p>`)
.join('')
- 段落分割了,是不是章节也可以这么分割,顺便存储章节,方便后续快速选择
content.value = content
.replace('<', '<')
.replace('>', '>')
.replace('script', '')
.replace(/^(第\s*[一二三四五六七八九十百千万\d]+\s*[章回].*)$/gm, (match) => {
const chapter = { title: match, id: `chapter${chapters.value.length}` }
chapters.value.push(chapter)
return `<div class="chapter" id="${chapter.id}">${match}</div>`
})
.split('\n')
.map(line => `<p>${line.replace(/^\s*/g, '')}</p>`)
.join('')