Skip to content
0

go壁纸api

v1-2025.9.14-解决手机拍照的图片显示不全问题(成功解决)

提出问题

bash
<!DOCTYPE html>
<html>
<head>
    <title>随机图片展示</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
        }
        img {
            object-fit: cover;
            width: 100%;
            height: 100%;
            background-color: #000;
        }
        .image-container {
            position: relative;
            width: 100%;
            height: 100%;
            background-color: #000;
        }
        .image-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            transform: scale(1.1);
        }
        
        .image-layer.transitioning {
            transition: opacity 2.5s cubic-bezier(0.25, 0.1, 0.25, 1), 
                       transform 2.5s cubic-bezier(0.25, 0.1, 0.25, 1);
        }
        
        .image-layer.active {
            opacity: 1;
            animation: breathe 8s ease-in-out infinite;
        }
        
        @keyframes breathe {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.05);
            }
        }
    </style>
</head>
<body>
    <div class="image-container">
        <img class="image-layer" id="image1" src="" alt="">
        <img class="image-layer" id="image2" src="" alt="">
    </div>

    <script>
        var images = {{ .ImagePaths }};
        var image1 = document.getElementById('image1');
        var image2 = document.getElementById('image2');
        var currentLayer = 1; // 当前显示的图层
        var imageInterval;

        function preloadImage(src) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(src);
                img.onerror = () => reject(src);
                img.src = src;
            });
        }

        let isTransitioning = false;

        function fadeToNewImage(src) {
            if (isTransitioning) return;
            isTransitioning = true;
            
            const nextLayer = currentLayer === 1 ? 2 : 1;
            const currentImg = currentLayer === 1 ? image1 : image2;
            const nextImg = nextLayer === 1 ? image1 : image2;

            preloadImage(src).then(loadedSrc => {
                // 在后台图层设置新图片
                nextImg.src = loadedSrc;
                nextImg.className = 'image-layer';
                
                // 获取当前图片的实时缩放值
                const currentTransform = window.getComputedStyle(currentImg).transform;
                let currentScale = 1;
                if (currentTransform && currentTransform !== 'none') {
                    const matrix = currentTransform.match(/matrix.*\((.+)\)/);
                    if (matrix) {
                        const values = matrix[1].split(', ');
                        currentScale = parseFloat(values[0]);
                    }
                }
                
                // 开始切换,保持当前缩放状态
                currentImg.classList.add('transitioning');
                nextImg.classList.add('transitioning');
                
                // 强制重绘
                nextImg.offsetHeight;
                
                // 平滑切换
                requestAnimationFrame(() => {
                    // 当前图片从实时状态淡出
                    if (currentImg.classList.contains('active')) {
                        currentImg.classList.remove('active');
                        currentImg.style.transform = `scale(${currentScale})`;
                        currentImg.style.opacity = '1';
                        
                        requestAnimationFrame(() => {
                            currentImg.style.opacity = '0';
                            currentImg.style.transform = 'scale(0.95)';
                        });
                    }
                    
                    // 新图片淡入
                    nextImg.style.opacity = '0';
                    nextImg.style.transform = 'scale(1.1)';
                    
                    requestAnimationFrame(() => {
                        nextImg.style.opacity = '1';
                        nextImg.style.transform = 'scale(1)';
                    });
                });
                
                // 切换完成后启动呼吸动画
                setTimeout(() => {
                    // 清理旧图片
                    currentImg.className = 'image-layer';
                    currentImg.style.cssText = '';
                    
                    // 激活新图片并启动呼吸
                    nextImg.className = 'image-layer active';
                    nextImg.style.cssText = '';
                    
                    currentLayer = nextLayer;
                    isTransitioning = false;
                }, 2500);
                
            }).catch(() => {
                isTransitioning = false;
                setTimeout(changeImage, 500);
            });
        }

        function changeImage() {
            if (images.length === 0) return;
            const randomSrc = images[Math.floor(Math.random() * images.length)];
            fadeToNewImage(randomSrc);
        }

        // 初始化第一张图片
        if (images.length > 0) {
            const firstSrc = images[Math.floor(Math.random() * images.length)];
            preloadImage(firstSrc).then(loadedSrc => {
                image1.src = loadedSrc;
                setTimeout(() => {
                    image1.className = 'image-layer active';
                }, 100);
            }).catch(() => {
                // 如果第一张图片加载失败,尝试下一张
                setTimeout(changeImage, 500);
            });
        }

        // 定时切换 (15秒)
        imageInterval = setInterval(changeImage, 15000);

        // 刷新图片列表
        setInterval(function() {
            fetch('/api/images')
                .then(response => response.json())
                .then(data => {
                    images = data.images;
                    if (images.length === 0) {
                        clearInterval(imageInterval);
                        image1.className = 'image-layer';
                        image2.className = 'image-layer';
                    } else {
                        if (!imageInterval) {
                            imageInterval = setInterval(changeImage, 15000);
                        }
                    }
                })
                .catch(error => console.error('Error fetching images:', error));
        }, 60000);
    </script>
</body>
</html>


这段代码好像存在问题:

之前虽然把照片的两边边框给处理掉了,但是像手机拍的照片,被轮播时,拍的人头直接看不到了,这个感觉🉐再优化下

豆包回答

问题核心在于当前使用的 object-fit: cover 会强制图片 “填满容器且保持比例”,导致手机拍摄的竖屏照片(通常为 3:49:16 比例)在横屏显示器(16:9 等宽屏比例)中被裁剪上下部分,从而丢失人像头部等关键内容。

