<template>
							 | 
						|
									<view class="yingbing-text-reader-computed">
							 | 
						|
										<text class="computed-text computed-text-chinese" ref="computedTextChinese" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}">中</text>
							 | 
						|
										<text space="nbsp" class="computed-text computed-text-space" ref="computedTextSpace" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}"> </text>
							 | 
						|
										<text class="computed-text computed-text-lower" ref="computedTextLower" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}">a</text>
							 | 
						|
										<text class="computed-text computed-text-upper" ref="computedTextUpper" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}">A</text>
							 | 
						|
										<text class="computed-text computed-text-number" ref="computedTextNumber" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}">9</text>
							 | 
						|
										<text class="computed-text computed-text-special" ref="computedTextSpecial" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}">&</text>
							 | 
						|
										<text class="computed-text computed-text-tibetan" ref="computedTextTibetan" :style="{
							 | 
						|
											'font-family': fontFamily
							 | 
						|
										}">སྤྱོ</text>
							 | 
						|
									</view>
							 | 
						|
								</template>
							 | 
						|
								
							 | 
						|
								<script>
							 | 
						|
									export default {
							 | 
						|
										inject: ['getMeasureSize', 'getCharSize', 'getFontSize', 'getFontFamily', 'getLineGap', 'getTopGap', 'getBottomGap', 'getSlide', 'getHeaderShow', 'getFooterShow', 'getTotalChapter', 'getSplit', 'getPageType'],
							 | 
						|
										props: {
							 | 
						|
											windowHeight: {
							 | 
						|
												type: [Number, String],
							 | 
						|
												default: 0
							 | 
						|
											},
							 | 
						|
											windowWidth: {
							 | 
						|
												type: [Number, String],
							 | 
						|
												default: 0
							 | 
						|
											}
							 | 
						|
										},
							 | 
						|
										computed: {
							 | 
						|
											measureSize () {
							 | 
						|
												return this.getMeasureSize()
							 | 
						|
											},
							 | 
						|
											charSize () {
							 | 
						|
												return this.getCharSize()
							 | 
						|
											},
							 | 
						|
											fontSize () {
							 | 
						|
												return this.getFontSize()
							 | 
						|
											},
							 | 
						|
											fontFamily () {
							 | 
						|
												return this.getFontFamily()
							 | 
						|
											},
							 | 
						|
											lineGap () {
							 | 
						|
												return this.getLineGap()
							 | 
						|
											},
							 | 
						|
											topGap () {
							 | 
						|
												return this.getTopGap()
							 | 
						|
											},
							 | 
						|
											bottomGap () {
							 | 
						|
												return this.getBottomGap()
							 | 
						|
											},
							 | 
						|
											slide () {
							 | 
						|
												return this.getSlide()
							 | 
						|
											},
							 | 
						|
											totalChapter () {
							 | 
						|
												return this.getTotalChapter()
							 | 
						|
											},
							 | 
						|
											split () {
							 | 
						|
												return this.getSplit()
							 | 
						|
											},
							 | 
						|
											pageType () {
							 | 
						|
												return this.getPageType()
							 | 
						|
											},
							 | 
						|
											//展示头部
							 | 
						|
											headerShow () {
							 | 
						|
												return this.pageType == 'scroll' ? this.getHeaderShow() : typeof this.chapter.headerShow == 'boolean' ? this.chapter.headerShow : this.getHeaderShow()//判断是否显示头部
							 | 
						|
											},
							 | 
						|
											//展示底部
							 | 
						|
											footerShow () {
							 | 
						|
												return this.pageType == 'scroll' ? this.getFooterShow() : typeof this.chapter.footerShow == 'boolean' ? this.chapter.footerShow : this.getFooterShow()//判断是否显示头部
							 | 
						|
											},
							 | 
						|
											contentWidth () {
							 | 
						|
												return this.windowWidth - (2 * this.slide)
							 | 
						|
											},
							 | 
						|
											contentHeight () {
							 | 
						|
												return this.windowHeight - this.topGap - this.bottomGap - (this.headerShow ? 30 : 0) - (this.footerShow ? 30 : 0)
							 | 
						|
											}
							 | 
						|
										},
							 | 
						|
										data () {
							 | 
						|
											return {
							 | 
						|
												pages: [],//渲染页面数组
							 | 
						|
												chapter: {},//章节内容临时存储
							 | 
						|
												contents: [],//内容转化数组
							 | 
						|
												success: null,//成功回调
							 | 
						|
												fail: null,//失败回调,
							 | 
						|
												chineseSize: 0,//中文字符尺寸
							 | 
						|
												tibetanSize: 0,//藏文尺寸
							 | 
						|
												spaceSize: 0,//空格尺寸
							 | 
						|
												lowerSize: 0,//小写英文尺寸
							 | 
						|
												upperSize: 0,//大写英文尺寸
							 | 
						|
												numberSize: 0,//数字尺寸
							 | 
						|
												specialSize: 0//特殊字符尺寸
							 | 
						|
											}
							 | 
						|
										},
							 | 
						|
										methods: {
							 | 
						|
											async start ({chapter, success, fail}) {
							 | 
						|
												await this.getComputedTextSize()
							 | 
						|
												this.chapter = chapter//记录章节内容
							 | 
						|
												this.success = success
							 | 
						|
												this.fail = fail
							 | 
						|
												if ( !this.chapter.content ) this.handleSuccess()
							 | 
						|
												else {
							 | 
						|
													const content = this.chapter.content.replace(/\t/g, ' ').replace(/ /g, ' ')
							 | 
						|
													this.contents = this.contentToArr(content)//初始化内容,并将文本内容转为数组
							 | 
						|
													this.computedPage()
							 | 
						|
												}
							 | 
						|
											},
							 | 
						|
											//计算页面
							 | 
						|
											computedPage () {
							 | 
						|
												const start = this.pages.length > 0 ? this.pages[this.pages.length-1].end : 0//获取字符开始位置
							 | 
						|
												//新增页面
							 | 
						|
												const page = this.computedNextPage(start)//计算页面
							 | 
						|
												this.pages.push( Object.assign({}, this.chapter, {
							 | 
						|
													content: page.text.join(''),
							 | 
						|
													contents: page.text,
							 | 
						|
													type: 'text',
							 | 
						|
													start: start,
							 | 
						|
													end: page.end,
							 | 
						|
												}) )
							 | 
						|
												if ( page.end < this.contents.length - 1 ) this.computedPage()
							 | 
						|
												else this.handleSuccess()
							 | 
						|
											},
							 | 
						|
											computedNextPage (start) {
							 | 
						|
												const contentSync = this.contents.slice(start), text = []
							 | 
						|
												let pageHeight = this.fontSize + this.lineGap, length = 0, lastIndex = 0, end = 0
							 | 
						|
												while ( pageHeight <= this.contentHeight ) {
							 | 
						|
													text.push('');
							 | 
						|
													let lineWidth = 0
							 | 
						|
													for ( let i = lastIndex; i < contentSync.length; i++ ) {
							 | 
						|
														const char = contentSync[i]
							 | 
						|
														lineWidth += this.measureText(char, this.fontSize)
							 | 
						|
														if ( JSON.stringify(char) == JSON.stringify('\r') || JSON.stringify(char) == JSON.stringify('\n') ) {
							 | 
						|
															length += 1
							 | 
						|
															end = start + length;
							 | 
						|
															lastIndex = i + 1;
							 | 
						|
															break;
							 | 
						|
														} else if ( lineWidth >= this.contentWidth ) {
							 | 
						|
															lastIndex = i;
							 | 
						|
															break;
							 | 
						|
														} else {
							 | 
						|
															text[text.length - 1] += char
							 | 
						|
															length += 1;
							 | 
						|
															end = start + length;
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
													pageHeight += this.fontSize + this.lineGap
							 | 
						|
													if ( end >= contentSync.length - 1 + start ) break;
							 | 
						|
												}
							 | 
						|
												return { start, end, text }
							 | 
						|
											},
							 | 
						|
											//成功回调
							 | 
						|
											handleSuccess () {
							 | 
						|
												const slots1 = this.chapter.frontSlots || []//获取章节前置插槽
							 | 
						|
												const slots2 = this.chapter.backSlots || []//获取章节后置插槽
							 | 
						|
												slots1.reverse()//反转前置插槽
							 | 
						|
												//插入前置插槽
							 | 
						|
												slots1.forEach(name => {
							 | 
						|
													const start = this.pages.length > 0 ? this.pages[0].start : 2
							 | 
						|
													this.pages.unshift(Object.assign({}, this.chapter, {
							 | 
						|
														type: 'slot',
							 | 
						|
														content: name,
							 | 
						|
														start: start - 2,
							 | 
						|
														end: start - 1,
							 | 
						|
													}))
							 | 
						|
												})
							 | 
						|
												//插入后置插槽
							 | 
						|
												slots2.forEach(name => {
							 | 
						|
													const end = this.pages.length > 0 ? this.pages[this.pages.length - 1].end : -1
							 | 
						|
													this.pages.push(Object.assign({}, this.chapter, {
							 | 
						|
														type: 'slot',
							 | 
						|
														content: name,
							 | 
						|
														start: end + 1,
							 | 
						|
														end: end + 2,
							 | 
						|
													}))
							 | 
						|
												})
							 | 
						|
												this.pages = this.pages.map((p, key) => {
							 | 
						|
													const total = this.pages.length
							 | 
						|
													const current = key + 1
							 | 
						|
													const rate = 1 / this.totalChapter
							 | 
						|
													const progress = this.totalChapter ? (rate * (current / total)) + ((p.index - 1) * rate) : 0
							 | 
						|
													return Object.assign({}, p, {total: total, current: current, progress: progress * 100})
							 | 
						|
												})
							 | 
						|
												this.success && this.success(this.pages)
							 | 
						|
												this.success = null
							 | 
						|
												this.fail = null
							 | 
						|
												this.pages = []
							 | 
						|
												this.contents = []
							 | 
						|
												this.chapter = {}
							 | 
						|
											},
							 | 
						|
											//将文本内容转为数组
							 | 
						|
											contentToArr (content) {
							 | 
						|
												const arr = this.split ? [] : content.split('')
							 | 
						|
												if ( arr.length == 0 ) {//如果传入了分隔符
							 | 
						|
													let chars = ''//临时字符串
							 | 
						|
													for ( let i = 0; i < content.length; i++ ) {
							 | 
						|
														const char = content.charAt(i)
							 | 
						|
														if ( /\r|\n/.test(char) ) {//如果是换行符
							 | 
						|
															if ( chars ) arr.push(chars)//直接将先前存储的字符push进数组
							 | 
						|
															arr.push(char)//再将标签push进数组
							 | 
						|
															chars = ''//清空临时字符串
							 | 
						|
														} else if ( this.split.indexOf(char) > -1 ) {//如果是分隔符
							 | 
						|
															chars += char//将分隔符加入字符串
							 | 
						|
															arr.push(chars)//将字符串push进数组
							 | 
						|
															chars = ''//清空临时字符串
							 | 
						|
														} else {//其余字符先存着
							 | 
						|
															chars += char
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												return arr
							 | 
						|
											},
							 | 
						|
											measureText (text, fontSize=10) {
							 | 
						|
											  text = new String(text);
							 | 
						|
											  text = text.split('');
							 | 
						|
											  let width = 0;
							 | 
						|
											  text.forEach((item) => {
							 | 
						|
												const index = this.charSize.findIndex(char => char.text.indexOf(item) > -1)
							 | 
						|
												if ( index > -1 ) {
							 | 
						|
												  width += this.charSize[index].size || 0
							 | 
						|
												} else if (/[a-z]/.test(item)) {
							 | 
						|
											      width += this.measureSize.lower || this.lowerSize || 7
							 | 
						|
											    } else if ( /[A-Z]/.test(item) ) {
							 | 
						|
												  width += this.measureSize.upper || this.upperSize || 7
							 | 
						|
												} else if (/[0-9]/.test(item)) {
							 | 
						|
											      width += this.measureSize.number || this.numberSize || 5.5
							 | 
						|
											    } else if (/[\u4e00-\u9fa5]/.test(item)) { //中文匹配
							 | 
						|
											      width += this.measureSize.chinese || this.chineseSize || 10
							 | 
						|
											    } else if (/[\u0f00-\u0fff]/.test(item)) { //藏文匹配
							 | 
						|
											      width += this.measureSize.tibetan || this.tibetanSize || 4.5
							 | 
						|
											    } else if (/\s/.test(item)) {
							 | 
						|
											      width += this.measureSize.space || this.spaceSize || 3.5
							 | 
						|
											    } else if (/[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(item)) {
							 | 
						|
											      width += this.measureSize.special || this.specialSize || 8
							 | 
						|
											    } else {
							 | 
						|
											      width += this.measureSize.other || this.chineseSize || 10
							 | 
						|
											    }
							 | 
						|
											  });
							 | 
						|
											  return width * fontSize / 10;
							 | 
						|
											},
							 | 
						|
											async getComputedTextSize (selector, el) {
							 | 
						|
												let arr = []
							 | 
						|
												arr.push(this.getSize('.computed-text-chinese', this.$refs.computedTextChinese))
							 | 
						|
												arr.push(this.getSize('.computed-text-space', this.$refs.computedTextSpace))
							 | 
						|
												arr.push(this.getSize('.computed-text-lower', this.$refs.computedTextLower))
							 | 
						|
												arr.push(this.getSize('.computed-text-upper', this.$refs.computedTextUpper))
							 | 
						|
												arr.push(this.getSize('.computed-text-number', this.$refs.computedTextNumber))
							 | 
						|
												arr.push(this.getSize('.computed-text-special', this.$refs.computedTextSpecial))
							 | 
						|
												arr.push(this.getSize('.computed-text-tibetan', this.$refs.computedTextTibetan))
							 | 
						|
												const ress = await Promise.all(arr)
							 | 
						|
												this.chineseSize = ress[0].width * (10 / 20)
							 | 
						|
												this.spaceSize = ress[1].width * (10 / 20)
							 | 
						|
												this.lowerSize = ress[2].width * (10 / 20)
							 | 
						|
												this.upperSize = ress[3].width * (10 / 20)
							 | 
						|
												this.numberSize = ress[4].width * (10 / 20)
							 | 
						|
												this.specialSize = ress[5].width * (10 / 20)
							 | 
						|
												this.tibetanSize = ress[6].width * (10 / 20)
							 | 
						|
												// console.log('chineseSize', this.chineseSize);
							 | 
						|
												// console.log('spaceSize', this.spaceSize);
							 | 
						|
												// console.log('lowerSize', this.lowerSize);
							 | 
						|
												// console.log('upperSize', this.upperSize);
							 | 
						|
												// console.log('numberSize', this.numberSize);
							 | 
						|
												// console.log('specialSize', this.specialSize);
							 | 
						|
												// console.log('tibetanSize', this.tibetanSize);
							 | 
						|
											},
							 | 
						|
											getSize (selector, el) {
							 | 
						|
												return new Promise(resolve => {
							 | 
						|
													// #ifndef APP-NVUE
							 | 
						|
													uni.createSelectorQuery().in(this).select(selector).boundingClientRect(data => {
							 | 
						|
														resolve(data)
							 | 
						|
													}).exec();
							 | 
						|
													// #endif
							 | 
						|
													// #ifdef APP-NVUE
							 | 
						|
													uni.requireNativePlugin('dom').getComponentRect(el, res => {
							 | 
						|
														resolve(res.size)
							 | 
						|
													})
							 | 
						|
													// #endif
							 | 
						|
												})
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								</script>
							 | 
						|
								
							 | 
						|
								<style scoped>
							 | 
						|
									.yingbing-text-reader-computed {
							 | 
						|
										position: absolute;
							 | 
						|
										top: -1000px;
							 | 
						|
										left: 0;
							 | 
						|
										/* #ifndef APP-NVUE */
							 | 
						|
										display: flex;
							 | 
						|
										/* #endif */
							 | 
						|
										flex-direction: row;
							 | 
						|
										flex-wrap: wrap;
							 | 
						|
										visibility: hidden;
							 | 
						|
									}
							 | 
						|
									.yingbing-text-reader-computed .computed-text {
							 | 
						|
										font-size: 20px;
							 | 
						|
										flex-shrink: 0;
							 | 
						|
									}
							 | 
						|
								</style>
							 |