You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

424 lines
15 KiB

class BgSwitcher {
constructor(images, options = {}) {
this.images = images;
// 从localStorage中读取保存的索引
const savedIndex = localStorage.getItem('bgSwitcherIndex');
if (savedIndex !== null && !isNaN(savedIndex)) {
this.index = parseInt(savedIndex);
} else {
this.index = 0;
}
this.container = options.container || document.body;
this.interval = options.interval || 3000;
this.effect = options.effect || BgSwitcher.fadeEffect;
this.timer = null;
this.apiTimer = null;
this.apiUrl = null;
this.apiInterval = 30000;
this.startTime = 0;
// 从localStorage中读取保存的剩余时间
const savedRemainingTime = localStorage.getItem('bgSwitcherRemainingTime');
this.remainingTime = savedRemainingTime !== null && !isNaN(savedRemainingTime) && savedRemainingTime >= 0 ? parseInt(savedRemainingTime) : this.interval;
this.bgLayer = document.createElement('div');
this.isInitialLoad = true;
// 从localStorage中读取API模式状态
const isApiMode = localStorage.getItem('bgSwitcherIsApiMode') === 'true';
if (isApiMode) {
this.apiUrl = localStorage.getItem('bgSwitcherApiUrl') || null;
const savedApiInterval = localStorage.getItem('bgSwitcherApiInterval');
this.apiInterval = savedApiInterval !== null && !isNaN(savedApiInterval) ? parseInt(savedApiInterval) : 30000;
}
// 监听页面可见性变化
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
document.addEventListener('visibilitychange', this.handleVisibilityChange);
// 监听页面卸载事件,确保保存状态
this.handleBeforeUnload = this.handleBeforeUnload.bind(this);
window.addEventListener('beforeunload', this.handleBeforeUnload);
this.bgLayer.style.position = 'fixed';
this.bgLayer.style.top = 0;
this.bgLayer.style.left = 0;
this.bgLayer.style.width = '100vw';
this.bgLayer.style.height = '100vh';
this.bgLayer.style.zIndex = '-1';
this.bgLayer.style.transition = 'opacity 1s';
this.bgLayer.style.opacity = 1;
this.bgLayer.style.backgroundSize = 'cover';
this.bgLayer.style.backgroundColor = '#000000';
this.bgLayer.style.backgroundPosition = 'center';
this.bgLayer.style.filter = 'brightness(0.68)'
this.container.style.backgroundColor = '#000000';
}
setBg(url) {
// 切换时先预加载目标图片,加载完成后再切换显示
const img = new Image();
img.onload = () => {
if (this.isInitialLoad) {
// 初始加载时,先设置背景图再添加到页面
this.bgLayer.style.backgroundImage = `url(${url})`;
this.container.appendChild(this.bgLayer);
this.isInitialLoad = false;
} else {
this.effect(this.bgLayer, url);
}
if(!this.isApiMode) {
this.scheduleNext();
}
};
img.onerror = () => {
// 加载失败时处理
console.warn('背景图片加载失败:', url);
if (this.isInitialLoad) {
// 初始加载失败时,也添加背景层到页面
this.container.appendChild(this.bgLayer);
this.isInitialLoad = false;
}
};
img.src = url;
}
next() {
const nextIndex = (this.index + 1) % this.images.length;
const nextUrl = this.images[nextIndex];
// 切换前先预加载
const img = new Image();
img.onload = () => {
this.index = nextIndex;
// 保存索引到localStorage
localStorage.setItem('bgSwitcherIndex', this.index);
this.effect(this.bgLayer, nextUrl);
this.scheduleNext();
};
img.onerror = () => {
// 加载失败时跳过
console.warn('背景图片加载失败:', nextUrl);
this.scheduleNext();
};
img.src = nextUrl;
}
prev() {
const prevIndex = (this.index - 1 + this.images.length) % this.images.length;
const prevUrl = this.images[prevIndex];
// 切换前先预加载
const img = new Image();
img.onload = () => {
this.index = prevIndex;
// 保存索引到localStorage
localStorage.setItem('bgSwitcherIndex', this.index);
this.effect(this.bgLayer, prevUrl);
this.scheduleNext();
};
img.onerror = () => {
// 加载失败时跳过
console.warn('背景图片加载失败:', prevUrl);
this.scheduleNext();
};
img.src = prevUrl;
}
start() {
if (this.timer || this.apiTimer) return;
// 如果处于API模式,启动API请求
if (this.apiUrl) {
this.fetchRandomWallpaper();
} else {
// 否则使用默认轮播
this.setBg(this.images[this.index]);
}
}
/**
* 安排下一次背景切换
*/
scheduleNext() {
if (this.timer) {
clearTimeout(this.timer);
}
// 记录开始时间
this.startTime = Date.now();
// 使用剩余时间或默认间隔
const timeToWait = this.remainingTime > 0 ? this.remainingTime : this.interval;
this.timer = setTimeout(() => {
this.remainingTime = this.interval; // 重置剩余时间
// 保存剩余时间到localStorage
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
this.next();
}, timeToWait);
}
/**
* 处理页面可见性变化
*/
handleVisibilityChange() {
if (document.hidden) {
// 页面不可见时,暂停计时器并计算剩余时间
if (this.timer) {
const elapsedTime = Date.now() - this.startTime;
this.remainingTime = Math.max(0, this.remainingTime - elapsedTime);
// 保存剩余时间到localStorage
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
clearTimeout(this.timer);
this.timer = null;
}
// 暂停API计时器
if (this.apiTimer) {
const elapsedTime = Date.now() - this.startTime;
this.remainingTime = Math.max(0, this.remainingTime - elapsedTime);
// 保存剩余时间到localStorage
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
clearTimeout(this.apiTimer);
this.apiTimer = null;
}
} else {
// 页面可见时,恢复计时器
if (!this.timer && !this.apiTimer && !this.apiUrl) {
// 如果没有活跃的计时器,使用默认的轮播
this.scheduleNext();
} else if (this.apiTimer === null && this.apiUrl) {
// 如果处于API模式但计时器未运行,恢复API请求
this.scheduleNextApiRequest();
}
}
}
/**
* 处理页面卸载事件,确保保存状态
*/
handleBeforeUnload() {
// 保存当前索引
localStorage.setItem('bgSwitcherIndex', this.index);
// 保存API模式状态
localStorage.setItem('bgSwitcherIsApiMode', !!this.apiUrl);
if (this.apiUrl) {
localStorage.setItem('bgSwitcherApiUrl', this.apiUrl);
localStorage.setItem('bgSwitcherApiInterval', this.apiInterval);
}
// 如果计时器在运行,计算并保存剩余时间
if (this.timer || this.apiTimer) {
const elapsedTime = Date.now() - this.startTime;
this.remainingTime = Math.max(0, this.remainingTime - elapsedTime);
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
}
}
stop() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
// 重置剩余时间
this.remainingTime = this.interval;
// 保存剩余时间到localStorage
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
}
/**
* 从API获取随机壁纸并定期更新
* @param {string} apiUrl - 获取随机壁纸的API地址
* @param {number} interval - 请求间隔时间(毫秒)
*/
startRandomApiSwitch(apiUrl, interval = 30000) {
this.stop(); // 停止当前的轮播
this.apiInterval = interval;
this.apiUrl = apiUrl;
// 创建专用的API计时器
this.apiTimer = null;
// 立即请求一次
this.fetchRandomWallpaper();
}
/**
* 从API获取随机壁纸
*/
fetchRandomWallpaper() {
// 记录开始时间,用于计算剩余时间
this.startTime = Date.now();
this.remainingTime = this.apiInterval;
fetch(this.apiUrl)
.then(response => {
console.log(response);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
// 假设API返回的数据格式为 { wallpaperUrl: '图片地址' }
const wallpaperUrl = data.wallpaperUrl || data.url || data.image;
if (wallpaperUrl) {
// 预加载图片
const img = new Image();
img.onload = () => {
if (this.isInitialLoad) {
// 初始加载时,先设置背景图再添加到页面
this.container.appendChild(this.bgLayer);
this.isInitialLoad = false;
}
// 保存当前索引(使用-1标记这是API获取的图片)
this.index = -1;
localStorage.setItem('bgSwitcherIndex', -1);
this.effect(this.bgLayer, wallpaperUrl);
this.scheduleNextApiRequest();
};
img.onerror = () => {
console.warn('API返回的壁纸加载失败:', wallpaperUrl);
this.scheduleNextApiRequest();
};
img.src = wallpaperUrl;
} else {
console.warn('背景图片加载失败:', url);
if (this.isInitialLoad) {
console.warn('API返回的数据格式不正确,未找到壁纸地址');
// 初始加载失败时,也添加背景层到页面
this.container.appendChild(this.bgLayer);
this.isInitialLoad = false;
}
this.scheduleNextApiRequest();
}
})
.catch(error => {
console.error('获取随机壁纸失败:', error);
this.scheduleNextApiRequest();
});
}
/**
* 安排下一次API请求
*/
scheduleNextApiRequest() {
if (this.apiTimer) {
clearTimeout(this.apiTimer);
}
// 使用剩余时间或默认间隔
const timeToWait = this.remainingTime > 0 ? this.remainingTime : this.apiInterval;
this.apiTimer = setTimeout(() => {
this.remainingTime = this.apiInterval; // 重置剩余时间
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
this.fetchRandomWallpaper();
}, timeToWait);
}
/**
* 停止API随机壁纸请求
*/
stopRandomApiSwitch() {
if (this.apiTimer) {
clearTimeout(this.apiTimer);
this.apiTimer = null;
}
this.apiUrl = null
// 重置剩余时间
this.remainingTime = this.interval;
localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime);
}
static fadeEffect(layer, url) {
layer.style.transition = 'opacity 1s';
layer.style.opacity = 0;
setTimeout(() => {
layer.style.backgroundImage = `url(${url})`;
layer.style.opacity = 1;
}, 500);
}
}
// 使用示例
// 1. 默认本地图片轮播
// const images = [
// '/static/bg2.webp',
// '/static/bg.jpg',
// ];
// const bgSwitcher = new BgSwitcher(images, { interval: 5000 });
// 启动轮播
// bgSwitcher.start();
// 2. 随机API壁纸示例
// 创建一个新的BgSwitcher实例用于API模式
const apiBgSwitcher = new BgSwitcher([], { interval: 5000 }); // API模式不需要本地图片列表
// 模拟API函数,实际使用时替换为真实API地址
function createMockWallpaperApi() {
// 模拟壁纸地址库
const mockWallpapers = [
'/static/bg2.webp',
'/static/bg.jpg',
];
// 创建一个简单的服务器端点
if (window.mockWallpaperServer) {
clearInterval(window.mockWallpaperServer);
}
// 模拟API响应
window.fetchRandomWallpaper = function () {
return new Promise((resolve) => {
setTimeout(() => {
const randomIndex = Math.floor(Math.random() * mockWallpapers.length);
resolve({
ok: true,
json() {
return {
wallpaperUrl: mockWallpapers[randomIndex]
}
}
});
}, 500);
});
};
// 替换原生fetch以模拟API调用
window.originalFetch = window.fetch;
window.fetch = function (url) {
if (url === '/api/random-wallpaper') {
return window.fetchRandomWallpaper();
}
return window.originalFetch(url);
};
console.log('模拟壁纸API已启动');
}
// 初始化模拟API
createMockWallpaperApi();
// 启动API模式的随机壁纸切换(每10秒请求一次)
apiBgSwitcher.startRandomApiSwitch('/api/random-wallpaper', 10000);
fetch("https://pic.xieyaxin.top/random.php")
.then(response => {
if(response.body instanceof ReadableStream) {
return response.blob()
}
return response.json()
})
.then(data => {
console.log(URL.createObjectURL(data));
})
// 要停止API模式,使用
// apiBgSwitcher.stopRandomApiSwitch();
// 要切换回本地图片轮播,使用
// apiBgSwitcher.stopRandomApiSwitch();
// apiBgSwitcher.start();
// 启动默认轮播
// bgSwitcher.start();