电子书阅读器一

引言

此系列为 Vue 实战商业级读书Web APP 全面提升技能 开发过程中的整理归纳,作为提纲方便重现。需要有一些前端的先验知识:javascript,vuejs,epubjs,nodejs 等。

阅读器知识

1.电子书的格式目前主要有 mobi 和 epub 两种,其中 mobi 是亚马逊独有的格式,epub是支持的通用格式。这次阅读器的目标是解析 epub 格式电子书。
2.使用解压命令解压一本电子书看下有什么:
------META-INF
 ------container.xml
------OEBPS
------mimetype
解压后目录内容如上所示,打开 container.xml 看到有


-
    -
        
    

这个 xml 文件指定了 rootfile 路径,我们需要的电子书内容其实都在 OEBPS 文件夹内,在 content.pdf 引导了电子书的内容,打开后发现也是一个 xml 类型文件,

出版信息

    图片,文件,章节,目录信息


    电子书的阅读顺序


有了对 epub 格式的基本了解就可以开始进行下一步开发了。

准备工作

1.字体图标:
在 Iconfont 下载 svg 格式的字体图标后导入 icomoon 转换后下载。
2.使用 vue-cli 3.0 搭建 vue 项目:
记得选择 Vuex 和 Router, Router 选择hash(not history mode)模式。
配置模式选择不集成(In dedicated config files),否则会全部集成到 package.json 里。
完成后发现文件结构比 2.0 的简洁许多,原来的 conf 和 build 文件夹没有了。
配置项目: vue.config.js,配置原来 build 里的内容。
配置 baseUrl:

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  }

vue cli 3.0 提供了图形化界面,可在命令行中通过

vue ui

启动图形化管理界面。
3.web字体:
在 CSS3 之前,web 设计师必须使用已在用户计算机上安装好的字体。通过 CSS3,web 设计师可以使用他们喜欢的任意字体。
准备好 web 字体后进行测试,通过后继续下一步。

4.viewport配置和rem设置:
该项目的主要使用场景是移动端,因此需要对 viewport 进行设置,禁止缩放,打开
public/index.html, 设置 viewport:


同时,打开 app.vue 加入以下代码:


我们对网页添加监听,加载完后根据屏幕尺寸设置 font-size,并限定字体最大尺寸为 50px。

解析电子书

先准备一本电子书放到 public 文件夹下

