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