优化思路是:优先保证人像等主体内容完整显示,同时兼顾画面填充效果,通过调整 object-fit 策略、增加背景适配、优化缩放逻辑实现平衡。以下是完整优化后的代码,关键修改已标注说明:

📌优化后完整代码

html
<!DOCTYPE html>
<html>
<head>
    <title>随机图片展示(人像优化版)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
            /* 新增:深色背景过渡,避免图片切换时出现白边 */
            background: linear-gradient(135deg, #111 0%, #333 100%);
        }
        .image-container {
            position: relative;
            width: 100%;
            height: 100%;
        }
        .image-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            /* 关键修改1:将 scale(1.1) 改为 scale(1),减少初始裁剪 */
            transform: scale(1);
            /* 关键修改2:object-fit 从 cover 改为 contain,优先完整显示图片 */
            object-fit: contain;
            /* 关键修改3:设置背景色,图片未填满区域用深色过渡,避免突兀 */
            background-color: rgba(0, 0, 0, 0.8);
        }
        
        .image-layer.transitioning {
            /* 保留过渡动画,确保切换平滑 */
            transition: opacity 2.5s cubic-bezier(0.25, 0.1, 0.25, 1), 
                       transform 2.5s cubic-bezier(0.25, 0.1, 0.25, 1);
        }
        
        .image-layer.active {
            opacity: 1;
            /* 关键修改4:优化呼吸动画缩放比例,从 1.05 改为 1.03,减少边缘裁剪 */
            animation: breathe 10s ease-in-out infinite; /* 延长动画周期,更自然 */
        }
        
        @keyframes breathe {
            0%, 100% {
                transform: scale(1); /* 初始/结束状态:原尺寸 */
            }
            50% {
                transform: scale(1.03); /* 中间状态:轻微放大,避免过度裁剪 */
            }
        }

        /* 新增:加载状态提示(可选,提升用户体验) */
        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #fff;
            font-size: 18px;
            z-index: 10;
        }
    </style>
</head>
<body>
    <div class="image-container">
        <!-- 新增:加载提示 -->
        <div class="loading" id="loading">图片加载中...</div>
        <!-- 图层保持不变 -->
        <img class="image-layer" id="image1" src="" alt="轮播图片">
        <img class="image-layer" id="image2" src="" alt="轮播图片">
    </div>

    <script>
        var images = {{ .ImagePaths }}; // 保持原有模板变量
        var image1 = document.getElementById('image1');
        var image2 = document.getElementById('image2');
        var loading = document.getElementById('loading');
        var currentLayer = 1;
        var imageInterval;
        var isTransitioning = false;

        // 1. 保留预加载逻辑,确保图片完全加载后再显示
        function preloadImage(src) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(src);
                img.onerror = () => reject(new Error(`图片加载失败: ${src}`));
                img.src = src;
            });
        }

        // 2. 关键优化:根据图片宽高比动态调整显示策略(进一步减少裁剪)
        function adjustImageDisplay(imgElement, src) {
            return new Promise((resolve) => {
                const tempImg = new Image();
                tempImg.onload = () => {
                    const imgRatio = tempImg.width / tempImg.height; // 图片宽高比
                    const containerRatio = window.innerWidth / window.innerHeight; // 容器宽高比

                    // 逻辑:如果是竖屏图片(手机拍摄,宽高比 < 1),强制用 contain 保证完整;
                    // 如果是横屏图片,可根据容器比例微调(可选 cover,根据需求切换)
                    if (imgRatio < 1) { // 竖屏图片(如手机人像照)
                        imgElement.style.objectFit = 'contain';
                    } else { // 横屏图片
                        imgElement.style.objectFit = 'contain'; // 也可改为 cover,根据需求选择
                    }
                    resolve();
                };
                tempImg.src = src;
            });
        }

        // 3. 优化切换逻辑:确保加载+适配完成后再开始过渡
        async function fadeToNewImage(src) {
            if (isTransitioning || !src) return;
            isTransitioning = true;

            const nextLayer = currentLayer === 1 ? 2 : 1;
            const currentImg = currentLayer === 1 ? image1 : image2;
            const nextImg = nextLayer === 1 ? image1 : image2;

            try {
                // 步骤1:预加载图片 + 动态适配显示策略
                await preloadImage(src);
                await adjustImageDisplay(nextImg, src);

                // 步骤2:设置新图片到后台图层
                nextImg.src = src;
                nextImg.className = 'image-layer';

                // 步骤3:获取当前图层的实时状态(保持过渡连贯性)
                const currentTransform = window.getComputedStyle(currentImg).transform;
                let currentScale = 1;
                if (currentTransform && currentTransform !== 'none') {
                    const matrix = currentTransform.match(/matrix.*\((.+)\)/);
                    if (matrix) {
                        const values = matrix[1].split(', ');
                        currentScale = parseFloat(values[0]) || 1;
                    }
                }

                // 步骤4:开始平滑过渡
                currentImg.classList.add('transitioning');
                nextImg.classList.add('transitioning');
                nextImg.offsetHeight; // 强制重绘,确保过渡生效

                // 步骤5:当前图层淡出,新图层淡入
                requestAnimationFrame(() => {
                    // 当前图层:从当前缩放状态平滑淡出
                    if (currentImg.classList.contains('active')) {
                        currentImg.style.transform = `scale(${currentScale})`;
                        currentImg.style.opacity = '1';
                        requestAnimationFrame(() => {
                            currentImg.style.opacity = '0';
                            currentImg.style.transform = 'scale(0.98)'; // 轻微缩小,避免突兀
                        });
                    }

                    // 新图层:从轻微放大状态淡入(匹配呼吸动画初始值)
                    nextImg.style.opacity = '0';
                    nextImg.style.transform = 'scale(1)';
                    requestAnimationFrame(() => {
                        nextImg.style.opacity = '1';
                        nextImg.style.transform = 'scale(1)';
                    });
                });

                // 步骤6:过渡完成,激活新图层的呼吸动画
                setTimeout(() => {
                    currentImg.className = 'image-layer';
                    currentImg.style.cssText = ''; // 清除旧样式,避免干扰
                    nextImg.className = 'image-layer active';
                    nextImg.style.cssText = '';
                    currentLayer = nextLayer;
                    isTransitioning = false;
                }, 2500);

            } catch (error) {
                console.error(error);
                isTransitioning = false;
                setTimeout(changeImage, 1000); // 失败后延迟重试
            }
        }

        // 4. 初始化第一张图片(新增加载提示隐藏逻辑)
        async function initFirstImage() {
            if (images.length === 0) {
                loading.textContent = '暂无图片可展示';
                return;
            }
            const firstSrc = images[Math.floor(Math.random() * images.length)];
            try {
                await preloadImage(firstSrc);
                await adjustImageDisplay(image1, firstSrc);
                image1.src = firstSrc;
                // 延迟显示,确保动画平滑
                setTimeout(() => {
                    image1.className = 'image-layer active';
                    loading.style.opacity = '0'; // 隐藏加载提示
                    setTimeout(() => loading.style.display = 'none', 500);
                }, 300);
            } catch (error) {
                console.error('初始化图片失败:', error);
                loading.textContent = '图片加载失败,正在重试...';
                setTimeout(initFirstImage, 1500); // 重试
            }
        }

        // 5. 随机切换图片(逻辑不变)
        function changeImage() {
            if (images.length === 0) {
                clearInterval(imageInterval);
                return;
            }
            const randomSrc = images[Math.floor(Math.random() * images.length)];
            fadeToNewImage(randomSrc);
        }

        // 6. 初始化执行
        initFirstImage();

        // 7. 定时切换(保持15秒,可根据需求调整)
        imageInterval = setInterval(changeImage, 15000);

        // 8. 刷新图片列表(逻辑不变,新增错误处理)
        setInterval(async () => {
            try {
                const response = await fetch('/api/images');
                if (!response.ok) throw new Error('获取图片列表失败');
                const data = await response.json();
                images = data.images || [];

                if (images.length === 0) {
                    clearInterval(imageInterval);
                    image1.className = 'image-layer';
                    image2.className = 'image-layer';
                    loading.style.display = 'block';
                    loading.textContent = '暂无图片可展示';
                } else {
                    if (!imageInterval) {
                        imageInterval = setInterval(changeImage, 15000);
                        loading.style.display = 'none';
                    }
                }
            } catch (error) {
                console.error('刷新图片列表失败:', error);
            }
        }, 60000);
    </script>
