Vue开发Web阅读器(一)

本项目使用vue3

初始化项目后需配置vue.config.js,官方文档:https://cli.vuejs.org/zh/config/#vue-config-js

我的暂时配置:

const path = require('path')

function resolve (dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  chainWebpack: (config) => {
    config.resolve.alias
      .set('src', resolve('src'))
      .set('styles', resolve('src/assets/styles'))
      .set('components', resolve('src/components'))
  },
  devServer: {
    host: '0.0.0.0',
    port: 8080
  },
  publicPath: './'
}

安装sass依赖和阅读器引擎

npm i node-sass sass-loader --save-dev

npm i epubjs --save

将项目所需的字体图标放入assets下的styles文件夹,将icon.css导入main.js中

import 'styles/icon.css'

index.html中配置手机访问时不能缩放

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

rem配置

在App.vue中配置,当DOM内容加载时设置字体大小为屏幕宽度的1/10,当字体大小大于50px时,还是50px

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'app',
  components: {
    HelloWorld
  }
}

document.addEventListener('DOMContentLoaded', () => {
  let html = document.querySelector('html')
  let fontSize = window.innerWidth / 10
  fontSize = fontSize > 50 ? 50 : fontSize
  html.style.fontSize = fontSize + 'px'
})
</script>

reset.scss和全局样式global.scss配置

访问https://meyerweb.com/eric/tools/css/reset/复制到styles/reset.scss中

还要加入html和body的默认设置

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', 'PingFang SC', 'STHeitiSC-Light', 'Helvetica-Light', 'Arial', 'sans-serif';
}

配置global.scss

@import "reset.scss";

// 1rem = fontSize px
// 1px = 1/fontSize rem

$fontSize: 37.5;
@function px2rem($px) {
  @return ($px / $fontSize) + rem
}

@mixin center() {
  display: flex;
  justify-content: center;
  align-items: center;
}

然后在main.js中引入global.scss

通过epubjs将epub电子书解析成Book对象,再通过Book对象生成Rendition对象

 

在src目录下新建Ebook.vue,router.js中配置路由和重定向,当访问主页面时重定向到/ebook页面

import Vue from 'vue'
import Router from 'vue-router'

import Ebook from '@/Ebook'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      redirect: '/ebook'
    },
    {
      path: '/ebook',
      component: Ebook
    }
  ]
})

Ebook.vue中写入

<template>
  <div>aaa</div>
</template>

<script>
import Epub from 'epubjs'

const DOWNLOAD_URL = '/static/2018_Book_AgileProcessesInSoftwareEngine.epub'
export default {
  methods: {
    // 电子书解析和渲染
    showEpub () {
      // 生成Ebook
      // 生成Rendition,通过Book.renderTo方法生成
      // 通过Rendition.display渲染电子书
    }
  },
  mounted () {
    this.showEpub()
  }
}
</script>
<template>
  <div class="ebook">
    <div class="read-wrapper">
      <div id="read"></div>
    </div>
  </div>
</template>

<script>
import Epub from 'epubjs'

const DOWNLOAD_URL = '/2018_Book_AgileProcessesInSoftwareEngine.epub'
global.ePub = Epub
export default {
  methods: {
    // 电子书解析和渲染
    showEpub () {
      // 生成Ebook
      this.book = new Epub(DOWNLOAD_URL)
      // 生成Rendition,通过Book.renderTo方法生成
      this.rendition = this.book.renderTo('read', {
        width: window.innerWidth,
        height: window.innerHeight
      })
      // 通过Rendition.display渲染电子书
      this.rendition.display()
    }
  },
  mounted () {
    this.showEpub()
  }
}
</script>

设置遮罩,点击左边或右边遮罩来控制翻页

<template>
  <div class="ebook">
    <div class="read-wrapper">
      <div id="read" class="read"></div>
      <div class="mask">
        <div class="left" @click="prevPage"></div>
        <div class="center"></div>
        <div class="right" @click="nextPage"></div>
      </div>
    </div>
  </div>
</template>

<script>
import Epub from 'epubjs'

const DOWNLOAD_URL = '/2018_Book_AgileProcessesInSoftwareEngine.epub'
global.ePub = Epub
export default {
  methods: {
    // 电子书解析和渲染
    showEpub () {
      // 生成Ebook
      this.book = new Epub(DOWNLOAD_URL)
      // 生成Rendition,通过Book.renderTo方法生成
      this.rendition = this.book.renderTo('read', {
        width: window.innerWidth,
        height: window.innerHeight
      })
      // 通过Rendition.display渲染电子书
      this.rendition.display()
    },
    prevPage () {
      if (this.rendition) {
        this.rendition.prev()
      }
    },
    nextPage () {
      if (this.rendition) {
        this.rendition.next()
      }
    }
  },
  mounted () {
    this.showEpub()
  }
}
</script>

<style lang="scss" scoped>
  @import "~styles/global";

  .ebook {
    position: relative;
    .read-wrapper {
      .read {
        /*width: 500px;*/
        /*margin: 0 auto;*/
      }
      .mask {
        display: flex;
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        z-index: 100;
        .left {
          width: 100px;
        }
        .right {
          width: 100px;
        }
        .center {
          flex: 1;
        }
      }
    }
  }
