import Util from '../../../js_sdk/util.js' export default { props: { measureSize: { type: Object, default () { return new Object } } }, data () { return { computedResolve: null, chineseSize: 0, spaceSize: 0, lowerSize: 0, upperSize: 0, numberSize: 0, specialSize: 0 } }, methods: { reset (data, pages = []) { if ( data.custom && data.custom.length > 0 ) { pages.length > 0 ? pages[pages.length - 1].isLastPage = false : null data.custom.forEach(custom => { let type = '' if ( custom.indexOf('slot:') > -1 ) { type = 'slot' custom = custom.split(':')[1] } else { type = 'custom' let clicks = custom.match(/onclick=\"*([\s\S]*?)\"/ig); if ( clicks ) { clicks.forEach(click => { let name = click.match(/onclick=\"*([\s\S]*?)(\(|\")/)[1] let func = click.match(/onclick=\"*([\s\S]*?)\"/) let args = func[1].replace(name, '') args = args ? args.slice(1, args.length - 1).replace(/\s/g, '') : '' custom = custom.replace(func[0], `onclick="triggerCustomClick('${name}', [${args}])"`) }) } } let end = pages.length > 0 ? pages[pages.length - 1].end : 0 pages.push({ chapter: data.chapter, title: data.title || '', type: type, dataId: data.chapter * 100000 + end, start: end, end: end + 10, isLastPage: false, text: custom }) }) pages[pages.length - 1].isLastPage = true } this.computedResolve(pages) this.computedResolve = null }, measureText (text, fontSize=10) { text = new String(text); text = text.split(''); let width = 0; text.forEach((item) => { 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 (/\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; }, getComputedTextSize (selector, el) { let arr = [] arr.push(Util.getRect('.computed-text-chinese', this.$refs.computedTextChinese, this)) arr.push(Util.getRect('.computed-text-space', this.$refs.computedTextSpace, this)) arr.push(Util.getRect('.computed-text-lower', this.$refs.computedTextLower, this)) arr.push(Util.getRect('.computed-text-upper', this.$refs.computedTextUpper, this)) arr.push(Util.getRect('.computed-text-number', this.$refs.computedTextNumber, this)) arr.push(Util.getRect('.computed-text-special', this.$refs.computedTextSpecial, this)) Promise.all(arr).then(ress => { ress.forEach((res, key) => { if ( key == 0 ) { this.chineseSize = res.width * (10 / 20) } if ( key == 1 ) { this.spaceSize = res.width * (10 / 20) } if ( key == 2 ) { this.lowerSize = res.width * (10 / 20) } if ( key == 3 ) { this.upperSize = res.width * (10 / 20) } if ( key == 4 ) { this.numberSize = res.width * (10 / 20) } if ( key == 5 ) { this.specialSize = res.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); }) }) }, async computedText (data, start) { let rect = await this.getRect() let viewWidth = rect.width - (this.options.slide * 2) let viewHeight = rect.height - this.options.topGap - this.options.bottomGap if ( this.options.headerShow ) viewHeight = viewHeight - uni.upx2px(50) if ( this.options.footerShow ) viewHeight = viewHeight - uni.upx2px(50) let pageHeight = this.options.fontSize + this.options.lineHeight; let strs = []; let page = { title: data.title || '', chapter: data.chapter, type: 'text', dataId: data.chapter * 100000 + start, start: start, end: 0, isLastPage: false, text: [] } let length = 0; let contentSync = data.content.substr(start).replace(/\t/g, ' ').replace(/ /g, ' '); let lastIndex = 0; while ( (pageHeight + this.options.fontSize + this.options.lineHeight) <= viewHeight ) { strs.push(''); let lineWidth = 0; let charText = '' for ( let i = lastIndex; i < contentSync.length; i++ ) { const char = contentSync.charAt(i) if ( JSON.stringify(char) == JSON.stringify('\r') || JSON.stringify(char) == JSON.stringify('\n') ) { lineWidth += this.measureText(charText, this.options.fontSize); if ( lineWidth >= viewWidth ) { lastIndex = i - charText.length+ 1; break; } strs[strs.length - 1] += charText length += charText.length + 1 page.end = page.start + length; lastIndex = i + 1; break; } charText += char if ( !this.split || char == this.split ) { lineWidth += this.measureText(charText, this.options.fontSize); if ( lineWidth >= viewWidth ) { lastIndex = i - charText.length+ 1; break; } strs[strs.length - 1] += charText length += charText.length page.end = page.start + length charText = '' } } pageHeight += this.options.fontSize + this.options.lineHeight; if ( page.end >= data.content.length - 1 ) { page.isLastPage = true; break; } } page.text = strs; return page; }, getRect () { return new Promise (resolve => { // #ifndef APP-NVUE const query = uni.createSelectorQuery().in(this); query.select('.yingbing-read-page').boundingClientRect(data => { resolve(data) }).exec(); // #endif // #ifdef APP-NVUE uni.requireNativePlugin('dom').getComponentRect(this.$refs.yingbingReadPage, res => { resolve(res.size) }) // #endif }) }, getPages (data) { let pages = []; const doWhile = (start = 0) => { this.computedText(data, start).then(page => { pages.push(page); if ( page.isLastPage ) { this.reset(data, pages) } else { doWhile(page.end); } }); } doWhile(); }, computedChapter(data) { return new Promise(resolve => { this.computedResolve = resolve data.content ? this.getPages(data) : this.reset(data) }) }, //绘制页面 resetPage(data) { setTimeout(() => { //一次最多渲染3章的内容,根据定位的章节剪切出3章内容渲染 let currentChapter = data.currentChapter || this.contents[0].chapter let nowIndex = this.contents.findIndex(item => item.chapter == currentChapter); let prevIndex = -1; let nextIndex = -1; let contents = []; if (!this.contents[nowIndex].isStart) prevIndex = this.contents.findIndex(item => item.chapter == currentChapter - 1); if (!this.contents[nowIndex].isEnd) nextIndex = this.contents.findIndex(item => item.chapter == currentChapter + 1); if (prevIndex > -1) { contents.push(this.contents[prevIndex]) } contents.push(this.contents[nowIndex]) if (nextIndex > -1) { contents.push(this.contents[nextIndex]) } let arr = []; const dowhile = (i) => { let item = contents[i]; this.computedChapter(item).then(pages => { if (currentChapter == item.chapter) { let index = Object.keys(pages).findIndex(key => data.start >= pages[key].start && data.start < pages[key].end) this.currentDataId = pages[index > -1 ? index : 0].dataId; } arr = arr.concat(pages) if (i == contents.length - 1) { if ( this.options.pageType != 'scroll' ) { if ( !this.firstTipUnable ) { arr.unshift({ title: contents[0].title || '', chapter: contents[0].chapter, type: contents[0].isStart ? 'top' : 'loading', direction: 'prev', dataId: arr[0].dataId - 1, start: 0, end: 0 }) } else if ( !contents[0].isStart ) { arr.unshift({ title: contents[0].title || '', chapter: contents[0].chapter, type: 'loading', direction: 'prev', dataId: arr[0].dataId - 1, start: 0, end: 0 }) } if ( !this.lastTipUnable ) { arr.push({ title: item.title || '', chapter: item.chapter, type: item.isEnd ? 'bottom' : 'loading', direction: 'next', dataId: arr[arr.length - 1].dataId + 1, start: 0, end: 0 }) } else if ( !contents[0].isEnd ) { arr.push({ title: item.title || '', chapter: item.chapter, type: 'loading', direction: 'next', dataId: arr[arr.length - 1].dataId + 1, start: 0, end: 0 }) } } this.pages = arr if ( this.options.pageType == 'scroll' ) { this.$refs.list.scrollTo(0) } this.$nextTick(() => { if ( this.options.pageType != 'scroll' ) { this.$refs.flip.refresh() this.handleFlipChange(this.currentDataId); } else { setTimeout(() => { Util.getRect('#scroll-item_' + this.currentDataId, Util.getRefs(this, 'scrollItem_' + this.currentDataId, 0), this).then(rect => { this.$refs.list.scrollTo(rect.top) }) }, 50) } this.initLoading = false; this.preload(currentChapter); }) } else { setTimeout(() => { dowhile(i + 1) }, 100) } }) } dowhile(0) }, 50) }, computedPage(e) { this.computedChapter(e.content).then((pages) => { let arr = []; const pagesSync = e.type == 'prev' ? pages.concat(this.pages) : this.pages.concat(pages); let newPages = pagesSync.filter(item => (item.type == 'text' || item.type == 'custom' || item.type == 'slot')) // pagesSync.forEach(item => { // if (arr.indexOf(item.chapter) == -1) arr.push(item.chapter) // }) // if (arr.length > 3) { // let reChapter = e.type == 'prev' ? pagesSync[pagesSync.length - 1].chapter : pagesSync[0].chapter; // newPages = pagesSync.filter(item => item.chapter != reChapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot')); // } else { // newPages = pagesSync.filter(item => (item.type == 'text' || item.type == 'custom' || item.type == 'slot')); // } if ( this.options.pageType != 'scroll' ) { const prevIndex = this.contents.findIndex(content => content.chapter == newPages[0].chapter); const nextIndex = this.contents.findIndex(content => content.chapter == newPages[newPages.length - 1].chapter); if ( !this.firstTipUnable && this.contents[prevIndex].isStart ) { newPages.unshift({ title: this.contents[prevIndex].title || '', chapter: this.contents[prevIndex].chapter, type: 'top', direction: 'prev', dataId: newPages[0].dataId - 1, start: 0, end: 0 }) } else { newPages.unshift({ title: this.contents[prevIndex].title || '', chapter: this.contents[prevIndex].chapter, type: 'loading', direction: 'prev', dataId: newPages[0].dataId - 1, start: 0, end: 0 }) } if ( !this.lastTipUnable && this.contents[nextIndex].isEnd ) { newPages.push({ title: this.contents[nextIndex].title || '', chapter: this.contents[nextIndex].chapter, type: 'bottom', direction: 'next', dataId: newPages[newPages.length - 1].dataId + 1, start: 0, end: 0 }) } else { newPages.push({ title: this.contents[nextIndex].title || '', chapter: this.contents[nextIndex].chapter, type: 'loading', direction: 'next', dataId: newPages[newPages.length - 1].dataId + 1, start: 0, end: 0 }) } this.pages = newPages const nowIndex = newPages.findIndex(page => page.dataId == this.currentDataId); if ( nowIndex == -1 ) { this.currentDataId = e.type == 'next' ? pages[0].dataId : pages[pages.length - 1].dataId; this.handleFlipChange(this.currentDataId) } else { this.startAutoplay() } } else { let dataId = e.type == 'prev' ? this.pages[0].dataId : this.pages[this.pages.length-1].dataId Util.getRect('.scroll-item-wrapper', this.$refs.scrollItemWrapper, this).then(rect => { let lastHeight = rect.height this.pages = e.type == 'prev' ? pages.concat(this.pages) : this.pages.concat(pages) if ( e.type == 'prev' ) { this.$nextTick(function () { setTimeout(() => { Util.getRect('.scroll-item-wrapper', this.$refs.scrollItemWrapper, this).then(rect => { this.$refs.list.scrollTo(rect.height - lastHeight) }) }, 50) }) } }) } }) }, //预加载章节 preload (chapter) { if ( !this.enablePreload ) return false const nowIndex = this.contents.findIndex(item => item.chapter == chapter); let prevIndex = -2; let nextIndex = -2; let chapters = []; if ( !this.contents[nowIndex].isStart ) prevIndex = this.contents.findIndex(item => item.chapter == chapter - 1); if ( !this.contents[nowIndex].isEnd ) nextIndex = this.contents.findIndex(item => item.chapter == chapter + 1); if ( prevIndex == -1 ) { chapters.push(chapter - 1); } if ( nextIndex == -1 ) { chapters.push(chapter + 1); } if ( chapters.length > 0 ) { this.$emit('preload', chapters, (status, contents) => { if (status == 'success') { contents.forEach(item => { const index = this.contents.findIndex(content => content.chapter == item.chapter) if (index > -1) { this.contents[index] = item; } else { this.contents.push(item); } }) } }) } }, filterPage (pageInfo) { if ( pageInfo && pageInfo.dataId > -1 ) { const nowChapters = this.pages.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot')) const currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId) if ( currentPage > -1 ) { return (currentPage + 1) + ' / ' + nowChapters.length } else { return pageInfo.type == 'top' ? '最前面' : pageInfo.type == 'bottom' ? '最后面' : pageInfo.type.indexOf('Loading') > -1 ? '请等待' : '' } } else { return '加载中' } }, filterDate () { let date = new Date() return Util.zeroize(date.getHours()) + ':' + Util.zeroize(date.getMinutes()) } } }