</body>
</html>

📌核心优化点说明

优化方向原问题解决方案
图片裁剪问题object-fit: cover 裁剪竖屏照片改为 object-fit: contain,优先保证图片完整显示,未填满区域用深色背景过渡
呼吸动画过度缩放scale(1.05) 放大导致边缘裁剪调整为 scale(1.03),延长动画周期至 10 秒,减少裁剪风险
初始状态裁剪初始 scale(1.1) 直接裁剪图片改为 scale(1),从原尺寸开始动画,避免初始丢失内容
动态适配策略未区分横竖屏图片新增宽高比判断:竖屏图片强制 contain,横屏可灵活切换(根据需求调整)
用户体验加载时无提示,失败无反馈增加加载中提示,失败自动重试,优化错误文案

📌额外建议(根据实际场景选择)

  1. 如果希望横屏图片填满容器: 在 adjustImageDisplay 函数中,将横屏图片(imgRatio > 1)的 objectFit 改回 cover,竖屏图片保持 contain,实现 “横屏填充满,竖屏保完整”。
  2. 如果图片尺寸差异过大: 可在后端接口 /api/images 中返回图片宽高比信息,前端直接使用,减少前端临时计算的性能消耗。
  3. 增加图片居中裁剪(折中方案): 若 contain 导致背景过多,可改用 object-fit: scale-down(优先完整显示,超出时裁剪),或通过 object-position: center 确保裁剪时保留中间区域(人像通常在中间)。

通过以上优化,手机拍摄的竖屏人像照会优先完整显示,同时兼顾整体视觉效果,避免头部等关键内容被裁剪。

效果测试

bash
[root@wiki templates]# pwd
/root/img-server-fxj/templates
[root@wiki templates]# ls
index.qx  index.qx.bak  index.qx-v1  no-images.qx
[root@wiki templates]# 


#index.qx-v1 这一般OK的。

存在问题

人像照片可以正常显示了,但是两边出现了黑框。

image-20250914081151760

v2-2025.9.14-解决 让 照片 两边的边框为高斯模糊/毛玻璃效果(不理想)

提出问题

bash
在以上代码基础上,能否把轮播图片时  2边的黑框 改成高斯模糊 毛玻璃 风格呢,这个比较好看些

要将轮播图片两侧的黑框改为为高斯模糊(毛玻璃)风格,我们可以利用 CSS 的backdrop-filter属性结合伪元素来实现。这种效果能让图片边缘与背景自然融合,同时保持图片主体清晰。以下是修改后的代码:

带毛玻璃效果的图片轮播

V1

创建时间:07:49

