2、阅读进度
2.1 进度面板与分页逻辑
- 进度面板和之前的布局一样,建立一个新的EbookSettingProgress组件来写进度条
2.1.2 分页逻辑
- 当book已经全部加载完毕的时候利用promise来处理,book.locations.generate函数来处理分页,参数是每一页的字数
- 标准宽度是375的话,越大每一页字数就要越多,同时字体越大字数要越少(其实这里考虑得不是特别完善,比如还有图片、标题等情况未考虑进去)
this.book.ready.then(() => {
return this.book.locations.generate(750 * (window.innerWidth / 375) * (16 / getFontSize(this.fileName))).then((locations) => {
this.setBookAvailable(true)
//bookAvailable是看当前书本有没有全部解析完
})
})
2.2 进度拖拽功能
- 定义了一个全局变量progress来记录当前进度条的进度(这里的进度条是通过html自带的进度条实现的)也就是一个特殊的input,input的value就是进度条的进度
- 用book的locations的cfiFromPercentage函数获取当前百分比下的cfi(epubjs用来定位到任何一处的标识),找到cfi后直接调用rendition的display方法即可
onProgressChange (progress) {
// 设置全局变量progress
this.setProgress(progress).then(() => {
this.displayProgress()
})
}
displayProgress () {
const cfi = this.currentBook.locations.cfiFromPercentage(this.progress / 100)
this.currentBook.rendition.display(cfi)
}
- 同时拖拽要使得下方的进度条颜色也要随着变化,这里先删掉本css中关于progress的background的样式(主题css中已经设置过了),设置一个updateProgressBg函数:
updateProgressBg () {
this.$refs.progress.style.cssText = `background-size:${this.progress}% 100% !important`
}
- 一开始写的是下面这样,但是被主题css中的样式覆盖了,所以要用cssText来加上!important属性,优先级提升了
updateProgressBg () {
this.$refs.progress.style.backgroundSize = `${this.progress}% 100% `
}
- 但是这里一开始还未拖动的时候是全部黑色的,所以这里要在页面刷新完毕的时候就进行一次背景初始化,利用钩子函数updated,在里面调用更新函数updateProgressBg
2.3 上下章切换
2.3.1 上下章按钮功能实现
- 要切换上下章,必须要两个条件:
- 书本已经全部解析完毕(bookAvalaible为真)
- 切上一章时不能在第一章,切下一章时不能在最后一章(通过book.spine.length可以查询到全部的章数)
- 判断完两个条件后,设置全局属性section的值变化(加一或减一),.then中获取新一章节的内容(通过book.section(章节数来获取))
- 得到新章节信息为sectionInfo,sectionInfo里的href就为那一张所对应的url,直接调用rendition的display方法即可
- 以下一章节为例:
nextSection () {
if (this.section < this.currentBook.spine.length - 1 && this.bookAvailable) {
this.setSection(this.section + 1).then(() => {
const sectionInfo = this.currentBook.section(this.section)
if (sectionInfo && sectionInfo.href) {
this.currentBook.rendition.display(sectionInfo.href)
}
})
}
}
2.3.2 章节切换与进度条同步
- 同步的话也比较简单,就是在每次切换上下章完成之后then调用refreshLocation这个刷新函数
- refreshLocation里面是先获取当前的位置currentLocation,再获取当前的百分比设置好progress即可
refreshLocation () {
const currentLocation = this.currentBook.rendition.currentLocation()
this.setProgress(Math.floor(currentLocation.start.percentage * 100))
}
- 同时要在下方显示本章章节的名称,通过book.navigation.get(sectionInfo.href).label来获取章节名称,这个章节名称加在计算属性里,每次值变化的时候就会自动更新
computed: {
getSectionName () {
//这里要先判断一下,不然一开始书本还未解析完毕就会报错
if (this.bookAvailable) {
const sectionInfo = this.currentBook.section(this.section)
if (sectionInfo && sectionInfo.href) {
return this.currentBook.navigation.get(sectionInfo.href).label
}
}
return ''
}
}
2.4 保存阅读进度
-
影响到阅读进度主要有三种方式,这三种方式都要保存进度
- 翻页(prevPage、nextPage)
- 直接切换章节(section)
- 拖动进度条(progress)
-
就在上面提到的这些函数里面都调用saveProgress(这个是localStorage里的方法,保存到浏览器里),然后只需要在最开始渲染的时候跳到上次保存的那一页即可
-
开始渲染是用的rendition的display方法,没有进度的话就直接是display(),有的话就是display(进度),所以这里要自己封装一个display方法用来跳到指定的cfi,或者是初始页面
display (target, callback) {
if (target) {
this.currentBook.rendition.display(target).then(() => {
this.refreshLocation()
if (callback) callback()
})
} else {
this.currentBook.rendition.display().then(() => {
this.refreshLocation()
if (callback) callback()
})
}
}
-
display方法的作用上面也说了,在progress组件里有很多地方都需要跳到指定的位置(如切换章节、拖动进度条),所以将该方法放到全局(mixin)里
-
这里想说一下,book.js和mixin.js里都放有全局方法,但这两个文件还是有区别的:
- book里放的都是静态资源以及方法,如fontSize数组 添加css标签的函数等,都是在特定的场合需要静态方法或数据的时候才会调用;
- mixin里放的就是全局属性以及方法,只要一个方法需要在多个组件里调用的时候就可以将其放到文件里方便引入
-
开始的时候调用display函数就可以每次回到上次所在的位置,但是进度条还是在0的位置;明明在一开始的时候就调用了refreshLocation函数,结果发现调用函数的时候progress的值还为null,并没有被初始化,这是由于一开始分页算法还没有完成,所以要在分页完成之后再次调用该函数来初始化进度条的位置(分页的then里调用refreshLocation)
-
同时下面的章节名称也要在初始化的时候更新,这里可以在refreshLocation里根据currentLocation来获取currentLocation.start.index来获取当前章节,再调用vuex的setSection即可
2.5 阅读时间
-
阅读时间,要在载入阅读器开始就要计算,所以要在index.vue中添加计算时间的函数startLoopReadTime,添加定时器每一秒让时间加一,每30秒保存一次数据到localStorage,并在钩子函数mounted里调用该函数,在钩子函数beforeDestroy里取消定时器
-
阅读时间显示在进度条页面中,同时也是支持国际化的,在progress组件里设置一个getReadTimeText的函数,并且通过vue-i18n来获取文字,替换里面原有的$1为当前的阅读时间,要转化为分钟(向上取整)