2023-09-21 14:49:07 +08:00

451 lines
15 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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())
}
}
}