html
<!DOCTYPE html>
<html>
<head>
    <title>随机图片展示(毛玻璃边框版)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
            /* 背景图用于增强毛玻璃效果的层次感 */
            background-image: url('https://picsum.photos/id/1015/1920/1080');
            background-size: cover;
            background-position: center;
            background-attachment: fixed;
        }
        .image-container {
            position: relative;
            width: 100%;
            height: 100%;
        }
        
        /* 新增:左右毛玻璃边框 */
        .image-container::before,
        .image-container::after {
            content: '';
            position: absolute;
            top: 0;
            height: 100%;
            width: 15%; /* 边框宽度,可根据需要调整 */
            z-index: 5; /* 确保在图片上方 */
            pointer-events: none; /* 不影响图片交互 */
        }
        
        .image-container::before {
            left: 0;
            /* 左侧渐变模糊效果 */
            background: linear-gradient(90deg, 
                rgba(255, 255, 255, 0.15) 0%, 
                rgba(255, 255, 255, 0) 100%);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
        }
        
        .image-container::after {
            right: 0;
            /* 右侧渐变模糊效果 */
            background: linear-gradient(270deg, 
                rgba(255, 255, 255, 0.15) 0%, 
                rgba(255, 255, 255, 0) 100%);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
        }

        .image-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            transform: scale(1);
            object-fit: contain;
            /* 轻微透明让背景渗透,增强融合感 */
            opacity: 0.98;
        }
        
        .image-layer.transitioning {
            transition: opacity 2.5s cubic-bezier(0.25, 0.1, 0.25, 1), 
                       transform 2.5s cubic-bezier(0.25, 0.1, 0.25, 1);
        }
        
        .image-layer.active {
            opacity: 1;
            animation: breathe 10s ease-in-out infinite;
        }
        
        @keyframes breathe {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.03);
            }
        }

        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #fff;
            font-size: 18px;
            z-index: 10;
            text-shadow: 0 2px 4px rgba(0,0,0,0.3);
            backdrop-filter: blur(5px);
            -webkit-backdrop-filter: blur(5px);
            padding: 10px 20px;
            border-radius: 8px;
            background-color: rgba(0,0,0,0.2);
        }
    </style>
</head>
<body>
    <div class="image-container">
        <div class="loading" id="loading">图片加载中...</div>
        <img class="image-layer" id="image1" src="" alt="轮播图片">
        <img class="image-layer" id="image2" src="" alt="轮播图片">
    </div>

    <script>
        // 示例图片地址,实际使用时替换为{{ .ImagePaths }}
        var images = [
            'https://picsum.photos/id/64/800/1200', // 竖屏示例
            'https://picsum.photos/id/65/1200/800', // 横屏示例
            'https://picsum.photos/id/91/800/1200', // 竖屏示例
            'https://picsum.photos/id/96/1200/800'  // 横屏示例
        ];
        
        var image1 = document.getElementById('image1');
        var image2 = document.getElementById('image2');
        var loading = document.getElementById('loading');
        var currentLayer = 1;
        var imageInterval;
        var isTransitioning = false;

        function preloadImage(src) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(src);
                img.onerror = () => reject(new Error(`图片加载失败: ${src}`));
                img.src = src;
            });
        }

        function adjustImageDisplay(imgElement, src) {
            return new Promise((resolve) => {
                const tempImg = new Image();
                tempImg.onload = () => {
                    const imgRatio = tempImg.width / tempImg.height;
                    const containerRatio = window.innerWidth / window.innerHeight;

                    if (imgRatio < 1) {
                        imgElement.style.objectFit = 'contain';
                    } else {
                        imgElement.style.objectFit = 'contain';
                    }
                    resolve();
                };
                tempImg.src = src;
            });
        }

        async function fadeToNewImage(src) {
            if (isTransitioning || !src) return;
            isTransitioning = true;

            const nextLayer = currentLayer === 1 ? 2 : 1;
            const currentImg = currentLayer === 1 ? image1 : image2;
            const nextImg = nextLayer === 1 ? image1 : image2;

            try {
                await preloadImage(src);
                await adjustImageDisplay(nextImg, src);

                nextImg.src = src;
                nextImg.className = 'image-layer';

                const currentTransform = window.getComputedStyle(currentImg).transform;
                let currentScale = 1;
                if (currentTransform && currentTransform !== 'none') {
                    const matrix = currentTransform.match(/matrix.*\((.+)\)/);
                    if (matrix) {
                        const values = matrix[1].split(', ');
                        currentScale = parseFloat(values[0]) || 1;
                    }
                }

                currentImg.classList.add('transitioning');
                nextImg.classList.add('transitioning');
                nextImg.offsetHeight;

                requestAnimationFrame(() => {
                    if (currentImg.classList.contains('active')) {
                        currentImg.style.transform = `scale(${currentScale})`;
                        currentImg.style.opacity = '1';
                        requestAnimationFrame(() => {
                            currentImg.style.opacity = '0';
                            currentImg.style.transform = 'scale(0.98)';
                        });
                    }

                    nextImg.style.opacity = '0';
                    nextImg.style.transform = 'scale(1)';
                    requestAnimationFrame(() => {
                        nextImg.style.opacity = '1';
                        nextImg.style.transform = 'scale(1)';
                    });
                });

                setTimeout(() => {
                    currentImg.className = 'image-layer';
                    currentImg.style.cssText = '';
                    nextImg.className = 'image-layer active';
                    nextImg.style.cssText = '';
                    currentLayer = nextLayer;
                    isTransitioning = false;
                }, 2500);

            } catch (error) {
                console.error(error);
                isTransitioning = false;
                setTimeout(changeImage, 1000);
            }
        }

        async function initFirstImage() {
            if (images.length === 0) {
                loading.textContent = '暂无图片可展示';
                return;
            }
            const firstSrc = images[Math.floor(Math.random() * images.length)];
            try {
                await preloadImage(firstSrc);
                await adjustImageDisplay(image1, firstSrc);
                image1.src = firstSrc;
                setTimeout(() => {
                    image1.className = 'image-layer active';
                    loading.style.opacity = '0';
                    setTimeout(() => loading.style.display = 'none', 500);
                }, 300);
            } catch (error) {
                console.error('初始化图片失败:', error);
                loading.textContent = '图片加载失败,正在重试...';
                setTimeout(initFirstImage, 1500);
            }
        }

        function changeImage() {
            if (images.length === 0) {
                clearInterval(imageInterval);
                return;
            }
            const randomSrc = images[Math.floor(Math.random() * images.length)];
            fadeToNewImage(randomSrc);
        }

        initFirstImage();
        imageInterval = setInterval(changeImage, 15000);

        setInterval(async () => {
            try {
                const response = await fetch('/api/images');
                if (!response.ok) throw new Error('获取图片列表失败');
                const data = await response.json();
                images = data.images || [];

                if (images.length === 0) {
                    clearInterval(imageInterval);
                    image1.className = 'image-layer';
                    image2.className = 'image-layer';
                    loading.style.display = 'block';
                    loading.textContent = '暂无图片可展示';
                } else {
                    if (!imageInterval) {
                        imageInterval = setInterval(changeImage, 15000);
                        loading.style.display = 'none';
                    }
                }
            } catch (error) {
                console.error('刷新图片列表失败:', error);
            }
        }, 60000);
    </script>
