原生js模仿B站首页之冬季Banner的实现

实现效果

图层效果分析

Banner区总共七张图片加一个无限循环的视频,随着鼠标的移动,呈现出不同时间段的效果。在鼠标的移动的过程中,给各个图片设置相应的位移和透明度来实现视觉效果的转换。整个Banner区的效果分为三个时段:早上、下午和晚上。默认显示的是下午的图片,鼠标由左到右会慢慢显示出晚上的效果,由右到左则慢慢显示早上的效果。

实现步骤

按B站布局方式放置好图片和视频

<div class="animate-box">
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/0.jpg"></div>
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/1.jpg"></div>
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/2.png"
         style="width: 2400px;height: 184px;transform: translate(40px, 16px) rotate(10deg);"></div>
    <div class="layer">
         <video loop="loop" src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/video.webm" style="object-fit: cover; opacity: 0;" autoplay="autoplay"
                    muted="muted"></video>
    </div>
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/3.png" style="opacity: 0;"></div>
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/4.png" style="filter: blur(2px);"></div>
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/5.png" style="filter: blur(2px);"></div>
    <div class="layer"><img src="https://cdn.jsdelivr.net/gh/Zhangzyb/BiliBili@1.0/images/6.png" style="opacity: 0; filter: blur(5px);"></div>
    <canvas id="canvas" width="1519" height="155"></canvas>
</div>

<style>
.animate-box {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    overflow: hidden;
}