</style>

添加菜单,添加点击中心处显示和隐藏菜单事件

<template>
  <div class="ebook">
    <transition name="slide-down">
      <div v-show="isMenuShow" class="title-wrapper">
        <div class="left">
          <span class="icon-back icon"></span>
        </div>
        <div class="right">
          <div class="icon-wrapper">
            <span class="icon-cart icon"></span>
          </div>
          <div class="icon-wrapper">
            <span class="icon-person icon"></span>
          </div>
          <div class="icon-wrapper">
            <span class="icon-more icon"></span>
          </div>
        </div>
      </div>
    </transition>
    <div class="read-wrapper">
      <div id="read" class="read"></div>
      <div class="mask">
        <div class="left" @click="prevPage"></div>
        <div class="center" @click="toggle"></div>
        <div class="right" @click="nextPage"></div>
      </div>
    </div>
    <transition name="slide-up">
      <div v-show="isMenuShow" class="menu-wrapper">
        <div class="icon-wrapper">
          <span class="icon-menu icon"></span>
        </div>
        <div class="icon-wrapper">
          <span class="icon-progress icon"></span>
        </div>
        <div class="icon-wrapper">
          <span class="icon-bright icon"></span>
        </div>
        <div class="icon-wrapper">
          <span class="icon-a icon">A</span>
        </div>
      </div>
    </transition>
  </div>
</template>

<script>
import Epub from 'epubjs'

const DOWNLOAD_URL = '/2018_Book_AgileProcessesInSoftwareEngine.epub'
global.ePub = Epub
export default {
  data () {
    return {
      isMenuShow: false
    }
  },
  methods: {
    // 电子书解析和渲染
    showEpub () {
      // 生成Ebook
      this.book = new Epub(DOWNLOAD_URL)
      // 生成Rendition,通过Book.renderTo方法生成
      this.rendition = this.book.renderTo('read', {
        width: window.innerWidth,
        height: window.innerHeight
      })
      // 通过Rendition.display渲染电子书
      this.rendition.display()
    },
    prevPage () {
      if (this.rendition) {
        this.rendition.prev()
      }
    },
    nextPage () {
      if (this.rendition) {
        this.rendition.next()
      }
    },
    toggle () {
      this.isMenuShow = !this.isMenuShow
    }
  },
  mounted () {
    this.showEpub()
  }
}
</script>

<style lang="scss" scoped>
  @import "~styles/global";

  .ebook {
    position: relative;
    overflow-y: hidden;
    .title-wrapper {
      display: flex;
      justify-content: space-between;
      align-items: center;
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: px2rem(48);
      z-index: 101;
      background-color: #fff;
      box-shadow: 0 px2rem(8) px2rem(8) rgba(0, 0, 0, 0.15);
      &.slide-down-enter, &.slide-down-leave-to {
        transform: translate3d(0, -100%, 0);
      }
      &.slide-down-enter-to, &.slide-down-leave {
        transform: translate3d(0, 0, 0);
      }
      &.slide-down-enter-active, &.slide-down-leave-active {
        transition: all .3s linear;
      }
      .left {
        flex: 0 0 px2rem(60);
        @include center;
      }
      .right {
        flex: 1;
        display: flex;
        justify-content: flex-end;
        .icon-wrapper {
          flex: 0 0 px2rem(40);
          @include center;
        }
      }
    }
    .read-wrapper {
      .read {

      }
      .mask {
        display: flex;
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        z-index: 100;
        .left {
          width: px2rem(100)
        }
        .right {
          width: px2rem(100)
        }
        .center {
          flex: 1;
        }
      }
    }
    .menu-wrapper {
      display: flex;
      position: absolute;
      z-index: 101;
      left: 0;
      bottom: 0;
      width: 100%;
      height: px2rem(48);
      box-shadow: 0 px2rem(-8) px2rem(8) rgba(0, 0, 0, 0.15);
      background-color: #fff;
      .icon-wrapper {
        flex: 1;
        @include center
      }
    }
  }
</style>

动画效果解析

      &.slide-down-enter, &.slide-down-leave-to {
        transform: translate3d(0, -100%, 0);
      }
      &.slide-down-enter-to, &.slide-down-leave {
        transform: translate3d(0, 0, 0);
      }
      &.slide-down-enter-active, &.slide-down-leave-active {
        transition: all .3s linear;
      }

transition包裹在需要展示动画的标签外部,但动画的样式和包裹的标签属于同一级,所以要使用&表示和标签同一级

slide-down-enter表示开始显示时

slide-down-leave-to表示完全隐藏时

slide-down-enter-to表示完全显示时

slide-down-leave表示开始隐藏时

slide-down-enter-active表示显示的全过程

slide-down-leave-active表示隐藏的全过程

2个动画的方式差不多可以简写,而且以后可能还会用到,所以写入global.scss中

.slide-down-enter, .slide-down-leave-to {
  transform: translate3d(0, -100%, 0);
}

.slide-up-enter, .slide-up-leave-to {
  transform: translate3d(0, 100%, 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 {
  transition: all .3s linear;
}

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风里有诗句哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值