四零语境前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

440 lines
12 KiB

<template>
<view class="yingbing-whole-reader"
id="yingbing-whole-reader"
:data-start="start"
:data-color="color"
:data-title="title"
:data-background="background"
:data-split="split"
:data-fontSize="fontSize"
:data-fontFamily="fontFamily"
:data-lineGap="lineGap"
:data-topGap="topGap"
:data-bottomGap="bottomGap"
:data-slide="slide"
:data-pageType="pageType"
:data-backShow="backShow"
:data-headerShow="headerShow"
:data-footerShow="footerShow"
:data-autoplay="autoplaySync"
:data-interval="interval"
:data-unableClickPage="unableClickPage"
:data-selectable="selectable"
:selectable="selectable" :change:selectable="wholeReader.selectableWatcher"
:unableClickPage="unableClickPage" :change:unableClickPage="wholeReader.unableClickPageWatcher"
:autoplay="autoplaySync" :change:autoplay="wholeReader.autoplayWatcher"
:interval="interval" :change:interval="wholeReader.intervalWatcher"
:content="contentSync" :change:content="wholeReader.contentWatcher"
:fontSize="fontSize" :change:fontSize="wholeReader.fontSizeWatcher"
:fontFamily="fontFamily" :change:fontFamily="wholeReader.fontFamilyWatcher"
:split="split" :change:split="wholeReader.splitWatcher"
:lineGap="lineGap" :change:lineGap="wholeReader.lineGapWatcher"
:topGap="topGap" :change:topGap="wholeReader.topGapWatcher"
:bottomGap="bottomGap" :change:bottomGap="wholeReader.bottomGapWatcher"
:slide="slide" :change:slide="wholeReader.slideWatcher"
:pageType="pageType" :change:pageType="wholeReader.pageTypeWatcher"
:backShow="backShow" :change:backShow="wholeReader.backShowWatcher"
:headerShow="headerShow" :change:headerShow="wholeReader.headerShowWatcher"
:footerShow="footerShow" :change:footerShow="wholeReader.footerShowWatcher"
:background="background" :change:background="wholeReader.backgroundWatcher"
:color="color" :change:color="wholeReader.colorWatcher"
:isRender="isRender" :change:isRender="wholeReader.renderWatcher"
:isRefresh="isRefresh" :change:isRefresh="wholeReader.refreshWatcher"
:pageTo="pageTo" :change:pageTo="wholeReader.pageToWatcher"
@touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
</view>
</template>
<script>
import TouchClickMixin from '../mixin/touch-click.js'
export default {
options: {
addGlobalClass: true,
virtualHost: true, // 将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定
},
mixins: [TouchClickMixin],
props: {
//自动播放
autoplay: {
type: Boolean,
default: false
},
interval: {
type: [Number, String],
default: 5000
},
//字体颜色
color: {
type: String,
default: '#333333'
},
//字体大小(单位px)
fontSize: {
type: [Number, String],
default: 15
},
fontFamily: {
type: String,
default: 'Arial'
},
//背景颜色
background: {
type: String,
default: '#fcd281'
},
//分隔符
split: {
type: String,
default: ''
},
//翻页方式
pageType: {
type: String,
default: 'scroll'
},
//行间距(单位px)
lineGap: {
type: [Number, String],
default: 15
},
//页面左右边距(单位px)
slide: {
type: [Number, String],
default: 20
},
//页面上边距(单位px)
topGap: {
type: [Number, String],
default: 10
},
//页面下边距(单位px)
bottomGap: {
type: [Number, String],
default: 10
},
backShow: {
type: Boolean,
default: false
},
headerShow: {
type: Boolean,
default: true
},
footerShow: {
type: Boolean,
default: true
},
//是否关闭点击左右2侧位置翻页
unableClickPage: {
type: Boolean,
default: false
},
//开启文本选择
selectable: {
type: Boolean,
default: false
}
},
computed: {
contentSync () {
return {content: this.content}
}
},
data () {
return {
title: '',
content: '',
start: 0,
isRender: false,//是否渲染页面
isRefresh: false,//是否刷新页面
pageTo: 0
}
},
created() {
this.autoplaySync = this.autoplay
},
beforeDestroy() {
if ( this.touchTimer ) {//清楚定时任务
clearTimeout(this.touchTimer)
this.touchTimer = null
}
if ( this.longTimer ) {//清楚定时任务
clearTimeout(this.longTimer)
this.longTimer = null
}
},
methods: {
//初始化
init ({content, title, start}) {
this.content = content
this.title = title
this.$emit('setCatalog', this.getCatalog(this.content));
this.change(start)
},
//跳转
async change (start) {
const rect = await this.getRect()
this.windowWidth = rect.width
this.windowHeight = rect.height
this.start = parseInt(start)
this.isRender = false
this.$nextTick(function () {
this.isRender = true
})
},
//刷新
async refresh () {
const rect = await this.getRect()
this.windowWidth = rect.width
this.windowHeight = rect.height
this.isRefresh = false
this.$nextTick(function () {
this.isRefresh = true
})
},
prev () {
this.pageTo = 0
this.$nextTick(function(){
this.pageTo = -1
})
},
next () {
this.pageTo = 0
this.$nextTick(function(){
this.pageTo = 1
})
},
//抛出阅读页面改变事件
handleChange (e) {
this.$emit('change', e)
},
//往后翻页完成事件
handleEnded () {
this.$emit('ended')
},
//往前翻页完成事件
handleStarted () {
this.$emit('started')
},
handleBack () {
this.$emit('back')
},
//使用正则获取章节目录 并抛出事件
getCatalog (content) {
// const reg = new RegExp(/(第?[一二两三四五六七八九十○零百千万亿0-91234567890※✩★☆]{1,6}[章回卷节折篇幕集部]?[、.-\s][^\n]*)[_,-]?/g);
const reg = new RegExp(/(第+[一二两三四五六七八九十○零百千万亿0-91234567890※✩★☆]{1,6}[章回卷节折篇幕集部]?[、.-\s::,,][^\n]*)[_,-]?/g)
let match = '';
let catalog = [];
let chapter = 0
while ((match = reg.exec(content)) != null) {
chapter++
catalog.push({
title: match[0],
start: match.index
})
}
return catalog.length > 0 ? catalog : [{
start: 0,
title: this.title || '整章'
}]
},
getRect () {
return new Promise(resolve => {
uni.createSelectorQuery().in(this).select('.yingbing-whole-reader').boundingClientRect(data => {
resolve(data)
}).exec();
})
}
},
watch: {
autoplay (newVal) {
this.autoplaySync = newVal
}
}
}
</script>
<!-- #ifdef H5 || APP-VUE -->
<script lang="renderjs" module="wholeReader" type="module">
import Wholereader from "./wholereader.js"
export default {
data () {
return {
content: '',
whole: null
}
},
methods: {
renderWatcher (newVal) {
if ( newVal && this.content ) {
if ( this.whole ) this.whole.destroy()
this.whole = new Wholereader({
container: document.querySelector('#yingbing-whole-reader'),
autoplay: this.getData('autoplay') == 'true' ? true : false,
interval: this.getData('interval'),
content: this.getData('content'),
title: this.getData('title'),
color: this.getData('color'),
background: this.getData('background'),
fontSize: this.getData('fontSize'),
fontFamily: this.getData('fontFamily'),
slide: this.getData('slide'),
topGap: this.getData('topGap'),
bottomGap: this.getData('bottomGap'),
lineGap: this.getData('lineGap'),
split: this.getData('split'),
headerShow: this.getData('headerShow') == 'true' ? true : false,
footerShow: this.getData('footerShow') == 'true' ? true : false,
backShow: this.getData('backShow') == 'true' ? true : false,
pageType: this.getData('pageType'),
unableClickPage: this.getData('unableClickPage') == 'true' ? true : false,
selectable: this.getData('selectable') == 'true' ? true : false
})
this.whole.render(this.getData('start'))//开始渲染页面
this.whole.on('change', e => {//注册翻页改变事件
this.triggerMethod('handleChange', e)
})
this.whole.on('ended', () => {//注册往后翻页完成事件
this.triggerMethod('handleEnded')
})
this.whole.on('started', () => {//注册往前翻页完成事件
this.triggerMethod('handleStarted')
})
this.whole.on('back', () => {//点击返回按钮事件
this.triggerMethod('handleBack')
})
}
},
refreshWatcher (newVal) {
if ( this.refreshTimer ) {
window.clearTimeout(this.refreshTimer)
this.refreshTimer = null
}
if ( newVal && this.whole ) {
this.refreshTimer = window.setTimeout(() => {
this.whole.refresh()
}, 200)
}
},
pageToWatcher (newVal) {
if ( newVal == -1 ) this.whole && this.whole.prev()
if ( newVal == 1 ) this.whole && this.whole.next()
},
autoplayWatcher (newVal) {
this.whole && this.whole.setConfig('autoplay', newVal)
},
intervalWatcher (newVal) {
this.whole && this.whole.setConfig('interval', newVal)
},
contentWatcher (newVal, oldVal) {
this.content = newVal.content
},
fontSizeWatcher (newVal) {
this.whole && this.whole.setConfig('fontSize', newVal)
this.refreshWatcher(true)
},
fontFamilyWatcher (newVal) {
this.whole && this.whole.setConfig('fontFamily', newVal)
this.refreshWatcher(true)
},
splitWatcher (newVal) {
this.whole && this.whole.setConfig('split', newVal)
this.refreshWatcher(true)
},
lineGapWatcher (newVal) {
this.whole && this.whole.setConfig('lineGap', newVal)
this.refreshWatcher(true)
},
topGapWatcher (newVal) {
this.whole && this.whole.setConfig('topGap', newVal)
this.refreshWatcher(true)
},
bottomGapWatcher (newVal) {
this.whole && this.whole.setConfig('bottomGap', newVal)
this.refreshWatcher(true)
},
slideWatcher (newVal) {
this.whole && this.whole.setConfig('slide', newVal)
this.refreshWatcher(true)
},
pageTypeWatcher (newVal) {
this.whole && this.whole.setConfig('pageType', newVal)
this.refreshWatcher(true)
},
backShowWatcher (newVal) {
this.whole && this.whole.setConfig('backShow', newVal)
this.refreshWatcher(true)
},
headerShowWatcher (newVal) {
this.whole && this.whole.setConfig('headerShow', newVal)
this.refreshWatcher(true)
},
footerShowWatcher (newVal) {
this.whole && this.whole.setConfig('footerShow', newVal)
this.refreshWatcher(true)
},
backgroundWatcher (newVal) {
this.whole && this.whole.setConfig('background', newVal)
this.refreshWatcher(true)
},
colorWatcher (newVal) {
this.whole && this.whole.setConfig('color', newVal)
this.refreshWatcher(true)
},
selectableWatcher (newVal) {
this.whole && this.whole.setConfig('selectable', newVal)
this.refreshWatcher(true)
},
unableClickPageWatcher (newVal) {
this.whole && this.whole.setConfig('unableClickPage', newVal)
},
getData (name) {
const dom = document.getElementById('yingbing-whole-reader')
if ( name == 'content' ) {
return this.content.replace(/\t/g, ' ').replace(/ /g, ' ')
} else if ( ['fontSize', 'lineGap', 'topGap', 'bottomGap', 'slide', 'start', 'interval'].includes(name) ) {
return parseInt(dom.getAttribute('data-' + name))
} else {
return dom.getAttribute('data-' + name)
}
},
triggerMethod (name, args) {
// #ifndef H5
// UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
// cid: this._$id,
// method: name,
// args: args
// })
this.$ownerInstance.callMethod(name, args)
// #endif
// #ifdef H5
this[name](args)
// #endif
}
}
}
</script>
<!-- #endif -->
<style scoped>
@import url(/uni_modules/yingbing-ReadPage/components/yingbing-whole-reader/wholereader.css);
.yingbing-whole-reader {
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: hidden;
}
.yingbing-reader-content-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.yingbing-reader-content-loading-text {
margin-top: 10px;
font-size: 15px;
}
.yingbing-reader-content-loading-tip {
margin-top: 10px;
font-size: 12px;
}
</style>