知识点:

阅读器工作原理

ePub 和 mobi

ePub(Electronic Publication) 电子出版物

mobi 是 Amazon Kindle 的电子书格式

开发流程

当前Node版本 16.15.0

创建项目

vue create ebook-demo

安装scss依赖

npm i -D sass sass-loader@10.1.0

安装阅读器引擎

npm i epubjs@0.3.71

viewport配置

viewport是用来设置用户在手机上的可视区域

width = device-width:指定 viewport 宽度为设备宽度

initial-scale = 1.0:指定默认缩放比例为 1:1

通过 maximum-scale 和 minimum-scale 限定屏幕缩放比例为 1:1

通过 user-scalable 限制用户对屏幕进行缩放

 <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">

rem 配置

rem 是 css3 新增的一个相对长度单位

rem 的值 等于 font-size 值的倍数

1rem = 根元素 font-size

2rem= 根元素 font-size * 2

DOMContentLoaded 事件动态设置根元素 font-size

事件DOMContentLoaded和load的区别

他们的区别是,触发的时机不一样,先触发DOMContentLoaded事件,后触发load事件。

DOM文档加载的步骤为

1.解析HTML结构。
2.加载外部脚本和样式表文件。
3.解析并执行脚本代码。
4.DOM树构建完成。//DOMContentLoaded
5.加载图片等外部文件。
6.页面加载完毕。//load

在第4步,会触发DOMContentLoaded事件。在第6步,触发load事件。

html.style.size = window.innerWidth / 10 + 'px'

document.addEventListener('DOMContentLoader', () => {const html = document.querySelector('html')html.style.fontSize = window.innerWidth / 10 + 'px'
})

reset.scss 和 global.scss

reset.scss 的目的是为了消除不同浏览器默认样式的不一致性

global.scss 规定了整个站点的公共样式、公共方法和公共参数等

实现 px2rem 方法,将 px 转化为 rem

reset.scss

