| <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> |