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