|
export default function Wholereader ({container, autoplay, interval, content, title, color, background, fontSize, fontFamily, split, lineGap, topGap, bottomGap, slide, pageType, backShow, headerShow, footerShow, unableClickPage, selectable }){
|
|
if(!(this instanceof Wholereader)){ //如果this不是指向MyClass
|
|
throw new TypeError("TypeError: Class constructor Wholereader cannot be invoked without 'new'")
|
|
}
|
|
this.container = typeof container == 'string' ? document.querySelector('#' + container) : container//容器
|
|
this.content = content || ''//小说类容
|
|
this.title = title || ''//小说标题
|
|
this.color = color || '#333333'//字体颜色
|
|
this.background = background || '#fcd281'//页面背景
|
|
this.fontSize = parseInt(fontSize || 15)//字体大小
|
|
this.fontFamily = fontFamily || 'Microsoft YaHei, 微软雅黑'//字体
|
|
this.split = split || ''//分隔符
|
|
this.lineGap = parseInt(lineGap || 15)//行间隔
|
|
this.topGap = parseInt(topGap || 10)//顶部间隔
|
|
this.bottomGap = parseInt(bottomGap || 10)//底部间隔
|
|
this.slide = parseInt(slide || 20)//左右间隔
|
|
this.pageType = pageType || 'real'//翻页类型
|
|
this.backShow = backShow//显示返回按钮
|
|
this.headerShow = headerShow//显示顶部
|
|
this.footerShow = footerShow//显示底部
|
|
this.unableClickPage = unableClickPage//关闭点击翻页
|
|
this.selectable = selectable//开启文本选择
|
|
this.autoplay = autoplay || false//自动播放
|
|
this.interval = interval || 5000//自动播放周期
|
|
this._contents = []//内容集合
|
|
this._wrapperEl = null//内容盒子
|
|
this._scrollerEl = null//滚动盒子
|
|
this._viewWidth = 0//容器宽度
|
|
this._viewHeight = 0//容器高度
|
|
this._contentWidth = 0//内容宽度
|
|
this._contentHeight = 0//内容高度
|
|
this._start = 0//当前页开始位置
|
|
this._end = 0//当前页开始位置
|
|
this._pageWating = false//翻页等待
|
|
this._touchTimer = null//触摸事件定时器
|
|
this._autoplayTimer = null//自动播放定时器
|
|
this._touchTime = 0//触摸时间
|
|
this._touchstartX = 0//触摸开始点
|
|
this._moveX = 0//移动位置
|
|
this._pageEl = null //翻页对象
|
|
this._pageDirection = ''//翻页方向
|
|
this._updownloading = false//上下章节加载等待
|
|
this._eventCallback = {}//事件
|
|
}
|
|
//渲染页面
|
|
Object.defineProperty(Wholereader.prototype,'render',{
|
|
value: async function(start){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.render is not a constructor")
|
|
}
|
|
if ( this.container && typeof this.container != 'undefined' ) {
|
|
this._stopAutoplay()
|
|
this._contents = this._contentToArr()//将文本转为数组
|
|
if ( this._wrapperEl ) this.container.removeChild(this._wrapperEl)//清空容器
|
|
this._viewWidth = this.container.offsetWidth//获取容器宽度
|
|
this._viewHeight = this.container.offsetHeight//获取容器高度
|
|
this._contentWidth = this._viewWidth - (2 * this.slide)//获取内容宽度
|
|
this._contentHeight = this._viewHeight - this.topGap - this.bottomGap - (this.headerShow ? 30 : 0) - (this.footerShow ? 30 : 0)//获取内容高度
|
|
//创建内容盒子
|
|
this._wrapperEl = document.createElement('DIV')
|
|
this._wrapperEl.setAttribute('class', ( this.selectable ? 'whole-reader-wrapper-selectable ' : '' ) + 'whole-reader-wrapper ' + (this.pageType == 'scroll' ? 'whole-scroll-reader' : 'whole-flip-reader'))
|
|
this.container.appendChild(this._wrapperEl)
|
|
this._start = start//记录当前页开始位置
|
|
const pages = [];
|
|
if ( start > 0 ) pages.push(this._computedPrevText(start))//计算上一页
|
|
pages.push(this._computedNextText(start))//计算当前页
|
|
this._end = pages[pages.length - 1].end
|
|
if ( pages[pages.length - 1].end < this._contents.length - 1 ) pages.push(this._computedNextText(pages[pages.length - 1].end))//计算下一页
|
|
if ( this.pageType == 'scroll' ) {//滚动阅读
|
|
this._wrapperEl.style.padding = `${this.topGap}px ${this.slide}px ${this.topGap}px ${this.slide}px`;
|
|
this._wrapperEl.style.background = this.background;
|
|
this._wrapperEl.style.color = this.color;
|
|
if ( this.headerShow ) {//开启头部显示
|
|
const header = this._createHeaderDom()
|
|
this._wrapperEl.appendChild(header)
|
|
}
|
|
//创建滚动元素
|
|
this._scrollerEl = document.createElement('div')
|
|
this._scrollerEl.setAttribute('class', 'whole-scroll-reader-content')
|
|
this._wrapperEl.appendChild(this._scrollerEl)
|
|
if ( this.footerShow ) {//开启底部显示
|
|
const footer = await this._createFooterDom(this._start)
|
|
this._wrapperEl.appendChild(footer)
|
|
}
|
|
pages.forEach(page => {
|
|
this._scrollerEl.appendChild(this._createTextDom(page))//创建页面元素
|
|
})
|
|
const scrollItems = this._scrollerEl.getElementsByClassName('whole-scroll-reader-content-text')
|
|
let offsetHeight = 0;
|
|
for ( let i = 0; i < scrollItems.length; i++ ) {
|
|
offsetHeight += i > 0 ? scrollItems[i - 1].offsetHeight : 0;
|
|
if ( this._start >= scrollItems[i].getAttribute('start') && this._start < scrollItems[i].getAttribute('end') ) {
|
|
this._scrollerEl.scrollTop = offsetHeight;
|
|
this._end = scrollItems[i].getAttribute('end');
|
|
}
|
|
}
|
|
this._scrollerEl.onscroll = this._scroll.bind(this)
|
|
} else {//翻页阅读
|
|
for ( let i = 0; i < pages.length; i++ ) this._wrapperEl.appendChild(await this._createPageDom(pages[i]))//创建页面元素
|
|
this._pageChange()//触发change
|
|
//绑定touch事件
|
|
this._wrapperEl.ontouchstart = this._touchstart.bind(this)
|
|
this._wrapperEl.ontouchmove = this._touchmove.bind(this)
|
|
this._wrapperEl.ontouchend = this._touchaction.bind(this)
|
|
this._wrapperEl.ontouchcancel = this._touchaction.bind(this)
|
|
//兼容pc端
|
|
this._wrapperEl.onmousedown = (e) => {
|
|
this._mousedown = true
|
|
this._touchstart({touches: [{pageX: e.pageX, pageY: e.pageY}]})
|
|
}
|
|
this._wrapperEl.onmousemove = (e) => {
|
|
if ( !this._mousedown ) return
|
|
this._touchmove({touches: [{pageX: e.pageX, pageY: e.pageY}]})
|
|
}
|
|
this._wrapperEl.onmouseup = (e) => {
|
|
this._mousedown = false
|
|
this._touchaction({touches: [{pageX: e.pageX, pageY: e.pageY}]})
|
|
}
|
|
}
|
|
this._startAutoplay()
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//刷新页面
|
|
Object.defineProperty(Wholereader.prototype,'refresh',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.refresh is not a constructor")
|
|
}
|
|
if ( this.container && typeof this.container != 'undefined' ) {
|
|
if ( this.pageType != 'scroll' && this._wrapperEl && this._scrollerEl ) {//滚动阅读改为翻页阅读时
|
|
this._wrapperEl.removeChild(this._scrollerEl)
|
|
this._scrollerEl = null
|
|
}
|
|
this.render(this._start)
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//设置参数
|
|
Object.defineProperty(Wholereader.prototype,'setConfig',{
|
|
value:function(attribute, value){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.setConfig is not a constructor")
|
|
}
|
|
this[attribute] = value
|
|
if ( attribute == 'autoplay' ) this._startAutoplay()
|
|
},
|
|
enumerable:false
|
|
})
|
|
//销毁
|
|
Object.defineProperty(Wholereader.prototype,'destroy',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.destroy is not a constructor")
|
|
}
|
|
if ( this._wrapperEl ) {
|
|
this._stopAutoplay()
|
|
if ( this._touchtimer ) {
|
|
window.clearTimeout(this._touchtimer)
|
|
this._touchtimer = null
|
|
}
|
|
if ( this._scrollerEl ) {
|
|
this._wrapperEl.removeChild(this._scrollerEl)
|
|
this._scrollerEl = null
|
|
}
|
|
this.container.removeChild(this._wrapperEl)
|
|
this._wrapperEl = null
|
|
this._pageEl = null
|
|
this._eventCallback = {}
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
// 注册事件
|
|
Object.defineProperty(Wholereader.prototype,'on',{
|
|
value:function(name, callback){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.on is not a constructor")
|
|
}
|
|
this._eventCallback[name] = callback
|
|
},
|
|
enumerable:false
|
|
})
|
|
//往前翻页
|
|
Object.defineProperty(Wholereader.prototype,'prev',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.prev is not a constructor")
|
|
}
|
|
if ( this.pageType == 'scroll' ) {//滚动阅读
|
|
const items = this._scrollerEl.getElementsByClassName('whole-scroll-reader-content-text')
|
|
let index = 0
|
|
for ( let i = 0; i < items.length; i++ ) if ( items[i].getAttribute('start') == this._start ) index = i
|
|
if ( index > 0 ) items[index - 1].scrollIntoView({behavior: 'smooth', 'block': 'end'})//滚动到上一个内容
|
|
else this._scrollerEl.scrollTop = 0
|
|
} else {//翻页阅读
|
|
if ( !this._pageWating ) {
|
|
this._touchstartX = 1
|
|
this._pageEl = this._getPageActived(-1)
|
|
this._pageDirection = 'prev'
|
|
this._touchaction()
|
|
}
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//往后翻页
|
|
Object.defineProperty(Wholereader.prototype,'next',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader.next is not a constructor")
|
|
}
|
|
if ( this.pageType == 'scroll' ) {//滚动阅读
|
|
const items = this._scrollerEl.getElementsByClassName('whole-scroll-reader-content-text')
|
|
let index = items.length - 1
|
|
for ( let i = 0; i < items.length; i++ ) if ( items[i].getAttribute('start') == this._start ) index = i
|
|
if ( index < items.length - 1 ) items[index + 1].scrollIntoView({behavior: 'smooth'})//滚动到下一个内容
|
|
else this._scrollerEl.scrollTop = this._scrollerEl.scrollHeight
|
|
} else {//翻页阅读
|
|
if ( !this._pageWating ) {
|
|
this._touchstartX = this._viewWidth
|
|
this._pageEl = this._getPageActived(0)
|
|
this._pageDirection = 'next'
|
|
this._touchaction()
|
|
}
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//开启自动播放
|
|
Object.defineProperty(Wholereader.prototype,'_startAutoplay',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._startAutoplay is not a constructor")
|
|
}
|
|
this._stopAutoplay()
|
|
if ( !this.autoplay ) return
|
|
if ( this.pageType == 'scroll' ) {//滚动阅读
|
|
this._autoplayTimer = window.setInterval(() => {
|
|
if ( this._scrollerEl.scrollTop < this._scrollerEl.scrollHeight - this._contentHeight ) this._scrollerEl.scrollTop += 1
|
|
}, 20)
|
|
} else {//翻页阅读
|
|
this._autoplayTimer = window.setTimeout(() => {
|
|
let index = 0
|
|
const items = this._wrapperEl.getElementsByClassName('whole-flip-reader-page-item')
|
|
for ( let i = 0; i < items.length; i++ ) if ( items[i].getAttribute('start') == this._start ) index = i
|
|
if ( index < items.length - 1 ) this.next()
|
|
else this._startAutoplay()
|
|
}, this.interval)
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//关闭自动播放
|
|
Object.defineProperty(Wholereader.prototype,'_stopAutoplay',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._stopAutoplay is not a constructor")
|
|
}
|
|
if ( !this._autoplayTimer ) return
|
|
if ( this.pageType == 'scroll' ) window.clearInterval(this._autoplayTimer)//滚动阅读
|
|
else window.clearTimeout(this._autoplayTimer)//翻页阅读
|
|
this._autoplayTimer = null
|
|
},
|
|
enumerable:false
|
|
})
|
|
//创建一个独立的canvas画板,用于计算文字布局
|
|
Object.defineProperty(Wholereader.prototype,'_createComputedCanvas',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._createComputedCanvas is not a constructor")
|
|
}
|
|
const canvasDom = document.createElement('canvas');
|
|
canvasDom.width = this._contentWidth;
|
|
canvasDom.height = this._contentHeight;
|
|
const context = canvasDom.getContext('2d', {alpha: false});
|
|
context.font = this.fontSize + 'px ' + this.fontFamily
|
|
context.imageSmoothingEnabled = false
|
|
context.lineWidth = 1
|
|
return context
|
|
},
|
|
enumerable:false
|
|
})
|
|
//测量文字(备用)
|
|
Object.defineProperty(Wholereader.prototype,'_measureText',{
|
|
value:function(text){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._measureText is not a constructor")
|
|
}
|
|
const span = document.createElement('SPAN');
|
|
span.style.fontSize = this.fontSize + 'px'
|
|
span.style.fontFamily = this.fontFamily
|
|
span.style.whiteSpace = 'pre-wrap'
|
|
span.innerHTML = text
|
|
document.body.appendChild(span)
|
|
const width = span.offsetWidth
|
|
document.body.removeChild(span)
|
|
return width
|
|
},
|
|
enumerable:false
|
|
})
|
|
//计算当前页和下一页的文字排版
|
|
Object.defineProperty(Wholereader.prototype,'_computedNextText',{
|
|
value:function(start, end){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._computedNextText is not a constructor")
|
|
}
|
|
const context = this._createComputedCanvas()
|
|
let pageHeight = this.fontSize + this.lineGap, text = [], length = 0, lastIndex = 0
|
|
const contentSync = end ? this._contents.slice(start, end) : this._contents.slice(start)
|
|
while ( pageHeight <= this._contentHeight ) {
|
|
text.push('');
|
|
let lineWidth = 0
|
|
for ( let i = lastIndex; i < contentSync.length; i++ ) {
|
|
const char = contentSync[i]
|
|
lineWidth += context.measureText(char).width;
|
|
// lineWidth += this._measureText(char)
|
|
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};
|
|
},
|
|
enumerable:false
|
|
})
|
|
//计算上一页的文字排版
|
|
Object.defineProperty(Wholereader.prototype,'_computedPrevText',{
|
|
value:function(end){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._computedPrevText is not a constructor")
|
|
}
|
|
const context = this._createComputedCanvas()
|
|
let pageHeight = this.fontSize + this.lineGap, text = [], start = 0, length = 0, lastIndex1 = 0, lastIndex2 = 0
|
|
while ( pageHeight <= this._contentHeight ) {
|
|
if ( end - length > 0 ) {
|
|
text.unshift('');
|
|
let lineWidth = 0, contentSync = this._contents.slice(0, end)
|
|
for ( let i = end - length - 1; i >= 0; i-- ) {
|
|
const char = contentSync[i]
|
|
lineWidth += context.measureText(char).width;
|
|
// lineWidth += this._measureText(char)
|
|
if ( JSON.stringify(char) == JSON.stringify('\r') || JSON.stringify(char) == JSON.stringify('\n') ) {
|
|
lastIndex1 = i - 1;
|
|
length += 1
|
|
break;
|
|
} else if ( lineWidth >= this._contentWidth ) {
|
|
lastIndex1 = i;
|
|
break;
|
|
} else {
|
|
text[0] = char + text[0];
|
|
length += 1
|
|
start = end - length;
|
|
}
|
|
if ( start == 0 ) break;
|
|
}
|
|
pageHeight += this.fontSize + this.lineGap
|
|
} else {
|
|
if ( this.pageType != 'scroll' ) {
|
|
text.push('');
|
|
let lineWidth = 0, contentSync = this._contents.slice(end)
|
|
for ( let i = lastIndex2; i < contentSync.length; i++ ) {
|
|
const char = contentSync[i]
|
|
lineWidth += context.measureText(char).width;
|
|
// lineWidth += this._measureText(char)
|
|
if ( JSON.stringify(char) == JSON.stringify('\r') || JSON.stringify(char) == JSON.stringify('\n') ) {
|
|
lastIndex2 = i + 1;
|
|
length += 1
|
|
break;
|
|
} else if ( lineWidth >= this._contentWidth ) {
|
|
lastIndex2 = i;
|
|
break;
|
|
} else {
|
|
text[text.length - 1] += char;
|
|
length += 1;
|
|
end = start + length;
|
|
}
|
|
}
|
|
pageHeight += this.fontSize + this.lineGap
|
|
if ( end >= this._contents.length - 1 ) break;
|
|
} else break;
|
|
}
|
|
}
|
|
return {start, end, text}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//创建页面元素
|
|
Object.defineProperty(Wholereader.prototype,'_createPageDom',{
|
|
value:async function(page){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._createComputedCanvas is not a constructor")
|
|
}
|
|
//创建页面盒子
|
|
const item = document.createElement('DIV');
|
|
item.style.zIndex = this._contents.length - page.start;
|
|
item.setAttribute('class', 'whole-flip-reader-page-item whole-flip-reader-page-item-start_' + page.start + (this._start == page.start ? ' whole-flip-reader-page-item-actived' : ''));
|
|
item.setAttribute('start', page.start);
|
|
item.setAttribute('end', page.end);
|
|
//创建页面内容
|
|
const content = document.createElement('div');
|
|
content.setAttribute('class', 'whole-flip-reader-page-item-content')
|
|
content.style.width = this._viewWidth + 'px';
|
|
content.style.height = this._viewHeight + 'px';
|
|
content.style.background = this.background
|
|
content.style.color = this.color
|
|
content.style.padding = `${this.topGap}px ${this.slide}px ${this.topGap}px ${this.slide}px`;
|
|
if ( this.headerShow ) {//开启头部显示
|
|
const header = this._createHeaderDom();
|
|
content.appendChild(header);
|
|
}
|
|
//创建文字元素
|
|
const text = this._createTextDom(page);
|
|
content.appendChild(text)
|
|
if ( this.footerShow ) {//开启底部显示
|
|
const footer = await this._createFooterDom(page.start)
|
|
content.appendChild(footer)
|
|
}
|
|
item.appendChild(content);
|
|
//创建背景
|
|
const bg = document.createElement('DIV');
|
|
bg.setAttribute('class', 'whole-flip-reader-page-item-bg')
|
|
bg.style.height = Math.sqrt(Math.pow(this._viewHeight, 2) + Math.pow(this._viewWidth, 2)) + 'px';
|
|
bg.style.background = this.background;
|
|
item.appendChild(bg);
|
|
//创建阴影区域
|
|
const shadow = document.createElement('DIV');
|
|
shadow.setAttribute('class', 'whole-flip-reader-page-item-shadow')
|
|
item.appendChild(shadow);
|
|
if ( page.start < this._start ) this._pageAnimation(-this._viewWidth, 0, { box: item, content, bg, shadow })//如果是上一页内容则设置已经翻页样式
|
|
return item
|
|
},
|
|
enumerable:false
|
|
})
|
|
//创建头部元素
|
|
Object.defineProperty(Wholereader.prototype,'_createHeaderDom',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._createHeaderDom is not a constructor")
|
|
}
|
|
const header = document.createElement('DIV');
|
|
header.setAttribute('class', 'whole-reader-header')
|
|
header.innerHTML = `<span class="whole-reader-header-text" style="color: ${this.color}">${this.title}</span>`
|
|
if ( this.backShow ) {
|
|
const back = document.createElement('DIV')
|
|
back.setAttribute('class', 'whole-reader-header-back')
|
|
back.style.borderTopColor = this.color
|
|
back.style.borderLeftColor = this.color
|
|
back.ontouchstart = function (e) { e.stopPropagation && e.stopPropagation(); }
|
|
back.ontouchmove = function (e) { e.stopPropagation && e.stopPropagation(); }
|
|
back.onmousedown = function (e) { e.stopPropagation && e.stopPropagation(); }
|
|
back.onmousemove = function (e) { e.stopPropagation && e.stopPropagation(); }
|
|
back.ontouchend = (e) => {
|
|
e.stopPropagation && e.stopPropagation();
|
|
window.setTimeout(() => { this._eventCallback.back && this._eventCallback.back() }, 50)//不加延迟可能会造成返回或者跳转页面时,触发页面相同位置点击事件
|
|
}
|
|
back.onmouseup = (e) => {
|
|
e.stopPropagation && e.stopPropagation();
|
|
if ( !this._mousedown ) return
|
|
window.setTimeout(() => { this._eventCallback.back && this._eventCallback.back() }, 50)//不加延迟可能会造成返回或者跳转页面时,触发页面相同位置点击事件
|
|
}
|
|
header.insertBefore(back, header.firstChild)
|
|
}
|
|
// if ( this.backShow ) header.innerHTML = `<div class="whole-reader-header-back" style="border-top-color: ${this.color};border-left-color: ${this.color}"></div>` + header.innerHTML
|
|
return header
|
|
},
|
|
enumerable:false
|
|
})
|
|
//创建底部元素
|
|
Object.defineProperty(Wholereader.prototype,'_createFooterDom',{
|
|
value:async function(start){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._createFooterDom is not a constructor")
|
|
}
|
|
const progress = ((start / this._contents.length) * 100).toFixed(2)
|
|
const d = new Date()
|
|
const time = (d.getHours() < 10 ? ('0' + d.getHours()) : d.getHours()) + ':' + (d.getMinutes() < 10 ? ('0' + d.getMinutes()) : d.getMinutes())
|
|
const footer = document.createElement('DIV');
|
|
footer.setAttribute('class', 'whole-reader-footer')
|
|
footer.innerHTML = `
|
|
<div class="whole-reader-footer-left">
|
|
${await this._createBatteryDom()}
|
|
<span class="whole-reader-footer-text" style="color: ${this.color}">${time}</span>
|
|
</div>
|
|
<span class="whole-reader-footer-text">${progress}%</span>
|
|
`
|
|
return footer
|
|
},
|
|
enumerable:false
|
|
})
|
|
//创建电池元素
|
|
Object.defineProperty(Wholereader.prototype,'_createBatteryDom',{
|
|
value: async function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._createBatteryDom is not a constructor")
|
|
}
|
|
const max = 16
|
|
const res = window.navigator.getBattery ? await window.navigator.getBattery() : {level: 1}
|
|
const value = res.level * max
|
|
return `
|
|
<div class="whole-reader-battery">
|
|
<div class="whole-reader-battery-wrapper" :style="border-color: ${this.color}">
|
|
<div class="whole-reader-battery-content">
|
|
<div class="whole-reader-battery-content-value" style="background-color: ${this.color};width: ${value}px"></div>
|
|
</div>
|
|
</div>
|
|
<div class="whole-reader-battery-top" style="background-color: ${this.color}"></div>
|
|
</div>
|
|
`
|
|
},
|
|
enumerable:false
|
|
})
|
|
//创建文字元素
|
|
Object.defineProperty(Wholereader.prototype,'_createTextDom',{
|
|
value:function(page){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._createTextDom is not a constructor")
|
|
}
|
|
const dom = document.createElement('DIV')
|
|
dom.setAttribute('class', this.pageType == 'scroll' ? 'whole-scroll-reader-content-text' : 'whole-flip-reader-content-text')
|
|
dom.setAttribute('start', page.start)
|
|
dom.setAttribute('end', page.end)
|
|
page.text.forEach(t => {
|
|
const p = document.createElement('P');
|
|
p.style.height = this.fontSize + 'px';
|
|
p.style.marginTop = this.lineGap + 'px';
|
|
p.style.fontSize = this.fontSize + 'px';
|
|
p.style.fontFamily = this.fontFamily;
|
|
p.style.whiteSpace = 'pre-wrap';
|
|
p.innerHTML = t || ' ';
|
|
dom.appendChild(p)
|
|
})
|
|
return dom
|
|
},
|
|
enumerable:false
|
|
})
|
|
//滚动模式滚动事件
|
|
Object.defineProperty(Wholereader.prototype,'_scroll',{
|
|
value: async function(e){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._scroll is not a constructor")
|
|
}
|
|
try{
|
|
const scrollItems = this._scrollerEl.getElementsByClassName('whole-scroll-reader-content-text');
|
|
const scrollTop = this._scrollerEl.scrollTop + this.topGap + this.bottomGap + (this.headerShow ? 30 : 0) + (this.footerShow ? 30 : 0)
|
|
for ( let i = 0; i < scrollItems.length; i++ ) {
|
|
const offsetTop1 = scrollItems[i].offsetTop;
|
|
const offsetTop2 = i < scrollItems.length - 1 ? scrollItems[i+1].offsetTop : offsetTop1 + 2;
|
|
if ( scrollTop >= offsetTop1 && scrollTop < offsetTop2 ) {
|
|
const start = parseInt(scrollItems[i].getAttribute('start'));
|
|
const end = parseInt(scrollItems[i].getAttribute('end'));
|
|
if ( this._start != start ) {
|
|
this._start = start
|
|
this._end = end
|
|
if ( this.footerShow ) {//如果页面位置发生改变,则更新footer
|
|
const newFooter = await this._createFooterDom(start)
|
|
const oldFooter = this._wrapperEl.getElementsByClassName('whole-reader-footer')[0]
|
|
this._wrapperEl.removeChild(oldFooter)
|
|
this._wrapperEl.appendChild(newFooter)
|
|
}
|
|
this._pageChange()
|
|
}
|
|
}
|
|
}
|
|
if ( Math.ceil(this._scrollerEl.scrollTop + this._scrollerEl.offsetHeight) >= this._scrollerEl.scrollHeight ) {//触底
|
|
if ( this._updownloading ) return;
|
|
this._updownloading = true;
|
|
const end = parseInt(this._scrollerEl.lastChild.getAttribute('end'));
|
|
if ( end < this._contents.length - 1 ) {
|
|
const page = this._computedNextText(end)
|
|
const item = this._createTextDom(page)
|
|
this._scrollerEl.appendChild(item)
|
|
if ( this._scrollerEl.getElementsByClassName('whole-scroll-reader-content-text').length > 3 ) this._scrollerEl.removeChild(this._scrollerEl.firstChild);
|
|
this._scrollerEl.scrollTop = this._scrollerEl.scrollHeight - this._scrollerEl.lastChild.offsetHeight - this._scrollerEl.offsetHeight;
|
|
} else this._eventCallback.ended && this._eventCallback.ended()//后翻页完成事件
|
|
this._updownloading = false;
|
|
}
|
|
if ( this._scrollerEl.scrollTop <= 0 ) {//触顶
|
|
if ( this._updownloading ) return
|
|
this._updownloading = true;
|
|
const start = parseInt(this._scrollerEl.firstChild.getAttribute('start'));
|
|
if ( start > 0 ) {
|
|
const page = this._computedPrevText(start)
|
|
const item = this._createTextDom(page)
|
|
this._scrollerEl.insertBefore(item, this._scrollerEl.firstChild)
|
|
this._scrollerEl.scrollTop = item.offsetHeight
|
|
if ( this._scrollerEl.getElementsByClassName('whole-scroll-reader-content-text').length > 3 ) this._scrollerEl.removeChild(this._scrollerEl.lastChild);
|
|
} else this._eventCallback.started && this._eventCallback.started()//前翻页完成事件
|
|
this._updownloading = false;
|
|
}
|
|
}catch(e){
|
|
//TODO handle the exception
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//翻页模式触摸开始事件
|
|
Object.defineProperty(Wholereader.prototype,'_touchstart',{
|
|
value:function(e){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._touchstart is not a constructor")
|
|
}
|
|
if ( this._pageWating ) return;
|
|
this._touchTimer = window.setTimeout(() => {
|
|
this._touchTime = 200;
|
|
}, 200)
|
|
const touch = e.touches[0];
|
|
this._touchstartX = touch.pageX;
|
|
},
|
|
enumerable:false
|
|
})
|
|
//翻页模式触摸滑动事件
|
|
Object.defineProperty(Wholereader.prototype,'_touchmove',{
|
|
value:function(e){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._touchmove is not a constructor")
|
|
}
|
|
if ( this._touchstartX == 0 || (this.pageType != 'real' && this.pageType != 'cover') ) return;
|
|
const touch = e.touches[0]
|
|
if ( this._pageEl ) {
|
|
const height = this._viewHeight / 2;
|
|
const maxDeg = height / 5;
|
|
const rotateZ = this._pageDirection == 'next' ? ((touch.pageY - height) / maxDeg) : -((touch.pageY - height) / maxDeg);
|
|
this._moveX = touch.pageX - this._touchstartX;
|
|
if ( this._pageDirection == 'next' ) this._moveX > 0 ? this._moveX = 0 : null
|
|
else this._moveX < 0 ? this._moveX = 0 : null
|
|
this._pageAnimation(this._moveX, rotateZ);
|
|
} else {
|
|
if ( touch.pageX < this._touchstartX ) {
|
|
this._pageEl = this._getPageActived(0);
|
|
this._pageDirection = 'next'
|
|
} else {
|
|
this._pageEl = this._getPageActived(-1);
|
|
this._pageDirection = 'prev'
|
|
}
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//翻页模式触摸处理事件
|
|
Object.defineProperty(Wholereader.prototype,'_touchaction',{
|
|
value:function(e){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._touchaction is not a constructor")
|
|
}
|
|
window.clearTimeout(this._touchTimer);
|
|
this._touchTimer = null
|
|
if ( this._touchstartX == 0 ) return;
|
|
if ( !this._pageEl && this._touchTime < 200 && (!this.unableClickPage || this.pageType == 'none') ) {
|
|
//获取点击位置,判断向哪里翻页
|
|
if (this._touchstartX > (this._viewWidth / 4) * 3) {//向右翻页
|
|
this._pageEl = this._getPageActived(0);
|
|
this._pageDirection = 'next'
|
|
}
|
|
if (this._touchstartX < (this._viewWidth / 4)) {//向左翻页
|
|
this._pageEl = this._getPageActived(-1);
|
|
this._pageDirection = 'prev'
|
|
}
|
|
}
|
|
this._touchstartX = 0
|
|
if ( this._pageEl ) {
|
|
this._pageWating = true;
|
|
if ( this._touchTime < 200 ) {
|
|
const duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 0
|
|
const value = this._pageDirection == 'next' ? 1 : -1;
|
|
this._pageDuration(duration);
|
|
this._pageAnimation(-value * this._viewWidth);
|
|
setTimeout(() => {
|
|
this._changePageActived(value);
|
|
this._resetPageMove();
|
|
}, duration + 50)
|
|
} else {
|
|
const duration = (this.pageType == 'real' || this.pageType == 'cover') ? 100 : 0
|
|
if ( Math.abs(this._moveX) >= this._viewWidth / 4 ) {
|
|
const value = this._pageDirection == 'next' ? 1 : -1;
|
|
this._pageDuration(duration);
|
|
this._pageAnimation(-value * this._viewWidth);
|
|
setTimeout(() => {
|
|
this._changePageActived(value);
|
|
this._resetPageMove();
|
|
}, duration + 50)
|
|
} else {
|
|
this._pageDuration(duration);
|
|
this._pageAnimation(0);
|
|
setTimeout(() => {
|
|
this._resetPageMove();
|
|
}, duration + 50)
|
|
}
|
|
}
|
|
} else {
|
|
this._touchTime = 0
|
|
}
|
|
},
|
|
enumerable:false
|
|
})
|
|
//设置翻页对象动画效果
|
|
Object.defineProperty(Wholereader.prototype,'_pageAnimation',{
|
|
value:function(moveX, rotateZ = 0, el){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._pageAnimation is not a constructor")
|
|
}
|
|
const lateX = this._pageDirection == 'next' ? moveX : moveX - this._viewWidth;
|
|
const pageEl = el || this._pageEl;
|
|
pageEl.box.style.transform = `translateX(${lateX}px)`;
|
|
pageEl.box.style.boxShadow = el ? '' : '10px 10px 20px rgba(0,0,0,.2)';
|
|
pageEl.content.style.transform = this.pageType == 'real' ? `translateX(${-lateX}px)` : pageEl.content.style.transform;
|
|
pageEl.bg.style.transform = this.pageType == 'real' ? `translate(${lateX}px, -50%) rotateZ(${rotateZ}deg)` : pageEl.bg.style.transform;
|
|
pageEl.shadow.style.boxShadow = '0 0 60px ' + (this.pageType == 'real' ? Math.abs(lateX) > 30 ? 30 : Math.abs(lateX) : 0) + 'px rgba(0,0,0,0.5)';
|
|
},
|
|
enumerable:false
|
|
})
|
|
//设置翻页对象动画时间
|
|
Object.defineProperty(Wholereader.prototype,'_pageDuration',{
|
|
value:function(duration){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._pageDuration is not a constructor")
|
|
}
|
|
this._pageEl.box.style.transition = duration > 0 ? 'transform ' + duration + 'ms' : '';
|
|
this._pageEl.content.style.transition = duration > 0 ? 'transform ' + duration + 'ms' : '';
|
|
this._pageEl.bg.style.transition = duration > 0 ? 'transform ' + duration + 'ms' : '';
|
|
this._pageEl.shadow.style.transition = duration > 0 ? 'box-shadow ' + duration + 'ms' : '';
|
|
},
|
|
enumerable:false
|
|
})
|
|
//获取翻页对象
|
|
Object.defineProperty(Wholereader.prototype,'_getPageActived',{
|
|
value:function(value){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._getPageActived is not a constructor")
|
|
}
|
|
const boxs = this.container.getElementsByClassName('whole-flip-reader-page-item');
|
|
for ( let i = 0; i < boxs.length; i++ ) {
|
|
if ( boxs[i].getAttribute('class').indexOf('whole-flip-reader-page-item-actived') > 1 ) {
|
|
if ( boxs[i + value + 1] && boxs[i + value] ) {
|
|
return {
|
|
box: boxs[i + value],
|
|
content: boxs[i + value].getElementsByClassName('whole-flip-reader-page-item-content')[0],
|
|
bg: boxs[i + value].getElementsByClassName('whole-flip-reader-page-item-bg')[0],
|
|
shadow: boxs[i + value].getElementsByClassName('whole-flip-reader-page-item-shadow')[0]
|
|
};
|
|
}
|
|
}
|
|
}
|
|
if ( value < 0 ) this._eventCallback.started && this._eventCallback.started()//前翻页完成事件
|
|
else this._eventCallback.ended && this._eventCallback.ended()//后翻页完成事件
|
|
return false;
|
|
},
|
|
enumerable:false
|
|
})
|
|
//改变翻页对象
|
|
Object.defineProperty(Wholereader.prototype,'_changePageActived',{
|
|
value:async function(value){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._changePageActived is not a constructor")
|
|
}
|
|
const boxs = this.container.getElementsByClassName('whole-flip-reader-page-item');
|
|
let index = -1
|
|
for ( let i = 0; i < boxs.length; i++ ) if ( boxs[i].getAttribute('class').indexOf('page-item-actived') > -1 ) index = i
|
|
boxs[index].setAttribute('class', boxs[index].getAttribute('class').replace('whole-flip-reader-page-item-actived', ''));
|
|
boxs[index + value].setAttribute('class', boxs[index + value].getAttribute('class') + ' whole-flip-reader-page-item-actived');
|
|
this._start = parseInt(boxs[index + value].getAttribute('start'));
|
|
this._end = parseInt(boxs[index + value].getAttribute('end'));
|
|
this._pageChange()
|
|
if ( value < 0 && !boxs[index + value - 1] ) {//向前翻页
|
|
if ( this._updownloading ) return;
|
|
this._updownloading = true;
|
|
const start = parseInt(this._wrapperEl.firstChild.getAttribute('start'));
|
|
if ( start > 0 ) {
|
|
const page = this._computedPrevText(start)
|
|
const item = await this._createPageDom(page)
|
|
this._wrapperEl.insertBefore(item, this._wrapperEl.firstChild)
|
|
if ( this._wrapperEl.getElementsByClassName('whole-flip-reader-page-item').length > 3 ) this._wrapperEl.removeChild(this._wrapperEl.lastChild);
|
|
}
|
|
this._updownloading = false;
|
|
}
|
|
if ( value > 0 && !boxs[index + value + 1] ) {//向后翻页
|
|
if ( this._updownloading ) return;
|
|
this._updownloading = true;
|
|
const end = parseInt(this._wrapperEl.lastChild.getAttribute('end'));
|
|
if ( end < this._contents.length - 1 ) {
|
|
const page = this._computedNextText(end)
|
|
const item = await this._createPageDom(page)
|
|
this._wrapperEl.appendChild(item)
|
|
if ( this._wrapperEl.getElementsByClassName('whole-flip-reader-page-item').length > 3 ) this._wrapperEl.removeChild(this._wrapperEl.firstChild);
|
|
}
|
|
this._updownloading = false;
|
|
}
|
|
if ( value < 0 && boxs[index + value].getAttribute('end') != boxs[index + value + 1].getAttribute('start') ) this.refresh()//如果是向前翻页并且前一页结束位置和当前页开始位置不对应时刷新页面
|
|
},
|
|
enumerable:false
|
|
})
|
|
//页面改变事件
|
|
Object.defineProperty(Wholereader.prototype,'_pageChange',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._changePageActived is not a constructor")
|
|
}
|
|
const text = this._contents.slice(this._start, this._end)
|
|
this._eventCallback.change && this._eventCallback.change({
|
|
start: this._start,
|
|
end: this._end,
|
|
contents: text,
|
|
content: text.join('')
|
|
});
|
|
if ( this.pageType != 'scroll' ) this._startAutoplay()
|
|
},
|
|
enumerable:false
|
|
})
|
|
//重置翻页事件
|
|
Object.defineProperty(Wholereader.prototype,'_resetPageMove',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._resetPageMove is not a constructor")
|
|
}
|
|
this._pageDuration(0)
|
|
if ( this._pageEl ) this._pageEl.box.style.boxShadow = ''
|
|
this._pageWating = false;
|
|
this._moveX = 0;
|
|
this._pageEl = '';
|
|
this._pageDirection = 'next';
|
|
this._touchTime = 0;
|
|
this._touchstartX = 0;
|
|
},
|
|
enumerable:false
|
|
})
|
|
//将内容转化为数组
|
|
Object.defineProperty(Wholereader.prototype,'_contentToArr',{
|
|
value:function(){
|
|
if(!(this instanceof Wholereader)){//那么相反 不是正常调用的就是错误的调用
|
|
throw new TypeError("TypeError: Wholereader._contentToArr is not a constructor")
|
|
}
|
|
const arr = this.split ? [] : this.content.split('')
|
|
if ( arr.length == 0 ) {//如果传入了分隔符
|
|
let chars = ''//临时字符串
|
|
for ( let i = 0; i < this.content.length; i++ ) {
|
|
const char = this.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
|
|
},
|
|
enumerable:false
|
|
})
|