</body>
</html>

📌主要修改说明

  1. 毛玻璃边框实现
    • 使用.image-container::before::after伪元素创建左右边框
    • 通过backdrop-filter: blur(10px)实现高斯模糊效果
    • 配合渐变背景linear-gradient实现从模糊到透明的过渡,使边框更自然
  2. 背景增强
    • 添加了一张固定背景图,增强毛玻璃效果的层次感
    • 毛玻璃效果会模糊其下方的背景内容,创造深度感
  3. 细节优化
    • 图片设置轻微透明(opacity: 0.98),让背景能轻微渗透,增强融合感
    • 加载提示也添加了轻微的毛玻璃效果,保持风格统一
    • 调整了毛玻璃的透明度和模糊程度,确保效果美观且不影响图片主体观看
  4. 兼容性考虑
    • 添加了-webkit-backdrop-filter前缀以支持 WebKit 内核浏览器
    • 毛玻璃效果在不支持的浏览器中会自动降级为半透明效果

你可以通过调整这些值来优化效果:

  • 边框宽度:修改.image-container::before::afterwidth属性
  • 模糊程度:调整backdrop-filter: blur(10px)中的像素值
  • 边框透明度:修改渐变中的rgba(255, 255, 255, 0.15)的 alpha 值

这种毛玻璃效果能很好地解决图片两侧空白区域的视觉问题,同时增添现代感和高级感。

效果测试(存在问题)

image-20250914081448268

v3-2025.9.14-为啥自己本地图片不见了

问题描述

bash
我本来图片 路径 在images目录下,但你修改的这版后哦,为啥我本地的图片没法展示了?

豆包输出

问题出在示例代码中使用了在线图片地址(如https://picsum.photos/...),而没有使用你本地images目录下的图片路径。要解决这个问题,需要将代码中的图片路径修改为你本地图片的正确路径。

以下是修改后的代码,适配适配本地images目录下的图片:

适配本地图片的毛玻璃边框轮播

V1

创建时间:07:57

html
<!DOCTYPE html>
<html>
<head>
    <title>随机图片展示(本地图片版)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
            /* 使用本地背景图或纯色背景 */
            background-color: #222; /* 改为深色背景,避免与本地图片冲突 */
        }
        .image-container {
            position: relative;
            width: 100%;
            height: 100%;
        }
        
        /* 左右毛玻璃边框 */
        .image-container::before,
        .image-container::after {
            content: '';
            position: absolute;
            top: 0;
            height: 100%;
            width: 15%;
            z-index: 5;
            pointer-events: none;
        }
        
        .image-container::before {
            left: 0;
            background: linear-gradient(90deg, 
                rgba(0, 0, 0, 0.3) 0%, 
                rgba(0, 0, 0, 0) 100%);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
        }
        
        .image-container::after {
            right: 0;
            background: linear-gradient(270deg, 
                rgba(0, 0, 0, 0.3) 0%, 
                rgba(0, 0, 0, 0) 100%);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
        }

        .image-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            transform: scale(1);
            object-fit: contain;
            opacity: 0.98;
        }
        
        .image-layer.transitioning {
            transition: opacity 2.5s cubic-bezier(0.25, 0.1, 0.25, 1), 
                       transform 2.5s cubic-bezier(0.25, 0.1, 0.25, 1);
        }
        
        .image-layer.active {
            opacity: 1;
            animation: breathe 10s ease-in-out infinite;
        }
        
        @keyframes breathe {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.03);
            }
        }

        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #fff;
            font-size: 18px;
            z-index: 10;
            text-shadow: 0 2px 4px rgba(0,0,0,0.3);
            backdrop-filter: blur(5px);
            -webkit-backdrop-filter: blur(5px);
            padding: 10px 20px;
            border-radius: 8px;
            background-color: rgba(0,0,0,0.2);
        }
    </style>