import Epub from 'epubjs'
globao.ePub = Epub
this.book = new Epub(‘/2016_Book_NewHorizonsForAData-DrivenEcon.epub')
console.log(this.book)

可以看到:

解析出电子书.png

电子书解析成功。

阅读器开发难点

1.阅读器开发:分页算法,全文搜索算法,引入 Web 字体,主题设计。
2.离线存储机制: LocalStorage + IndexedDB
3.复杂手势+交互动画,兼容手势 + 鼠标
4.利用 vuex + minin 实现组件解耦
5.利用 es6 优雅地实现数据结构变化
6.科大讯飞 Web 在线语音合成 API

global.scss 和 reset.scss 设置

重置样式是前端常用的 css 预设方法,预先设置了一些基本的样式,如 0 边距等,从网上搜一份 reset.css 文件,放置在
assets/style/ 下,重命名为 reset.scss。打开后添加

html,body {
  width: 100%;
  height: 100%;
  user-select: none;
  overflow: hidden;
}

把 user-select 置为 none, 阻止用户选中文字,避免和长按操作冲突。
在 global.scss 里引入 reset.scss ,添加 px2rem($px) 函数, 这样能在 vue 文件里对 css 样式进行像素控制。具体如下:

@import "./reset";

$ratio: 375 / 10;
@function px2rem($px) {
  @return $px / $ratio + rem;
}

这里设置了 1rem 对应的 px 为 375 / 10,因为 375 是 4.7 寸 iphone 的宽,移动端适配常用的一个数值。

引入vuex + vue-devtools

vuex介绍移步官网,使用 vuex 对组件进行解耦,对方法进行复用, chrome 安装 vue-devtools 可视化监控 state 变量和组件。

配置 nginx 静态资源服务器

将开发所需的大文件等放到 nginx 配置路径下。
nginx 配置:

 server {
        listen  9001;
        server_name resource;
        root D:\\fe\\resource;
        autoindex on;
        location / {
            add_header Access-Control-Allow-Origin *;
        }
        add_header Cache-Control "no-cache, must-revalidate";
    }

禁止缓存,每次必须重新获取资源。
启动 nginx: start nginx

阅读器需求分析

阅读器需求分析.png

1.阅读器部分先要实现一个动态路由实现这种地址的解析:

http://localhost:8081/#/ebook/ComputerScience|2016_Book_NewHorizonsForAData-DrivenEcon

做法是在 router 里配置动态路由:

  {
      path: '/ebook',
      component: () => import('./views/ebook/index.vue'),
      children: [
        {
          path: ':fileName',
          component: () => import('./components/ebook/EbookReader.vue')
        }
      ]
    }

2.然后在 EbookReader.vue 里解析:

mounted() {
    this.$route.params.fileName.split('|').join('\')
}

另外注意还要加上 '.epub' 才能下载电子书:

const url = process.env.VUE_APP_RES_URL + '/epub/' + this.fileName + '.epub'

注意到 VUE_APP_RES_URL 使我们在 .env.development 里预先定于好的,这里 VUE_APP_RES_URL = http://localhost:9001。指向的是 nginx 静态资源服务器。

将电子书渲染到指定 div 的方法:

this.rendition = this.book.renderTo('divID', {
          width: innerWidth,
          height: innerHeight,
          // method: 'scrolled'
          // 阅读器阅读模式
          method: 'default'
        })
// 渲染
this.rendition.display()

3.实现翻页
打开页面发现电子书是嵌入到一个 iframe 里展示的,使用 epubjs 自带的监听方法 this.rendition.on 来对 ‘touchStart' 和 ’touchEnd' 进行监听,通过 event.timeStamp 判断是否翻页,然后调用this.rendition.prev()/next() 函数即可处理翻页。event.changedTouches 对象是指触碰的手指,这里判断一只手指的动作。

this.rendition.on('touchStart', event => {
    this.touchStartX = event.changedTouches[0].clientX;
    this.touchStartTime = event.timeStamp;
}
this.rendition.on('touchEnd', event => {
    this.touchEndX = event.changedTouches[0].clientX;
    this.touchEndTime = event.timeStamp;
    const offsetX = event.changedTouches[0].clientX - this.touchStartX;
    if (this.touchEndTime - this.touchStartTime < 500){
        if (offset > 40) {
            // 上一页
            this.prevPage();
        } else if (offsetX < -40) {
            // 下一页
            this.nextPage();
        }
    } else {
        // 否则显示标题和菜单栏
        this.toggleMenuAndTitle();
    }
    event.preventDefault();
    event.stopPropagation();
}

4.标题和菜单栏
这一部分内容实现上比较容易,重点的内容有以下几点:
4.1.HTML 语义化:
在页面布局上使用合理的称谓和嵌套关系,易于理解。标签的命名遵照一定的格式,比如在 EbookReader 组件里:


标签的 class 命名均采用小写,逻辑断点用 '-' 号,字体图标均用 icon 前缀。
方法的命名,均采用驼峰命名,布尔类型的形容词用 ‘Visible' 类的形容词。
div 的嵌套规则上, 需要外层 div 的用 '-wrapper' 来标识,等等。平常开发过程中需要注意这些变量的命名要有统一的规则,DOM 嵌套上也要有合理的顺序,整个项目要保持高度的一致性。

4.2.动画相关:
vue 其实定义好了一套自己的 css 动画规则,我们只需要套用即可,项目中把常用动画封装到了 transition.scss 里,在 global.scss 里进行引入。

4.3.混入机制
之前是通过直接在 EbookReader.vue 文件里利用扩展运算符把 state 添加到 this 中去,
做法如下:

import { mappGetters } from 'vuex'
computed: {
    ...mapGetters(['menuVisible'])
}

但是这种方式的弊端显而易见,需要逐个 vue 文件里去手动引入定义的 state, 所有 vue 文件的一致性不高,那么有没有一种方式可以让所有 vue 文件用同样的方式引入 state 呢?Vue 提供了 mixin 混入机制,具体做法是在所需的 vue 文件里用以下方式写入:

import { ebookMixin } from '../../utils/mixin'
mixin: [ebookMixin]

在 src/utils 下新建 mixin.js 文件,写入

import { mapGetters, mapActions } from 'vuex'

export const ebookMixin = {
    computed: {
        ...mapGetters([
        'menuVisible',
        ])
    },
    methods: {
        ...mapActions([
            'menuVisible',
        ]),
        // 其他方法
        ···
    }
    // 其他
    ...
}

这样的做法既能将公有的方法抽象出来,然后通过引入 mixin 一次性导入即可,十分方便。

5.字号,字体设置UI

字号设置UI.png
字体设置UI.png

阅读器部分的菜单栏单独抽象出来为 EbookMenu.vue 文件,在 EbookMenu 里又把字体,目录,进度,模式 设置单独作为组件:






6.字号,字体设置
通过 epubjs 自带的函数对字体进行设置:

this.currentBook.rendition.themes.fontSize(fontSize)
this.currentBook.rendition.themes.font('Times New Roman')

由于 epubjs 渲染的内容在 iframe 里, 而 web 字体的引用为 css 文件引入的方式,此时需要 epubjs 提供的钩子函数将 css 文件加载到 iframe 里去:

 // epubjs勾子函数为电子书显示的iframe注入内容
        this.rendition.hooks.content.register(contents => {
          // console.log(process.env.VUE_APP_RES_URL)
          Promise.all([
            // 电子书显示添加字体
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/cabin.css`),
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/daysOne.css`),
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/montserrat.css`),
            contents.addStylesheet(`${process.env.VUE_APP_RES_URL}/fonts/tangerine.css`)
          ]).then(() => {
          })
        })

7.字体设置弹窗
css 实现进度条,如上图,左右是最小和最大字号的 A 字母预览,中间是 css 实现的进度条,这种效果如何实现要有合理的布局,清晰的结构,EbookSettingFont.vue 里进行了实现,涉及到了 flex 布局,利用高度为 0 设置 border-top 和 border-left 来模拟进度条, 点击改变 vuex 里的 defaultFontSize, 并利用以上提到的方法进行电子书字号的更改。
弹窗的动画需要注意的是,需要注意设置同样的高度才不会不一致的情况。

8.字号和字体的离线存储
使用 localStorage 进对电子书设置的字体,字号等信息进行缓存,utils/localStorage.js 里预先定义了一系列 localStorage 的操作方法。

9.字体设置国际化
vue-i18n 的使用, 预先定义所有需要的字体内容, 通过 vue.$t('name') 来使用国际化内容。

10.主题设置
分为 4 种主题,在 utils/book.js 里定义好了这 4 中主题的 css 样式,然后在 EbookSettingTheme.vue 里调用 epubjs 的 rendition.theme.select(theme) 来改变主题。

11.全局主题样式
全局主题根据 4 种不同样式抽象出 4 个 css 文件放置到 nginx 服务文件夹下, 在 utils/ book.js 下创建动态添加/删除 link 标签的方法,通过这种方式向页面注入 css 文件从而实现全局主题样式的切换。

阅读器第一部分完,阅读器第二部分将会实现阅,目录功能和全文搜索功能。

你可能感兴趣的:(电子书阅读器一)