266 lines
9.8 KiB
JavaScript
266 lines
9.8 KiB
JavaScript
class NovelReader {
|
|
/**
|
|
* 创建一个小说阅读器。
|
|
* @param {Array} chapters - 小说的章节数组。
|
|
* @param {number} pageSize - 每页的字符数。
|
|
* @param {string} chapterElementId - 显示章节内容的元素选择器。
|
|
* @param {string} prevPageButtonIdTop - 顶部上一页按钮元素的选择器。
|
|
* @param {string} nextPageButtonIdTop - 顶部下一页按钮元素的选择器。
|
|
* @param {string} prevPageButtonIdBottom - 底部上一页按钮元素的选择器。
|
|
* @param {string} nextPageButtonIdBottom - 底部下一页按钮元素的选择器。
|
|
* @param {string} switchButtonId - 自动滚动开关按钮元素的选择器。
|
|
* @param {number} scrollStep - 自动滚动每次滚动的像素值。
|
|
*/
|
|
constructor(chapters, pageSize, chapterElementId, prevPageButtonIdTop, nextPageButtonIdTop, prevPageButtonIdBottom, nextPageButtonIdBottom, switchButtonId, scrollStep = 50) {
|
|
this.chapters = chapters;
|
|
this.pageSize = pageSize;
|
|
this.currentChapterIndex = 0;
|
|
this.currentPageIndex = 0;
|
|
this.pages = [];
|
|
this.intervalId = null;
|
|
this.isScrolling = false;
|
|
this.scrollStep = scrollStep;
|
|
|
|
this.chapterElement = document.querySelector(chapterElementId);
|
|
this.prevPageButtonTop = document.querySelector(prevPageButtonIdTop);
|
|
this.nextPageButtonTop = document.querySelector(nextPageButtonIdTop);
|
|
this.prevPageButtonBottom = document.querySelector(prevPageButtonIdBottom);
|
|
this.nextPageButtonBottom = document.querySelector(nextPageButtonIdBottom);
|
|
this.switchButton = document.querySelector(switchButtonId);
|
|
|
|
this.init();
|
|
}
|
|
|
|
/**
|
|
* 初始化小说阅读器。
|
|
*/
|
|
init() {
|
|
this.pages = this.paginateChapter(this.chapters[this.currentChapterIndex]);
|
|
this.loadPage(0);
|
|
|
|
this.prevPageButtonTop.addEventListener('click', this.prevPage.bind(this));
|
|
this.nextPageButtonTop.addEventListener('click', this.nextPage.bind(this));
|
|
this.prevPageButtonBottom.addEventListener('click', this.prevPage.bind(this));
|
|
this.nextPageButtonBottom.addEventListener('click', this.nextPage.bind(this));
|
|
this.switchButton.addEventListener('click', this.toggleScrolling.bind(this));
|
|
|
|
// 检查本地存储中的自动播放设置
|
|
const autoPlay = localStorage.getItem('autoPlay');
|
|
if (autoPlay === 'true') {
|
|
this.startScrolling();
|
|
$(this.switchButton).addClass('van-switch--on').attr('aria-checked', 'true');
|
|
}
|
|
|
|
// 监听用户滚动
|
|
window.addEventListener('scroll', () => {
|
|
if (!this.isScrolling && window.scrollY === 0 && autoPlay === 'true') {
|
|
this.startScrolling();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 将章节分页,确保标签不会被切割。
|
|
* @param {string} chapter - 章节内容。
|
|
* @return {Array} - 切分成页的章节内容。
|
|
*/
|
|
paginateChapter(chapter) {
|
|
const pages = [];
|
|
let currentPage = '';
|
|
let currentIndex = 0;
|
|
|
|
while (currentIndex < chapter.length) {
|
|
const nextSlice = chapter.slice(currentIndex, currentIndex + this.pageSize);
|
|
const lastOpenTagIndex = nextSlice.lastIndexOf('<');
|
|
const lastCloseTagIndex = nextSlice.lastIndexOf('>');
|
|
|
|
if (lastOpenTagIndex > lastCloseTagIndex) {
|
|
const nextCloseTagIndex = chapter.indexOf('>', currentIndex + this.pageSize);
|
|
if (nextCloseTagIndex === -1) {
|
|
currentPage += chapter.slice(currentIndex);
|
|
currentIndex = chapter.length;
|
|
} else {
|
|
currentPage += chapter.slice(currentIndex, nextCloseTagIndex + 1);
|
|
currentIndex = nextCloseTagIndex + 1;
|
|
}
|
|
} else {
|
|
currentPage += nextSlice;
|
|
currentIndex += this.pageSize;
|
|
}
|
|
|
|
if (currentPage.length >= this.pageSize || currentIndex >= chapter.length) {
|
|
pages.push(currentPage);
|
|
currentPage = '';
|
|
}
|
|
}
|
|
|
|
return pages;
|
|
}
|
|
|
|
/**
|
|
* 加载指定的页面。
|
|
* @param {number} index - 要加载的页码。
|
|
*/
|
|
loadPage(index) {
|
|
if (index >= 0 && index < this.pages.length) {
|
|
this.chapterElement.innerHTML = this.pages[index];
|
|
this.currentPageIndex = index;
|
|
}
|
|
this.updateNavButtons();
|
|
// 获取元素
|
|
let element = document.querySelector('#app');
|
|
// 获取元素距离浏览器滚动到顶部的距离
|
|
let distanceToTop = element.getBoundingClientRect().top + window.scrollY;
|
|
//console.log('距离顶部的距离:', distanceToTop);
|
|
$(window).scrollTop(distanceToTop);
|
|
}
|
|
|
|
/**
|
|
* 更新导航按钮。
|
|
* @param {HTMLElement} prevButton - 上一页按钮元素。
|
|
* @param {HTMLElement} nextButton - 下一页按钮元素。
|
|
*/
|
|
updateButtonState(prevButton, nextButton) {
|
|
if (this.currentPageIndex === 0) {
|
|
prevButton.textContent = '上一章';
|
|
} else {
|
|
prevButton.textContent = '上一页';
|
|
}
|
|
prevButton.disabled = this.currentPageIndex === 0 && this.currentChapterIndex === 0;
|
|
|
|
if (this.currentPageIndex === this.pages.length - 1) {
|
|
nextButton.textContent = '下一章';
|
|
} else {
|
|
nextButton.textContent = '下一页';
|
|
}
|
|
nextButton.disabled = this.currentPageIndex === this.pages.length - 1 && this.currentChapterIndex === this.chapters.length - 1;
|
|
}
|
|
|
|
/**
|
|
* 根据当前页面和章节更新导航按钮。
|
|
*/
|
|
updateNavButtons() {
|
|
this.updateButtonState(this.prevPageButtonTop, this.nextPageButtonTop);
|
|
this.updateButtonState(this.prevPageButtonBottom, this.nextPageButtonBottom);
|
|
}
|
|
|
|
/**
|
|
* 导航到上一页或上一章。
|
|
*/
|
|
prevPage() {
|
|
if (this.currentPageIndex > 0) {
|
|
this.loadPage(this.currentPageIndex - 1);
|
|
} else if (this.currentChapterIndex > 0) {
|
|
this.currentChapterIndex -= 1;
|
|
this.pages = this.paginateChapter(this.chapters[this.currentChapterIndex]);
|
|
this.loadPage(this.pages.length - 1);
|
|
}else{
|
|
// 更新 URL
|
|
let strUpdatedUrl = this.updateChapterId(location.href,intPre);
|
|
location.href=strUpdatedUrl
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 导航到下一页或下一章。
|
|
*/
|
|
nextPage() {
|
|
if (this.currentPageIndex < this.pages.length - 1) {
|
|
|
|
this.loadPage(this.currentPageIndex + 1);
|
|
} else if (this.currentChapterIndex < this.chapters.length - 1) {
|
|
this.currentChapterIndex += 1;
|
|
this.pages = this.paginateChapter(this.chapters[this.currentChapterIndex]);
|
|
|
|
this.loadPage(0);
|
|
} else {
|
|
|
|
// 更新 URL
|
|
let strUpdatedUrl = this.updateChapterId(location.href,intNext);
|
|
//console.log(strUpdatedUrl)
|
|
location.href=strUpdatedUrl
|
|
//this.stopScrolling(); // 如果没有下一页和下一章,则停止滚动
|
|
}
|
|
|
|
}
|
|
|
|
updateChapterId(url,intChapterId) {
|
|
const urlObj = new URL(url);
|
|
const params = urlObj.searchParams;
|
|
|
|
if (params.has('chapter_id')) {
|
|
// 如果存在 chapter_id 参数,则将其值改为 1
|
|
params.set('chapter_id', intChapterId);
|
|
} else {
|
|
// 如果不存在 chapter_id 参数,则追加该参数
|
|
params.append('chapter_id', intChapterId);
|
|
}
|
|
|
|
// 返回更新后的 URL
|
|
return urlObj.toString();
|
|
}
|
|
|
|
/**
|
|
* 启动自动滚动。
|
|
*/
|
|
startScrolling() {
|
|
var interval = parseInt($('.van-stepper__input').val()) * 1000; // 将秒转换为毫秒
|
|
//console.log('启动滚动,间隔:', interval);
|
|
this.isScrolling = true;
|
|
localStorage.setItem('autoPlay', 'true');
|
|
this.intervalId = setInterval(() => {
|
|
var scrollTop = $(window).scrollTop();
|
|
var scrollHeight = $(document).height();
|
|
var clientHeight = $(window).height();
|
|
//console.log('scrollTop:', scrollTop, 'scrollHeight:', scrollHeight, 'clientHeight:', clientHeight);
|
|
if (scrollTop + clientHeight >= scrollHeight) {
|
|
console.log('到达底部,触发下一页或下一章');
|
|
this.nextPage();
|
|
} else {
|
|
$(window).scrollTop(scrollTop + this.scrollStep); // 向下滚动50px
|
|
//console.log('滚动到:', scrollTop + this.scrollStep);
|
|
}
|
|
}, interval);
|
|
}
|
|
|
|
/**
|
|
* 停止自动滚动。
|
|
*/
|
|
stopScrolling() {
|
|
clearInterval(this.intervalId);
|
|
this.intervalId = null;
|
|
this.isScrolling = false;
|
|
localStorage.setItem('autoPlay', 'false');
|
|
//console.log('滚动停止');
|
|
}
|
|
|
|
/**
|
|
* 切换自动滚动状态。
|
|
*/
|
|
toggleScrolling() {
|
|
if (this.isScrolling) {
|
|
this.stopScrolling();
|
|
$(this.switchButton).removeClass('van-switch--on').attr('aria-checked', 'false');
|
|
} else {
|
|
this.startScrolling();
|
|
$(this.switchButton).addClass('van-switch--on').attr('aria-checked', 'true');
|
|
}
|
|
}
|
|
}
|
|
|
|
// 初始化小说阅读器
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
let element = document.getElementById('novel_content')
|
|
|
|
// 获取元素的文本内容
|
|
let text = element.textContent;
|
|
|
|
const chapters = [
|
|
text
|
|
];
|
|
|
|
const pageSize = 1000; // 每页的字数
|
|
new NovelReader(chapters, pageSize, '.novel-content pre', '.prevPageTop', '.nextPageTop', '.prevPageBottom', '.nextPageBottom', '.van-switch', 300);
|
|
});
|