</head>
<body>
    <div class="image-container">
        <div class="loading" id="loading">图片加载中...</div>
        <img class="image-layer" id="image1" src="" alt="轮播图片">
        <img class="image-layer" id="image2" src="" alt="轮播图片">
    </div>

    <script>
        // 关键修改:使用本地images目录下的图片
        // 请将以下列表替换为你实际的图片文件名
        var images = [
            'images/photo1.jpg',   // 替换为你的图片1
            'images/photo2.png',   // 替换为你的图片2
            'images/photo3.jpg',   // 替换为你的图片3
            'images/photo4.png'    // 替换为你的图片4
        ];
        
        var image1 = document.getElementById('image1');
        var image2 = document.getElementById('image2');
        var loading = document.getElementById('loading');
        var currentLayer = 1;
        var imageInterval;
        var isTransitioning = false;

        // 新增:检查图片是否存在的辅助函数
        function checkImageExists(src) {
            return new Promise((resolve) => {
                const img = new Image();
                img.onload = () => resolve(true);
                img.onerror = () => resolve(false);
                img.src = src;
            });
        }

        function preloadImage(src) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(src);
                img.onerror = () => reject(new Error(`图片加载失败: ${src}(请检查路径是否正确)`));
                img.src = src;
            });
        }

        function adjustImageDisplay(imgElement, src) {
            return new Promise((resolve) => {
                const tempImg = new Image();
                tempImg.onload = () => {
                    const imgRatio = tempImg.width / tempImg.height;
                    const containerRatio = window.innerWidth / window.innerHeight;

                    if (imgRatio < 1) {
                        imgElement.style.objectFit = 'contain';
                    } else {
                        imgElement.style.objectFit = 'contain';
                    }
                    resolve();
                };
                tempImg.src = src;
            });
        }

        async function fadeToNewImage(src) {
            if (isTransitioning || !src) return;
            isTransitioning = true;

            const nextLayer = currentLayer === 1 ? 2 : 1;
            const currentImg = currentLayer === 1 ? image1 : image2;
            const nextImg = nextLayer === 1 ? image1 : image2;

            try {
                // 先检查图片是否存在
                const exists = await checkImageExists(src);
                if (!exists) {
                    throw new Error(`图片不存在: ${src}`);
                }

                await preloadImage(src);
                await adjustImageDisplay(nextImg, src);

                nextImg.src = src;
                nextImg.className = 'image-layer';

                const currentTransform = window.getComputedStyle(currentImg).transform;
                let currentScale = 1;
                if (currentTransform && currentTransform !== 'none') {
                    const matrix = currentTransform.match(/matrix.*\((.+)\)/);
                    if (matrix) {
                        const values = matrix[1].split(', ');
                        currentScale = parseFloat(values[0]) || 1;
                    }
                }

                currentImg.classList.add('transitioning');
                nextImg.classList.add('transitioning');
                nextImg.offsetHeight;

                requestAnimationFrame(() => {
                    if (currentImg.classList.contains('active')) {
                        currentImg.style.transform = `scale(${currentScale})`;
                        currentImg.style.opacity = '1';
                        requestAnimationFrame(() => {
                            currentImg.style.opacity = '0';
                            currentImg.style.transform = 'scale(0.98)';
                        });
                    }

                    nextImg.style.opacity = '0';
                    nextImg.style.transform = 'scale(1)';
                    requestAnimationFrame(() => {
                        nextImg.style.opacity = '1';
                        nextImg.style.transform = 'scale(1)';
                    });
                });

                setTimeout(() => {
                    currentImg.className = 'image-layer';
                    currentImg.style.cssText = '';
                    nextImg.className = 'image-layer active';
                    nextImg.style.cssText = '';
                    currentLayer = nextLayer;
                    isTransitioning = false;
                }, 2500);

            } catch (error) {
                console.error(error);
                loading.textContent = error.message;
                isTransitioning = false;
                setTimeout(changeImage, 2000);
            }
        }

        async function initFirstImage() {
            if (images.length === 0) {
                loading.textContent = '暂无图片可展示';
                return;
            }
            const firstSrc = images[Math.floor(Math.random() * images.length)];
            try {
                await preloadImage(firstSrc);
                await adjustImageDisplay(image1, firstSrc);
                image1.src = firstSrc;
                setTimeout(() => {
                    image1.className = 'image-layer active';
                    loading.style.opacity = '0';
                    setTimeout(() => loading.style.display = 'none', 500);
                }, 300);
            } catch (error) {
                console.error('初始化图片失败:', error);
                loading.textContent = error.message;
                setTimeout(initFirstImage, 2000);
            }
        }

        function changeImage() {
            if (images.length === 0) {
                clearInterval(imageInterval);
                return;
            }
            const randomSrc = images[Math.floor(Math.random() * images.length)];
            fadeToNewImage(randomSrc);
        }

        // 初始化本地图片展示
        initFirstImage();
        imageInterval = setInterval(changeImage, 15000);

        // 注释掉远程获取图片的代码,因为我们使用本地图片
        /*
        setInterval(async () => {
            try {
                const response = await fetch('/api/images');
                if (!response.ok) throw new Error('获取图片列表失败');
                const data = await response.json();
                images = data.images || [];

                if (images.length === 0) {
                    clearInterval(imageInterval);
                    image1.className = 'image-layer';
                    image2.className = 'image-layer';
                    loading.style.display = 'block';
                    loading.textContent = '暂无图片可展示';
                } else {
                    if (!imageInterval) {
                        imageInterval = setInterval(changeImage, 15000);
                        loading.style.display = 'none';
                    }
                }
            } catch (error) {
                console.error('刷新图片列表失败:', error);
            }
        }, 60000);
        */
    </script>
</body>
</html>

