Skip to content

实践

此部分为JS学习实践部分,包括一些已有JS源码的学习和改写

Waterfall-Bg

class WaterfallBackground {
    constructor() {
        // 获取具有类名 "waterfall-column" 的所有元素
        this.columns = document.querySelectorAll(".waterfall-column");
        this.images = [
        ];

        // 调用 init 方法
        this.init();
    }

    shuffleArray(array) {
        for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }
        return array;
    }
    init() {
        this.columns.forEach(column => {
            const shuffledImages = this.shuffleArray([...this.images]); // 打乱图片数组
            const wrapper = document.createElement("div"); // 创建第一个 wrapper
            const wrapper2 = document.createElement("div"); // 创建第二个 wrapper

            // 设置 wrapper 的类名
            wrapper.className = "column-wrapper"; 
            wrapper2.className = "column-wrapper"; // 直接赋值

            // 遍历打乱后的图片数组
            shuffledImages.forEach(imgSrc => {
                const img = document.createElement("img"); // 创建图片元素
                img.src = imgSrc; // 设置图片路径
                img.loading = "lazy"; // 设置懒加载
                wrapper.appendChild(img); // 将图片添加到第一个 wrapper

                const img2 = img.cloneNode(true); // 克隆图片元素
                wrapper2.appendChild(img2); // 将克隆的图片添加到第二个 wrapper
            });

            // 将两个 wrapper 添加到列中
            column.appendChild(wrapper);
            column.appendChild(wrapper2);

            // 设置滚动动画
            this.setScrollAnimation(column);
        });

        // 添加事件监听器
        document.addEventListener("mousemove", this.handleParallax.bind(this));
    }
    setScrollAnimation(column) {
        let scrolling = true; // 控制动画启停的标志

        const wrappers = column.children;
        const firstWrapper = wrappers[0]; // 第一个图片容器
        const secondWrapper = wrappers[1]; // 第二个图片容器

        // 初始化第二个容器的位置,使其紧接在第一个容器下方
        secondWrapper.style.transform = `translateY(${firstWrapper.offsetHeight}px)`;

        const animate = () => {
            if (!scrolling) return; // 如果动画已暂停,直接退出

            // 更新两个容器的位置(固定位移量 0.5px)
            firstWrapper.style.transform = `translateY(-${window.scrollY * 0.5}px)`; // 向上滚动
            secondWrapper.style.transform = `translateY(${firstWrapper.offsetHeight + window.scrollY * 0.5}px)`; // 向下滚动

            // 关键修正:当第一个容器完全滚出视口时,将其重置到底部
            if (-parseInt(firstWrapper.style.transform.split("translateY(")[1]) >= firstWrapper.offsetHeight) {
                firstWrapper.style.transform = `translateY(${secondWrapper.offsetHeight}px)`;
            }

            // 关键修正:当第二个容器完全滚出视口时,将其重置到顶部
            if (parseInt(secondWrapper.style.transform.split("translateY(")[1]) >= secondWrapper.offsetHeight) {
                secondWrapper.style.transform = `translateY(-${firstWrapper.offsetHeight}px)`;
            }

            requestAnimationFrame(animate); // 递归调用动画
        };

        animate(); // 启动动画

        // 监听页面可见性变化(切换标签页时暂停动画)
        document.addEventListener("visibilitychange", () => {
            scrolling = !document.hidden;
            if (scrolling) animate();
        });
    }

   handleParallax(e) {
        // 计算鼠标在 X 和 Y 方向上的平移量
        const moveX = (e.clientX - window.innerWidth / 2) * 0.01;  // 根据鼠标位置计算水平方向的平移
        const moveY = (e.clientY - window.innerHeight / 2) * 0.01;  // 根据鼠标位置计算垂直方向的平移

        // 遍历所有的列元素,并应用平移效果
        this.columns.forEach(column => {
            column.style.transform = `translate(${moveX}px, ${moveY}px)`;
        });
    }
}
document['addEventListener']("DOMContentLoaded", () => {
    new WaterfallBackground();
});

Comments