.layer {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.layer img,
video {
    height: 155px;
    width: 2016px;
    transform: scale(1) translate(0px, 0px) rotate(0deg);
    filter: blur(0px);
    opacity: 1;
}</style>

使用js的鼠标事件完成效果

const box = document.querySelector('.animate-box');
let imgs = document.querySelectorAll('.layer img');
let video = document.querySelector('.layer video');

let initPosition;       // 进入盒子的初始位置
let mouseMove;          // 鼠标在盒子内的移动距离
let commonMove;         // 背景图、视频和水汽的移动距离
let branchesMove;       // 树枝移动距离
let snowMove;           // 雪球的移动距离
let snowRotate;         // 雪球的旋转度

// 鼠标从左到右
let videoOpacity;      // 视频移动过程中的透明度
let steamOpacity;      // 玻璃窗上的水汽移动过程中的透明度
let nightBranchOpacity;   // 晚上树枝的透明度

// 鼠标从右到左 
let afternoonImgOpacity;      // 下午的背景图片透明度
let snowOpacity;              // 雪球的透明度
let afternoonBranchOpacity;   // 下午的树枝透明度


box.addEventListener('mouseenter', (e) => {
    initPosition = e.pageX;
})

box.addEventListener('mousemove', (e) => {
    mouseMove = initPosition - e.pageX; // 小于0则表示鼠标再向右移动,否则鼠标在向左移动
    commonMove = mouseMove / 15;
    branchesMove = mouseMove / 11;
    snowMove = mouseMove / 5.5;
    snowRotate = mouseMove / 307;

    // 背景图片和视频的移动
    video.style.transform = 'translate(' + commonMove + 'px,0px)';  // 视频
    imgs[0].style.transform = 'translate(' + commonMove + 'px,0px)'; // 早上的背景图片
    imgs[1].style.transform = 'translate(' + commonMove + 'px,0px)'; // 下午的背景图片
    imgs[3].style.transform = 'translate(' + commonMove + 'px,0px)'; // 水汽

    // 雪球的位移
    imgs[2].style.transform = 'translate(' + (40 + snowMove) + 'px,' + (16 - mouseMove / 220) + 'px) rotate(' + (10 + snowRotate) + 'deg)';

    // 树枝的移动
    // for (let i = 4; i < 7; i++) {
    //     imgs[i].style.transform = 'translate(' + branchesMove + 'px,0px)';
    // }
    imgs[4].style.transform = 'translate(' + branchesMove + 'px,0px)';
    imgs[5].style.transform = 'translate(' + branchesMove + 'px,0px)';
    imgs[6].style.transform = 'translate(' + branchesMove + 'px,0px)';


    if (mouseMove < 0) { //鼠标向右移动
        // 在鼠标向右移动的过程中,视频、水汽和晚上的树枝的透明度均经过 0 ~ 1 的变化,其余不变
        // 并且三个元素的透明度变化速度各不相同
        videoOpacity = Math.abs(commonMove / 100) * 3;          // 视频的透明度
        steamOpacity = Math.abs(commonMove / 100) * 1.5;        // 水汽的透明度
        nightBranchOpacit = Math.abs(branchesMove / 100) * 2.2;   // 晚上树枝的透明度
        // 透明度超过 1 时均赋值为 1
        video.style.opacity = videoOpacity >= 1 ? 1 : videoOpacity;
        imgs[3].style.opacity = steamOpacity >= 1 ? 1 : steamOpacity;
        imgs[6].style.opacity = nightBranchOpacit >= 1 ? 1 : nightBranchOpacit;
    }
    else { // 鼠标向左移动
        // 鼠标向左移动过程中,下午的图片、雪球的图片和下午的树枝的透明度均经过 1 ~ 0 的变化,其余不变
        afternoonImgOpacity = 1 - (commonMove / 100) * 4;
        snowOpacity = 1 - (commonMove / 100) * 2.4;
        afternoonBranchOpacity = 1 - (commonMove / 100) * 3.2;
        // 透明度低于 0 时均赋值为 0
        imgs[1].style.opacity = afternoonImgOpacity <= 0 ? 0 : afternoonImgOpacity;         // 下午的图片透明度   
        imgs[2].style.opacity = snowOpacity <= 0 ? 0 : snowOpacity;                         // 雪球图片透明度
        imgs[5].style.opacity = afternoonBranchOpacity <= 0 ? 0 : afternoonBranchOpacity;   // 下午的树枝透明度   
    }
})

box.addEventListener('mouseleave', () => {
    //  鼠标从左到右移动离开图片区
    //  位移距离还原
    video.style.transform = 'translate(0,0)';
    imgs[0].style.transform = 'translate(0,0)';
    imgs[1].style.transform = 'translate(0,0)';
    imgs[2].style.transform = 'translate(40px, 16px) rotate(10deg)';
    imgs[3].style.transform = 'translate(0,0)';
    imgs[4].style.transform = 'translate(0,0)';
    imgs[5].style.transform = 'translate(0,0)';
    imgs[6].style.transform = 'translate(0,0)';

    // 透明度还原
    video.style.opacity = 0;
    imgs[3].style.opacity = 0;
    imgs[6].style.opacity = 0;

    // 鼠标从右到左离开图片区
    // 透明度还原
    imgs[1].style.opacity = 1;  // 下午的背景图片
    imgs[2].style.opacity = 1;  // 雪球图片
    imgs[5].style.opacity = 1;  // 下午的树枝图片
}) 

Canvas完成雪花效果

// 雪花特效 

let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');

canvas.style.position = 'absolute';
canvas.style.top = 0;
canvas.style.left = 0;

class Snow {
    constructor(canvas) {
        this.x = 0;
        this.y = 0;
        this.vy = 0;
        this.radius = 0;
        this.alpha = 0.5;
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.init();
    }

    init() {
        this.x = Math.random() * this.canvas.width;
        this.vy = 0.3 + Math.random() * 1;
        this.radius = 1 + Math.random() * 3;
        this.alpha += Math.random() * 0.5;
        this.ctx.fillStyle = '#ffffff';
    }

    draw() {
        this.ctx.globalAlpha = this.alpha;
        this.ctx.beginPath();
        this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
        this.ctx.closePath();
        this.ctx.fill();
    }
}

let snows = []
let cnt = 0;

function moveSnow(snow) {
    snow.y += snow.vy;
    if (snow.y >= canvas.height) {
        snow.y = 0
    }
    snow.draw();
}

function drawSnow() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    cnt++;
    if (cnt <= 60) {
        let snow = new Snow(canvas);
        snows.push(snow);
    }
    snows.forEach(moveSnow);
    window.requestAnimationFrame(drawSnow);
}

window.requestAnimationFrame(drawSnow);   

完整代码 预览地址

posted @ 2021-02-18 13:35  zhangzyb  阅读(63)  评论(0)    收藏  举报