📌关键修改说明

  1. 图片路径调整
    • images数组中的图片路径修改为本地路径格式:'images/文件名'
    • 请务必将示例中的photo1.jpg等替换为你实际的图片文件名
  2. 本地适配优化
    • 注释掉了从远程 API 获取图片列表的代码,因为我们使用本地图片
    • 背景从在线图片改为深色纯色背景,避免与本地图片风格冲突
  3. 错误处理增强
    • 新增了checkImageExists函数,专门检查本地图片是否存在
    • 错误提示中会显示具体的图片路径,方便你排查路径问题
  4. 毛玻璃效果调整
    • 边框颜色从白色系改为深色系(rgba(0, 0, 0, 0.3)),更适合本地图片展示

📌排查本地图片不显示的常见问题

  1. 路径是否正确
    • 确保你的 HTML 文件和images目录在同一级目录
    • 例如:index.htmlimages文件夹平级,图片放在images
  2. 文件名是否正确
    • 注意文件名的大小写(尤其是在 Linux/macOS 系统)
    • 扩展名是否正确(是jpg还是jpegpng还是gif
  3. 文件权限问题
    • 确保图片文件有可读权限

如果仍然无法显示,可以打开浏览器的开发者工具(F12),查看控制台中的错误信息,那里会显示具体的图片加载失败原因。

v4-2025.9.14-不能自动扫描目录,需结合后端(待解决)

问题描述

bash
var images = [
            'images/photo1.jpg',   // 替换为你的图片1
            'images/photo2.png',   // 替换为你的图片2
            'images/photo3.jpg',   // 替换为你的图片3
            'images/photo4.png'    // 替换为你的图片4
        ];
        

这里我不想每次都手动写路径,能不能自动扫描这个images目录下的图片呢?

豆包输出

要实现自动扫描images目录下的图片并动态加载,需要结合后端程序来实现目录扫描(因为浏览器的安全限制,JavaScript 无法直接访问本地文件系统)。以下是一个前后端结合的解决方案:

📌实现思路

  1. 后端(Go)提供一个 API 接口,用于扫描images目录并返回所有图片路径
  2. 前端通过这个 API 获取图片列表,而不是手动维护路径数组

📌前端代码修改(HTML/JavaScript)

自动扫描图片目录的轮播

V1

创建时间:07:59

html
<!DOCTYPE html>
<html>
<head>
    <title>随机图片展示(自动扫描版)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        /* 保持原有样式不变 */
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
            background-color: #222;
        }
        .image-container {
            position: relative;
            width: 100%;
            height: 100%;
        }
        
        .image-container::before,
        .image-container::after {
            content: '';
            position: absolute;
            top: 0;
            height: 100%;
            width: 15%;
            z-index: 5;
            pointer-events: none;
        }
        
        .image-container::before {
            left: 0;
            background: linear-gradient(90deg, 
                rgba(0, 0, 0, 0.3) 0%, 
                rgba(0, 0, 0, 0) 100%);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
        }
        
        .image-container::after {
            right: 0;
            background: linear-gradient(270deg, 
                rgba(0, 0, 0, 0.3) 0%, 
                rgba(0, 0, 0, 0) 100%);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
        }

        .image-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            transform: scale(1);
            object-fit: contain;
            opacity: 0.98;
        }
        
        .image-layer.transitioning {
            transition: opacity 2.5s cubic-bezier(0.25, 0.1, 0.25, 1), 
                       transform 2.5s cubic-bezier(0.25, 0.1, 0.25, 1);
        }
        
        .image-layer.active {
            opacity: 1;
            animation: breathe 10s ease-in-out infinite;
        }
        
        @keyframes breathe {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.03);
            }
        }

        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #fff;
            font-size: 18px;
            z-index: 10;
            text-shadow: 0 2px 4px rgba(0,0,0,0.3);
            backdrop-filter: blur(5px);
            -webkit-backdrop-filter: blur(5px);
            padding: 10px 20px;
            border-radius: 8px;
            background-color: rgba(0,0,0,0.2);
        }
    </style>
