以下功能包括,阅读epub电子书,上一张,下一章,左翻页,右翻页,目录,设置字体,背景色,书签等功能,使用uniapp框架开发,遇到许多问题,现在是一个完整的功能,以后用到可以使用。
html
<template>
<view>
<scroll-view scroll-y class="page DrawerPage" :class="modalName=='viewModal'?'show':''">
<cu-custom bgColor="bg-gradual-blue" :isBack="true" v-show="ifShowMenu">
<block slot="backText">返回</block>
<block slot="content">{{bookInfo.title}}</block>
</cu-custom>
<scroll-view scroll-y class="bg-white" style="padding-bottom: 80px;" :style="[{height:'calc(100vh - '+ this.CustomBar +'px + 50px)','background':themeList[defaultTheme].style.body.background}]">
<view id="book"></view>
</scroll-view>
<view style="padding: 0 20px 0;" class="cu-bar tabbar bg-white shadow foot" v-show="ifShowMenu">
<view class="action" @tap="prevPage">
<view>上一章</view>
</view>
<view class="action">
<text class="lg text-gray cuIcon-settings float-book-setting" style="font-size: 40upx;" @tap="setSettingRightPx" :class="{'float-book-setting-active':settingRightPx}"></text>
</view>
<view class="action" @tap="nextPage">
<view>下一章</view>
</view>
</view>
<uniTransition :show="settingRightPx" :modeClass="['slide-bottom']" :duration="500" @change="changeTools" class="setting-tools">
<view class="setting-content-view">
<text class="setting-font-size">大小:</text><slider class="setting-font-size-val" @change="intervalFontSizeChange" :value="interval" show-value min="10" max="22" />
</view>
<view class="setting-content-view">
<text class="setting-font-bg">背景:</text>
<view class="setting-font-bg-val">
<view class="text-center setting-font-bg-txt" :style="{'background':item.style.body.background}" @tap="setTheme(index)" v-for="(item,index) in themeList" :key="index"></view>
</view>
</view>
<button class="cu-btn block line-orange lg" @tap="showBookMarkList"><text class="cuIcon-list"></text> 书签</button>
<button class="cu-btn block line-orange lg" @tap="tabSelect" data-target="viewModal"><text class="cuIcon-list"></text> 目录</button>
<button class="cu-btn block bg-grey lg" @tap="setSettingRightPx"><text class="cuIcon-close"></text> 关闭</button>
</uniTransition>
</scroll-view>
<!-- 二级菜单 -->
<view class="DrawerClose" :class="modalName=='viewModal'?'show':''" @tap="hideModal">
<text class="cuIcon-pullright"></text>
</view>
<scroll-view scroll-y class="DrawerWindow" :class="modalName=='viewModal'?'show':''">
<view class="cu-list menu card-menu margin-top-xl margin-bottom-xl shadow-lg">
<view class="cu-item arrow" v-for="(item,index) in navigation" :key="index">
<view class="content" @tap="selectList(item)">
<text class="cuIcon-github text-grey"></text>
<text class="text-grey">{{item.label}}</text>
</view>
</view>
</view>
</scroll-view>
<!-- 书签弹框 -->
<view class="cu-modal" :class="bookMarkShow?'show':''">
<view class="cu-dialog">
<view class="cu-bar bg-white justify-end">
<view class="content">书签</view>
<view class="action" @tap="hideBookMark">
<text class="cuIcon-close text-red"></text>
</view>
</view>
<view class="padding-xl">
书签名:<input placeholder="请输入书签名" v-model="bookMarkName"></input>
</view>
<view class="cu-bar bg-white justify-end">
<view class="action">
<button class="cu-btn line-green text-green" @tap="hideBookMark()">取消</button>
<button class="cu-btn bg-green margin-left" @tap="saveBookMark()">确定</button>
</view>
</view>
</view>
</view>
<!-- 书签列表 -->
<view class="cu-modal drawer-modal justify-end" :class="bookMarkListShow?'show':''" @tap="hideBookMarkList()">
<view class="cu-dialog basis-lg" @tap.stop="" :style="[{top:CustomBar+'px',height:'calc(100vh - ' + CustomBar + 'px)'}]">
<view class="cu-list menu text-left">
<view class="cu-item arrow" v-for="(item,index) in bookMarkList" :key="index">
<view class="content">
<view @tap="toMark(item)">{{item.bookmarkname}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
js
<script>
import Epub from 'epubjs'
import uniTransition from "../../colorui/components/uni-transition.vue"
export default {
components: {uniTransition},
data() {
return {
contentHideHeight:'calc(100vh - '+ this.CustomBar +'px + 50px)',
contentShowHeight:'calc(100vh - '+ this.CustomBar +'px - 50px)',
ifShowMenu:true,
settingRightPx:false,
CustomBar: this.CustomBar,
interval: 16,
bookInfo:{},
showTopBar: true,
rendition: null,
defaultFontSize: 16,
defaultTheme: 3,
themeList: [
{
name: 'default',
style: {
body: {
'color': '#000', 'background': '#fff'
}
}
},
{
name: 'eye',
style: {
body: {
'color': '#000', 'background': '#ceeaba',
}
}
},
{
name: 'night',
style: {
body: {
'color': '#fff', 'background': '#000'
}
}
},
{
name: 'gold',
style: {
body: {
'color': '#000', 'background': 'rgb(241,236,226)',
}
}
}
],
modalName: null,
navigation:[],
locations:null,
bookMarkShow:false,
touchStartTime:0,
bookMarkName:"",
bookMarkListShow:false,
bookMarkList:[]
}
},
onReady:function(){
console.log("页面初始化");
},
onLoad: function(option) { //option为object类型,会序列化上个页面传递的参数
if(!option.bookId){
uni.showLoading({
title: '电子书加载异常'
});
return;
}
uni.showLoading({
title: '电子书加载中'
});
this.$http({
url:'/library/app/cnoocebook/bookInfo',
data:{"bookId":option.bookId},
method: "GET"
}).then(response=>{
uni.hideLoading();
if(response.data){
this.bookInfo=response.data;
this.readEBook();
}
}).catch(error=>{
uni.showToast({
icon: 'none',
title: error,
duration: 2000
});
});
},
methods: {
//阅读电子书
readEBook(){
//从本地缓存中获取字体大小设置
const fontSize=uni.getStorageSync('font_size');
this.defaultFontSize = fontSize?fontSize:16;
this.interval=this.defaultFontSize;
//从本地缓存中获取主题设置
const theme=uni.getStorageSync('default_theme')
this.defaultTheme = theme?theme:3;
let DOWNLOAD_URL = "https://xxxx/"+this.bookInfo.eid;
this.book = new Epub(DOWNLOAD_URL)
this.rendition = this.book.renderTo('book', {
//滚动翻页
// flow: "paginated",
flow: 'scrolled-doc',
width: window.innerWidth,
height: window.innerHeight,
method: 'default',
restore: false
});
this.rendition.display();
//内容加载完后进行回调
this.rendition.hooks.content.register((contents) => {
uni.hideLoading();
this.scalePage(contents.content);
});
//图书解析完毕回调
this.book.ready.then(() => {
// 生成目录
this.navigation = this.book.navigation.toc;
console.log(this.navigation);
// 生成Locations对象
let generate = this.book.locations.generate()
console.log(generate);
return generate;
}).then(result => {
// 保存locations对象
this.locations = this.book.locations;
console.log(this.locations);
})
// 获取Theme对象
this.themes = this.rendition.themes
// 设置默认字体
this.setFontSize(this.defaultFontSize)
//初始化主题
this.registerTheme()
// 设置默认主题
this.setTheme(this.defaultTheme)
//左右滑动翻页点击开始
this.rendition.on('touchstart', e => {
this.touchStartX = e.changedTouches[0].pageX;
this.touchStartY = e.changedTouches[0].pageY;
this.touchStartTime = e.timeStamp;
});
//左右翻页,显示隐藏操作菜单,显示书签弹框
this.rendition.on('touchend', e => {
const offsetX = e.changedTouches[0].pageX - this.touchStartX
const offsetY = e.changedTouches[0].pageY - this.touchStartY
const time = event.timeStamp - this.touchStartTime
if (time<500&&Math.abs(offsetX) > (Math.abs(offsetY)+50) && offsetX > 100) {
this.prevPage();
} else if (time<500&&Math.abs(offsetX) > (Math.abs(offsetY)+50) && offsetX < -100) {
this.nextPage();
}else if(time<500&&offsetX==0 && offsetY == 0){
this.showMenu();
}else if(time>=500){
this.bookMarkShow=true;
}
});
},
//设置页面中图片的大小
scalePage(body){
let images = body.getElementsByTagName("img");
let iheight = body.clientHeight,oheight;
for (let i = 0; i < images.length; i++) {
images[i].style.width='100%';
}
},
//设置按钮显示隐藏
setSettingRightPx(){
if(this.settingRightPx){
this.settingRightPx=false;
}else{
this.settingRightPx=true;
}
},
//改变字体大小
intervalFontSizeChange(e) {
console.log(e.target.value);
this.interval = e.target.value
this.setFontSize(this.interval);
},
//上一页
prevPage() {
if (this.rendition) {
uni.showLoading({
title: '电子书加载中'
});
this.rendition.prev().then((data) => {
uni.hideLoading();
})
}
},
//下一页
nextPage() {
if (this.rendition) {
uni.showLoading({
title: '电子书加载中'
});
this.rendition.next().then((data) => {
uni.hideLoading();
})
}
},
//跳转到指定页
toHref(href) {
this.rendition.display(href);
},
//设置背景色
setTheme(index) {
this.themes.select(this.themeList[index].name);
this.defaultTheme = index;
uni.setStorage({
key: 'default_theme',
data: this.defaultTheme,
success: function () {
console.log('主题设置成功');
}
});
},
//注册背景色
registerTheme() {
this.themeList.forEach(theme => {
this.themes.register(theme.name, theme.style)
})
},
//设置字体大小
setFontSize(fontSize) {
this.defaultFontSize = fontSize
if (this.themes) {
this.themes.fontSize(fontSize + 'px')
uni.setStorage({
key: 'font_size',
data: fontSize,
success: function () {
console.log('字体大小设置成功');
}
});
}
},
//选中一级菜单,同时显示二级菜单页面
tabSelect(e) {
this.tabCur = e.currentTarget.dataset.id;
this.scrollLeft = (e.currentTarget.dataset.id - 1) * 60;
this.modalName = e.currentTarget.dataset.target;
},
//隐藏二级菜单
hideModal(e) {
this.modalName = null
},
//选择二级菜单触发事件
selectList(data) {
console.log(JSON.stringify(data));
this.hideModal();
this.toHref(data.href);
this.settingRightPx=false;
},
//显示菜单
showMenu(){
this.ifShowMenu = ! this.ifShowMenu;
},
//隐藏标签弹框
hideBookMark(){
this.bookMarkShow=false;
},
//保存书签
saveBookMark(){
if(!this.bookMarkName){
uni.showToast({
icon: 'none',
title: "请输入书签名再提交",
duration: 2000
});
return false;
}
//得到当前书签的百分比数
let bookMark=(this.rendition.location.start.percentage).toFixed(4);
//保存书签
this.$http({
url:'/library/app/bookmark/save',
data:{"bookid":this.bookInfo.id,"bookmarkname":this.bookMarkName,"bookmarkpercentage":bookMark},
method: "POST"
}).then(response=>{
if(response.code==0){
uni.showToast({
title: '书签保存成功',
duration: 2000
});
this.bookMarkShow=false;
}
}).catch(error=>{
uni.showToast({
icon: 'none',
title: error,
duration: 2000
});
});
},
//隐藏书签列表
hideBookMarkList(){
this.bookMarkListShow=false;
},
//显示书签列表
showBookMarkList(){
this.$http({
url:'/library/app/bookmark/getList',
data:{"bookid":this.bookInfo.id},
method: "GET"
}).then(response=>{
if(response.code==0){
this.bookMarkListShow=true;
this.settingRightPx=false;
this.bookMarkList=response.list;
}
}).catch(error=>{
uni.showToast({
icon: 'none',
title: error,
duration: 2000
});
});
},
//跳转书签
toMark(mark){
const location = this.locations.cfiFromPercentage(mark.bookmarkpercentage);
this.rendition.display(location);
this.bookMarkListShow=false;
}
}
}
</script>
css
<style>
.float-book-setting{
line-height: 60upx;
border-radius: 40upx;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transition: -webkit-transform .3s;
transition: -webkit-transform .3s;
transition: transform .3s;
transition: transform .3s,-webkit-transform .3s;
}
.float-book-setting-active{
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
}
.setting-tools{
position: fixed;
width: 100%;
bottom: 0;
z-index: 1025;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.1);
background-color: white;
}
.setting-font-size{
position: absolute;
}
.setting-font-size-val{
margin-left: 100upx;
}
.setting-font-bg{
position: absolute;
}
.setting-font-bg-val{
margin-left: 100upx;
}
.setting-font-bg-txt{
margin: 10upx;
width: 40upx;
height: 40upx;
float: left;
border: 1px solid #eeee;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.1);
}
.setting-dir{
text-align: center;
height: 35px;
line-height: 35px;
}
.setting-close{
text-align: center;
height: 35px;
line-height: 35px;
}
.setting-content-view{
height: 100upx;
line-height: 100upx;
margin: 0 10px 0;
}
page {
background-image: var(--gradualBlue);
width: 100vw;
overflow: hidden;
}
.DrawerPage {
position: fixed;
width: 100vw;
height: 100vh;
left: 0vw;
background-color: #f1f1f1;
transition: all 0.4s;
}
.DrawerPage.show {
transform: scale(0.9, 0.9);
left: 85vw;
box-shadow: 0 0 60upx rgba(0, 0, 0, 0.2);
transform-origin: 0;
}
.DrawerWindow {
position: absolute;
width: 85vw;
height: 100vh;
left: 0;
top: 0;
transform: scale(0.9, 0.9) translateX(-100%);
opacity: 0;
pointer-events: none;
transition: all 0.4s;
padding: 100upx 0;
}
.DrawerWindow.show {
transform: scale(1, 1) translateX(0%);
opacity: 1;
pointer-events: all;
}
.DrawerClose {
position: absolute;
width: 40vw;
height: 100vh;
right: 0;
top: 0;
color: transparent;
padding-bottom: 30upx;
display: flex;
align-items: flex-end;
justify-content: center;
background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6));
letter-spacing: 5px;
font-size: 50upx;
opacity: 0;
pointer-events: none;
transition: all 0.4s;
}
.DrawerClose.show {
opacity: 1;
pointer-events: all;
width: 15vw;
color: #fff;
padding-bottom: 60px;
}
.DrawerPage .cu-bar.tabbar .action button.cuIcon {
width: 64upx;
height: 64upx;
line-height: 64upx;
margin: 0;
display: inline-block;
}
.DrawerPage .cu-bar.tabbar .action .cu-avatar {
margin: 0;
}
.DrawerPage .nav {
flex: 1;
}
.DrawerPage .nav .cu-item.cur {
border-bottom: 0;
position: relative;
}
.DrawerPage .nav .cu-item.cur::after {
content: "";
width: 10upx;
height: 10upx;
background-color: currentColor;
position: absolute;
bottom: 10upx;
border-radius: 10upx;
left: 0;
right: 0;
margin: auto;
}
.DrawerPage .cu-bar.tabbar .action {
flex: initial;
}
</style>