/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126License: none (public domain)
*/html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {margin: 0;padding: 0;border: 0;font-size: 100%;font: inherit;vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {display: block;
}
body {line-height: 1;
}
ol, ul {list-style: none;
}
blockquote, q {quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {content: '';content: none;
}
table {border-collapse: collapse;border-spacing: 0;
}
/* 设置字体和屏幕宽度 */
html,body {width: 100%;height: 100%;font-family: 'PingFangSc-Light','Arial';
}

global.scss

@import 'reset';
$fontSize:37.5;
@function px2rem($px){@return ($px / $fontSize) + rem
}
@mixin  center() {display: flex;justify-content: center;align-items: center;
}
.iconfont {font-size: px2rem(20);
}
.slide-down-enter, .slide-down-leave-to {transform: translate3d(0,px2rem(-108),0);
}
.slide-down-enter-to,.slide-down-leave,
.slide-up-enter-to,.slide-up-leave {transform: translate3d(0,0,0);
}
.slide-down-enter-active, .slide-down-leave-active,
.slide-up-enter-active,.slide-up-leave-active,
.fade-enter-active,.fade-leave-active,
.slide-right-enter-active, .slide-right-leave-active {transition: all .3s linear;
}
.slide-up-enter,.slide-up-leave-to {transform: translate3d(0,px2rem(108),0);
}

rem计算交由第三方插件完成,不用每次自己转换,开发时正常书写px

 安装插件

  • postcss-pxtorem 是一款 PostCSS 插件,用于将 px 单位转化为 rem 单位
  • lib-flexible 用于设置 rem 基准值
npm install lib-flexible postcss-pxtorem@5.1.1 --save-dev

项目根目录添加配置文件  postcss.config.js

添加配置

module.exports = {plugins: {'postcss-pxtorem': {rootValue: 37.5,   // 设计稿尺寸的 1/10propList: ['*'],},},
}

main.js 引入 lib-flexible

import 'lib-flexible'

epubjs 的核心工作原理解析

通过 epubjs 实例化一个 Book 对象

Book 对象会对电子书进行解析

通过 renderTo 方法生成一个 Rendition 对象

Rendition 对象主要负责电子书的渲染

Theme 负责电子书的 样式和主题,如:设置字号、设置主题等功能

Location 负责电子书的定位,用来实现 拖动进度条时快速定位的功能

Navigation 用来展示电子书目录并提供目录所在的路径

vscode 配置vue模版

1、唤出vscode控制台, 输入
win: Ctrl + Shift + P
mac: command + Shift + P

2、输入snippets, 点击 首选项:配置用户代码片段

3、在出现的框中输入 vue 之后按回车键, 出现 vue.json 文件

4、将vue.json文件改为下面得模板(可以根据个人需求修改当中的模板内容)

{    "Print to console": {"prefix": "vue2","body": ["<template>","  <div class=''></div>","</template>","","<script>","export default {","  name: '',","  components: {},","  props: {},","  data() {","    return {","    }","  },","  watch: {},","  computed: {},","  mounted() {},","  methods: {},","};","</script>","<style lang=\"scss\" scoped>","</style>"],"description": "A vue file template"}
}

5、然后新建一个 Ebook.vue 文件,输入“vue2”,按下回车键或者Tab键,模板就自动生成了

在 Ebook.vue 文件里完成 电子书的实现

在 public 目录下新建 static 目录,将电子书放入 static 目录

Vue 中 transition 过渡动画原理

使用 v-show 动态显示或隐藏元素时,会触发过渡动画

transition 需要指定 name ,并包裹一个包含 v-show 的 div

vue 会为 包裹 transition 包裹的 div 动态添加 class ,共6种

Ebook.vue

<template><div class='ebook'><transition name="slide-down"><div class="title-wrapper" v-show="showTitleAndMenu"><div class="left"><span class="icon icon-back"></span></div><div class="right"><div class="icon-wrapper"><span class="icon icon-cart"></span></div><div class="icon-wrapper"><span class="icon icon-person"></span></div><div class="icon-wrapper"><span class="icon icon-more"></span></div></div></div></transition><div class="read-wrapper"><div id="read"></div><div class="mask"><div class="left" @click="prevPage"></div><div class="center" @click="toggleTitleAndMenu"></div><div class="right" @click="nextPage"></div></div></div><div class="menu-bar"><transition name="slide-up"><div class="menu-wrapper" v-show="showTitleAndMenu"><div class="icon-wrapper" @click="showContentBar"><span class="icon icon-menu"></span></div><div class="icon-wrapper" @click="showSettingBar(2)"><span class="icon icon-progress"></span></div><div class="icon-wrapper" @click="showSettingBar(1)"><span class="icon icon-bright"></span></div><div class="icon-wrapper" @click="showSettingBar(0)"><span class="icon icon-a">A</span></div></div></transition><transition name="slide-up"><div class="setting-wrapper" v-show="showSetting"><div class="setting-font-size" v-show="settingTag === 0"><div class="preview" :style="{fontSize: fontSizeList[0] + 'px'}">A</div><div class="select"><div class="select-wrapper" v-for="(item, index) in fontSizeList" :key="index"><div class="line"></div><div class="point-wrapper" @click="changeFontSize(item)"><div class="point" v-show="defaultSize === item"><div class="small-point"></div></div></div><div class="line"></div></div></div><div class="preview" :style="{fontSize: fontSizeList[fontSizeList.length - 1] + 'px'}">A</div></div><div class="setting-theme" v-show="settingTag === 1"><div class="setting-theme-item" v-for="item in themeList" :key="item.name" @click="setTheme(item.name)"><div class="preview" :style="{background: item.style.body.background}":class="{'no-border': item.style.body.background === '#fff'}"></div><div class="text" :class="{selected: item.name === defaultTheme}">{{item.name}}</div></div></div><div class="setting-progress" v-show="settingTag === 2"><div class="progress-wrapper"><input type="range" class="progress"min="0"max="100"step="1"@change="onProgressChange($event.target.value)"@input="onProgressInput($event.target.value)":value="progress":disabled="!bookAvailable"ref="progress"></div><div class="text-wrapper"><span>{{bookAvailable ? progress + '%' : '加载中...'}}</span></div></div></div></transition><div class="content-mask" v-show="showContent" @click="hideContent"></div><div class="content" v-show="showContent" ><div class="content-wrapper" v-if="bookAvailable"><div class="content-item" v-for="(item, index) in navigation.toc" :key="index" @click="jumpTo(item.href)">{{item.label}}</div></div><div class="empty" v-else>加载中...</div></div></div></div>
</template><script>
import Epub from 'epubjs'
const DOWNLOAD_URL = '/static/boo1.epub'export default {name: '',components: {},props: {},data() {return {showTitleAndMenu: false,showSetting: false,fontSizeList: [12, 14, 16, 18, 20, 22, 24],defaultSize: 16,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)',}},},],defaultTheme: 'default',settingTag: 0,bookAvailable: false, // 图书是否可用状态progress: 0,showContent: false,}},watch: {},computed: {},mounted() {this.showEpub()},methods: {// 电子书的解析和渲染showEpub() {// 生成 book 对象this.book = new Epub(DOWNLOAD_URL)// 生成 Rendition 对象, 通过 book.renderTo 生成this.rendition = this.book.renderTo('read', {width: innerWidth,height: innerHeight,})// 通过 Rendition.display 渲染电子书this.rendition.display()// 获取 theme 对象this.themes = this.rendition.themes// 设置默认字体this.themes.fontSize(this.defaultSize + 'px')// 设置主题 // 注册主题 this.themes.register(name, styles)// 切换主题 this.themes.select(name)this.themeList.forEach(theme => this.themes.register(theme.name, theme.style))this.themes.select(this.defaultTheme)// 获取 locations 对象this.book.ready.then(() => {this.navigation = this.book.navigationreturn this.book.locations.generate()}).then(() => {this.locations = this.book.locationsthis.bookAvailable = true})},// 根据链接跳转到指定位置jumpTo(href) {this.rendition.display(href)this.hideTitleAndMenu()this.$nextTick(() => {this.onProgressInput(this.book.rendition.currentLocation().end.percentage * 100)})},hideTitleAndMenu() {this.showTitleAndMenu = falsethis.hideSettingBar()this.hideContent()},hideContent() {this.showContent = false},showContentBar() {this.showContent = true},// progress: 0 - 100onProgressChange(progress) {const percentage = progress / 100const location = percentage > 0 ? this.locations.cfiFromPercentage(percentage) : 0this.rendition.display(location)},onProgressInput(progress) {this.progress = progressthis.$refs.progress.style.backgroundSize = `${progress}% 100%`},prevPage() {// Rendition.previf (this.rendition) {this.rendition.prev()this.onProgressInput(this.book.rendition.currentLocation().end.percentage * 100)}},nextPage() {// Rendition.nextif (this.rendition) {this.rendition.next()this.onProgressInput(this.book.rendition.currentLocation().end.percentage * 100)}},toggleTitleAndMenu() {this.showTitleAndMenu = !this.showTitleAndMenuif (!this.showTitleAndMenu) {this.hideSettingBar()}},showSettingBar(index) {this.showSetting = truethis.settingTag = index},hideSettingBar() {this.showSetting = false},changeFontSize(fontSize) {this.defaultSize = fontSizeif (this.themes) {this.themes.fontSize(fontSize + 'px')}},setTheme(themeName) {if (this.themes) {this.themes.select(themeName)this.defaultTheme = themeName}},},
};
</script><style lang="scss" scoped>@import '@/assets/styles/global.scss';.ebook {position: relative;.title-wrapper {position: absolute;top: 0;left: 0;width: 100%;height: 48px;z-index: 110;display: flex;justify-content: space-between;align-items: center;box-shadow: 0 8px 8px rgba(0, 0, 0, .15);background-color: #fff;.left {flex: 0 0 60px;@include center;}.right {display: flex;align-items: center;.icon-wrapper {padding: 0 20px;}}&.slide-down-enter, &.slide-down-leave-to {transform: translateY(-100%);}&.slide-down-enter-to, &.slide-down-leave {transform: translateY(0);}&.slide-down-enter-active, &.slide-down-leave-active {transition: transform .3s linear;}}.read-wrapper {.mask {position: absolute;top: 0;left: 0;width: 100%;height: 100%;z-index: 100;display: flex;.left {width: 100px;}.center {flex: 1;}.right {width: 100px;}}}.menu-wrapper {position: absolute;bottom: 0;left: 0;width: 100%;height: 48px;z-index: 110;display: flex;align-items: center;box-shadow: 0 -8px 8px rgba(0, 0, 0, .15);background-color: #fff;.icon-wrapper {flex: 1;@include center;}&.slide-up-enter, &.slide-up-leave-to {transform: translateY(100%);}&.slide-up-enter-to, &.slide-up-leave {transform: translateY(0);}&.slide-up-enter-active, &.slide-up-leave-active {transition: transform .3s linear;}}.setting-wrapper {position: absolute;bottom: 48px;left: 0;width: 100%;height: 60px;background-color: #fff;z-index: 120;box-shadow: 0 -8px 8px rgba(0, 0, 0, .15);.setting-font-size {display: flex;align-items: center;justify-content: space-between;height: 100%;.preview {width: 40px;@include center;}.select {flex: 1;display: flex;.select-wrapper {flex: 1;@include center;&:first-child {.line {&:first-child {border-top: none;}}}&:last-child {.line {&:last-child {border-top: none;}}}.line {flex: 1;height: 0;border-top: 1px solid #ccc;}.point-wrapper {position: relative;width: 0;height: 7px;border-left: 1px solid #ccc;.point {width: 20px;height: 20px;position: absolute;top: -8px;left: -8px;border-radius: 50%;background-color: #fff;border: 1px solid #ccc;box-shadow: 0 4px 4px rgba(0, 0, 0, .15);@include center;.small-point {width: 5px;height: 5px;border-radius: 50%;background-color: #000;}}}}}}.setting-theme {width: 100%;height: 100%;@include center;.setting-theme-item {flex: 1;display: flex;flex-direction: column;align-items: center;padding: 5px;box-sizing: border-box;.preview {width: 100%;height: 20px;box-sizing: border-box;&.no-border {border: 1px solid #ccc;}}.text {font-size: 16px;color: #999;&.selected {color: #333;}}}}.setting-progress {width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;.progress-wrapper {width: 100%;height: 100%;@include center;padding: 0 30px;box-sizing: border-box;.progress {width: 100%;-webkit-appearance: none;height: 2px;background: -webkit-linear-gradient(#999, #999) no-repeat, #ddd;background-size: 0 100%;&.focus {outline: none;}&::-webkit-slider-thumb {-webkit-appearance: none;width: 20px;height: 20px;border-radius: 50%;background-color: #fff;box-shadow: 0 4px 4px 0 rgba(0, 0, 0, .15);border: 1px solid #ddd;}}}}}.content-mask {position: absolute;top: 0;left: 0;z-index: 140;width: 100%;height: 100%;background-color: rgba(51, 51, 51, .8);}.content {position: absolute;top: 0;left: 0;z-index: 150;width: 70%;height: 100%;background-color: #fff;padding: 20px;box-sizing: border-box;.content-item {width: 100%;padding: 20px 0;border-bottom: 1px solid #ccc;font-size: 16px;@include center;}}}
</style>

代码仓库地址:https://github.com/buhairong/project/tree/master/vue2/ebook-demo

1

快速入门Web阅读器开发相关推荐

  1. Web阅读器开发系列教程(入门篇)

    作者:Sam 前言 最近我在慕课网发布了两门关于Web阅读应用开发的课程,采用Vue全家桶开发.免费课是入门级课程,初步实现了一个阅读器.实战课是进阶课程,实现了一个高性能的互联网阅读应用.两个项目都 ...

  2. 快速入门Web前端开发的正确姿势

    入门标准很简单,就一条:达到能参与 Web 前端实际项目的开发水平.请注意,是实际项目,这就需要了解如今的实际项目开发都用了哪些技术栈.HTML/CSS/JavaScript 这三大基础技术栈肯定是需 ...

  3. 双语web阅读器+书城设计与实现

    背景: 前段日子心血来潮,突然想做个小说阅读器,带翻译功能的. 原因大概来之已久了,主要是我本人是一个超级大书虫,特别喜欢看网络小说.经典小说.名著.心理学等"有意思"的书. 从接 ...

  4. 零基础快速入门web学习路线(含视频教程)

    下面小编专门为广大web学习爱好者汇总了一条完整的自学线路:零基础快速入门web学习路线(含视频教程)(绝对纯干货)适合初学者的最新WEB前端学习路线汇总! 在当下来说web前端开发工程师可谓是高福利 ...

  5. 商业级web阅读器项目(上)

    1.技术难点分析 阅读器开发: 分页算法.全文搜索算法.引入web字体.主题设计 离线存储机制设计:LocalStorage+IndexDB 实现各种复杂手势+交互动画,如何兼容手势+鼠标操作 利用v ...

  6. 快速掌握Web高德地图开发

    本文章致力于帮助对Web高德地图开发感兴趣的朋友快速掌握开发要领,如果能对屏幕前的您有所助益,我将十分荣幸. 如何快速掌握Web高德地图开发? 要领如下: 一. 首先要知道高德地图本身有提供开放的地图 ...

  7. HealthKit开发快速入门教程之HealthKit开发概述简介

    HealthKit开发快速入门教程之HealthKit开发概述简介 2014年6月2日召开的年度开发者大会上,苹果发布了一款新的移动应用平台,可以收集和分析用户的健康数据.该移动应用平台被命名为&qu ...

  8. 视频教程-微信小程序快速入门视频课程-微信开发

    微信小程序快速入门视频课程 北京八维研修学院技术工程师,5年大型项目实战开发经验,3年授课经验. 孟宪杰 ¥168.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 AP ...

  9. 最详细,快速入门Web前端开发的正确姿势

    入门标准 入门标准很简单,就一条:达到能参与 Web 前端实际项目的开发水平.请注意,是实际项目,这就需要了解如今的实际项目开发都用了哪些技术栈.HTML/CSS/JavaScript 这三大基础技术 ...

最新文章

  1. 开发缺点_成都嗨创科技:原生APP开发与混合APP开发的优缺点对比
  2. JavaScript学习笔记(八)--- 函数表达式
  3. powercfg -h off_驭鲛记的主演会是谁?肖战关系特别好的艺人朋友呢?白敬亭和吴映洁有没有故事啊?高伟光是不是隐婚生子了?讲讲管h和马司令呗?...
  4. 工业以太网交换机与以太网光端机的区别
  5. 不行!不能这样下去!
  6. python做审计底稿视频_最新Python教学视频,每天自学俩小时,让你offer拿到手软...
  7. 8X53 VS 6763
  8. 怎样做好一个项目经理
  9. PHP中的数组(数据结构)
  10. 浪潮之颠一_读书笔记
  11. 趋势officescan客户端迁移和升级
  12. php源码内网穿透,PHP DDOS源码
  13. 2021西湖论剑 Re wp
  14. Ardunio开发实例-LM75温度传感器
  15. gentoo问题汇总
  16. 微信小程序开发详细步骤解决方案
  17. 谷歌浏览器chrome devtools 插件安装
  18. 深入理解Android Crash 流程
  19. 数据可视化(pyecharts库的介绍)
  20. kubesphere安装Maven+JDK17 流水线打包

热门文章

  1. 8.1SQL概述与数据库定义
  2. BCI IV-2a 运动想象论文代码复现
  3. 使用OpenCV-Python为照片添加不同滤镜(每天一个python小项目)
  4. Android在app中打开另一个app
  5. 用三个与非门设计或门两种方法
  6. Jeecg-Boot 2.1.3 大屏版本发布,基于SpringBoot的低代码开发平台
  7. 笔记:Learning Calibrated Medical Image Segmentation via Multi-rater Agreement Modeling
  8. 使用EJS脚本实现花生壳动态域名更新服务(一)
  9. 优酷上线优酷号短视频自媒体平台!
  10. 3.6 DNS服务器搭建