</head>
<body>
    <div class="image-container">
        <div class="loading" id="loading">加载图片列表中...</div>
        <img class="image-layer" id="image1" src="" alt="轮播图片">
        <img class="image-layer" id="image2" src="" alt="轮播图片">
    </div>

    <script>
        var images = [];  // 空数组,将从API获取
        var image1 = document.getElementById('image1');
        var image2 = document.getElementById('image2');
        var loading = document.getElementById('loading');
        var currentLayer = 1;
        var imageInterval;
        var isTransitioning = false;

        // 从API获取图片列表
        async function fetchImageList() {
            try {
                const response = await fetch('/api/images');
                if (!response.ok) {
                    throw new Error(`获取图片列表失败: ${response.status}`);
                }
                const data = await response.json();
                return data.images || [];
            } catch (error) {
                console.error('获取图片列表出错:', error);
                return [];
            }
        }

        function preloadImage(src) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(src);
                img.onerror = () => reject(new Error(`图片加载失败: ${src}`));
                img.src = src;
            });
        }

        function adjustImageDisplay(imgElement, src) {
            return new Promise((resolve) => {
                const tempImg = new Image();
                tempImg.onload = () => {
                    const imgRatio = tempImg.width / tempImg.height;
                    const containerRatio = window.innerWidth / window.innerHeight;

                    if (imgRatio < 1) {
                        imgElement.style.objectFit = 'contain';
                    } else {
                        imgElement.style.objectFit = 'contain';
                    }
                    resolve();
                };
                tempImg.src = src;
            });
        }

        async function fadeToNewImage(src) {
            if (isTransitioning || !src) return;
            isTransitioning = true;

            const nextLayer = currentLayer === 1 ? 2 : 1;
            const currentImg = currentLayer === 1 ? image1 : image2;
            const nextImg = nextLayer === 1 ? image1 : image2;

            try {
                await preloadImage(src);
                await adjustImageDisplay(nextImg, src);

                nextImg.src = src;
                nextImg.className = 'image-layer';

                const currentTransform = window.getComputedStyle(currentImg).transform;
                let currentScale = 1;
                if (currentTransform && currentTransform !== 'none') {
                    const matrix = currentTransform.match(/matrix.*\((.+)\)/);
                    if (matrix) {
                        const values = matrix[1].split(', ');
                        currentScale = parseFloat(values[0]) || 1;
                    }
                }

                currentImg.classList.add('transitioning');
                nextImg.classList.add('transitioning');
                nextImg.offsetHeight;

                requestAnimationFrame(() => {
                    if (currentImg.classList.contains('active')) {
                        currentImg.style.transform = `scale(${currentScale})`;
                        currentImg.style.opacity = '1';
                        requestAnimationFrame(() => {
                            currentImg.style.opacity = '0';
                            currentImg.style.transform = 'scale(0.98)';
                        });
                    }

                    nextImg.style.opacity = '0';
                    nextImg.style.transform = 'scale(1)';
                    requestAnimationFrame(() => {
                        nextImg.style.opacity = '1';
                        nextImg.style.transform = 'scale(1)';
                    });
                });

                setTimeout(() => {
                    currentImg.className = 'image-layer';
                    currentImg.style.cssText = '';
                    nextImg.className = 'image-layer active';
                    nextImg.style.cssText = '';
                    currentLayer = nextLayer;
                    isTransitioning = false;
                }, 2500);

            } catch (error) {
                console.error(error);
                loading.textContent = error.message;
                isTransitioning = false;
                setTimeout(changeImage, 2000);
            }
        }

        async function initFirstImage() {
            // 先从API获取图片列表
            images = await fetchImageList();
            
            if (images.length === 0) {
                loading.textContent = 'images目录中未找到图片';
                return;
            }
            
            const firstSrc = images[Math.floor(Math.random() * images.length)];
            try {
                await preloadImage(firstSrc);
                await adjustImageDisplay(image1, firstSrc);
                image1.src = firstSrc;
                setTimeout(() => {
                    image1.className = 'image-layer active';
                    loading.style.opacity = '0';
                    setTimeout(() => loading.style.display = 'none', 500);
                }, 300);
            } catch (error) {
                console.error('初始化图片失败:', error);
                loading.textContent = error.message;
                setTimeout(initFirstImage, 2000);
            }
        }

        function changeImage() {
            if (images.length === 0) {
                clearInterval(imageInterval);
                return;
            }
            const randomSrc = images[Math.floor(Math.random() * images.length)];
            fadeToNewImage(randomSrc);
        }

        // 初始化
        initFirstImage();
        
        // 定时切换
        imageInterval = setInterval(changeImage, 15000);
        
        // 每60秒刷新一次图片列表(支持动态添加新图片)
        setInterval(async () => {
            const newImages = await fetchImageList();
            if (newImages.length > 0) {
                images = newImages;
                // 如果之前没有图片,现在有了就重新开始轮播
                if (!imageInterval) {
                    imageInterval = setInterval(changeImage, 15000);
                    loading.style.display = 'none';
                    initFirstImage();
                }
            } else {
                clearInterval(imageInterval);
                imageInterval = null;
                loading.style.display = 'block';
                loading.textContent = 'images目录中未找到图片';
            }
        }, 60000);
    </script>
</body>
</html>

📌后端代码(Go)

需要在你的 Go 项目中添加一个 API 接口,用于扫描images目录并返回图片路径列表:

Go图片扫描API

V1

创建时间:07:59

go
package main

import (
	"encoding/json"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// 图片文件扩展名
var imageExtensions = map[string]bool{
	".jpg":  true,
	".jpeg": true,
	".png":  true,
	".gif":  true,
	".bmp":  true,
	".webp": true,
}

// 扫描images目录下的所有图片
func scanImages() ([]string, error) {
	var images []string
	root := "images" // 图片目录

	// 检查目录是否存在
	if _, err := os.Stat(root); os.IsNotExist(err) {
		return images, nil // 目录不存在返回空列表
	}

	// 遍历目录
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 只处理文件
		if !info.IsDir() {
			ext := strings.ToLower(filepath.Ext(path))
			// 检查是否为图片文件
			if imageExtensions[ext] {
				images = append(images, path)
			}
		}
		return nil
	})

	return images, err
}

// API处理器:返回图片列表
func imagesHandler(w http.ResponseWriter, r *http.Request) {
	images, err := scanImages()
	if err != nil {
		http.Error(w, "扫描图片失败: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// 设置响应头为JSON
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"images": images,
	})
}

func main() {
	// 静态文件服务:提供HTML和图片访问
	http.Handle("/", http.FileServer(http.Dir(".")))
	
	// 图片列表API
	http.HandleFunc("/api/images", imagesHandler)

	// 启动服务器
	http.ListenAndServe(":8080", nil)
}

📌实现原理

  1. 后端(Go)
    • 创建/api/images接口,使用filepath.Walk扫描images目录
    • 过滤出图片格式文件(支持 jpg、png、gif 等常见格式)
    • 以 JSON 格式返回图片路径列表
  2. 前端(JavaScript)
    • 页面加载时通过fetch('/api/images')获取图片列表
    • 每 60 秒自动刷新一次图片列表,支持动态添加新图片
    • 无需手动维护images数组,完全自动获取

📌使用方法

  1. 将 HTML 文件放在项目根目录
  2. 创建images目录并放入图片
  3. 运行 Go 程序:go run main.go
  4. 访问http://localhost:8080即可看到自动加载的图片轮播

这样,你只需将图片放入images目录,无需修改任何代码,轮播会自动识别并加载新图片。

最近更新