最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Bilibili网站首图动态Banner开发实现

    正文概述 掘金(天小天)   2021-03-29   610

    Bilibili网站首图动态Banner开发实现

    一、技术思考

    通过F12对Banner的观察,图片是由多张PNG图片相互叠加而成,监听鼠标移动实现图片的偏移,而飘落的花瓣则是使用canvas实现。下面就让我们来尝试一下。

    二、项目目录结构

    |bilibili-banner
    |
    |------css//样式
    |+---index.css
    |
    |------image//图片素材
    |+---......
    |
    |------js//逻辑代码
    |+---index.js
    |+---canvas.js
    |
    |--index.html
    

    三、CSS样式和HTMl代码

    这部分代码比较简单就不多赘述。

    1. /css/index.css

    *{
    	margin: 0;
    	padding: 0;
    }
    .bili-banner{
    	margin: 0 auto;
        position: relative;
        z-index: 0;
    	width: 100%;
    	height: 9.375vw;
        min-height: 155px;
        min-width: 999px;
        background-color: #f9f9f9;
        display: flex;
        justify-content: center;
    }
    .animated-banner{
    	position: absolute;
    	top: 0;
    	bottom: 0;
    	left: 0;
    	right: 0;
    	margin: auto;
    	overflow: hidden;
    }
    
    .layer{
    	position: absolute;
    	left: 0;
    	top: 0;
    	height: 100%;
    	width: 100%;
    	display: flex;
    	align-items: center;
    	justify-content: center;
    }
    
    .layer img{
    	transition: transform 0.2s;
    }
    

    2. /index.html

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>Banner</title>
    		<link href="css/index.css" rel="stylesheet" />
    	</head>
    	<body>
    		<div class="bili-banner">
    			<div class="animated-banner">
    				<!-- 背景 -->
    				<div class="layer">
    					<img 
    						src="image/bg1.png" 
    						data-height="360" data-width="9666" height="180" width="4833" 
    						style="transform: scale(1) translate(0px, -15px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 左山 -->
    				<div class="layer">
    					<img 
    						src="image/bg2.png" 
    						data-height="360" data-width="9666" height="180" width="4833" 
    						style="transform: scale(1) translate(1100px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 右山 -->
    				<div class="layer">
    					<img 
    						src="image/bg3.png"
    						data-height="360" data-width="3523" height="162" width="1585"
    						style="transform: scale(1) translate(675px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 左桥 -->
    				<div class="layer">
    					<img 
    						src="image/bg4.png"
    						data-height="360" data-width="2938" height="176" width="1439"
    						style="transform: scale(1) translate(-637px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 右船 -->
    				<div class="layer">
    					<img 
    						src="image/bg5.png"
    						data-height="139" data-width="556" height="62" width="250"
    						style="transform: scale(1) translate(607.5px, 45px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 开船人物 -->
    				<div class="layer">
    					<img 
    						src="image/p4.png"
    						data-height="302" data-width="734" height="84" width="205"
    						style="transform: scale(1) translate(252px, 36.4px) rotate(0deg); opacity: 0;"
    					>
    				</div>
    				<!-- 中草坪+树 -->
    				<div class="layer">
    					<img 
    						src="image/bg6.png"
    						data-height="180" data-width="1757" height="125" width="1229"
    						style="transform: scale(1) translate(112px, 25px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 中草坪+风筝 -->
    				<div class="layer">
    					<img 
    						src="image/bg7.png"
    						data-height="116" data-width="1757" height="81" width="1229"
    						style="transform: scale(1) translate(-350px, 49px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 人物 -->
    				<div class="layer">
    					<img 
    						src="image/p2.png"
    						data-height="346" data-width="497" height="138" width="198"
    						style="transform: scale(1) translate(-240px, 16px) rotate(0deg); opacity: 0;"
    					>
    				</div>
    				<!-- 人物 -->
    				<div class="layer">
    					<img 
    						src="image/p1.png"
    						data-height="256" data-width="146" height="102" width="58"
    						style="transform: scale(1) translate(-340px, 32px) rotate(0deg); opacity: 0;"
    					>
    				</div>
    				<!-- 中树 -->
    				<div class="layer">
    					<img 
    						src="image/t1.png"
    						data-height="254" data-width="602" height="114" width="270"
    						style="transform: scale(1) translate(-90px, 13.5px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 中草坪+树 -->
    				<div class="layer">
    					<img 
    						src="image/bg8.png"
    						data-height="360" data-width="4277" height="180" width="2138"
    						style="transform: scale(1) translate(100px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 中人物 -->
    				<div class="layer">
    					<img 
    						src="image/p3.png"
    						data-height="327" data-width="933" height="147" width="419"
    						style="transform: scale(1) translate(216px, 13.5px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 右树 -->
    				<div class="layer">
    					<img 
    						src="image/t3.png"
    						data-height="353" data-width="740" height="211" width="444"
    						style="transform: scale(1) translate(2100px, 0px) rotate(0deg); filter: blur(2px); opacity: 1;"
    					>
    				</div>
    				<!-- 左树 -->
    				<div class="layer">
    					<img 
    						src="image/t2.png"
    						data-height="360" data-width="1916" height="180" width="958"
    						style="transform: scale(1) translate(-1000px, 0px) rotate(0deg); filter: blur(1px); opacity: 1;"
    					>
    				</div>
    				<canvas width="1519" height="155" style="position: absolute; top: 0px; left: 0px;" id="canvas"></canvas>
    			</div>
    		</div>
    	</body>
    	<script src="js/index.js"></script>
    	<script src="js/canvas.js"></script>
    	<script>
    		......
    	</script>
    </html>
    
    

    四、监听鼠标移动控制图片平移

    这一部分代码主要是在/js/index.js文件中

    1. 具体实现思路

    1. 获取到所有.layer节点,从而更方便的获取img节点
    2. 监听Banner的mouseenter事件记录鼠标移入的clientX值
    3. 监听Banner的mousemove事件计算鼠标X方向移动距离并调用图片移动方法move
    4. 监听Banner的mouseleave事件清除监听并调用图片复位方法方法imgRest
    5. 至此就实现banner通过鼠标移动控制图片平移
    6. 具体的mova和imgRest方法其实就是改变对应图片的style中transform和opacity实现
    7. 其中涉及到一个工具函数GetTranslate来获取图片原始移动距离
    8. 注:没有做代码的精简方便大家理解

    2. /js/index.js

    let banner = document.querySelector('.bili-banner');
    let layers = document.getElementsByClassName('layer');
    
    let overX = 0;//鼠标移入时x坐标
    let leaveX = 0;//鼠标移出时x坐标
    
    //鼠标移入
    banner.addEventListener('mouseenter',event=>{
    	event.preventDefault();
    	overX = event.clientX;
    })
    
    //鼠标移出
    banner.addEventListener('mouseleave',event=>{
    	event.preventDefault();
    	leaveX = event.clientX;
    	removeEventListener("mousemove",move);
    	//图片复位
    	imgRest();
    })
    
    //鼠标移动
    banner.addEventListener('mousemove',move);
    
    //鼠标移动距离
    function move(e){
    	e.preventDefault();
    	let startX = overX; 
    	let endX = e.clientX;
    	let moveX = startX-endX;
    	//图片移动
    	imgMove(moveX);
    }
    
    //0
    let img0 = layers[0].children[0];
    let img0StartX = GetTranslate(img0,'x');
    //1
    let img1 = layers[1].children[0];
    let img1StartX = GetTranslate(img1,'x');
    //2
    let img2 = layers[2].children[0];
    let img2StartX = GetTranslate(img2,'x');
    //3
    let img3 = layers[3].children[0];
    let img3StartX = GetTranslate(img3,'x');
    //4
    let img4 = layers[4].children[0];
    let img4StartX = GetTranslate(img4,'x');
    //5 主角船
    let img5 = layers[5].children[0];
    let img5StartX = GetTranslate(img5,'x');
    //6 
    let img6 = layers[6].children[0];
    let img6StartX = GetTranslate(img6,'x');
    //7 
    let img7 = layers[7].children[0];
    let img7StartX = GetTranslate(img7,'x');
    //8 主角风筝1
    let img8 = layers[8].children[0];
    let img8StartX = GetTranslate(img8,'x');
    //9 主角风筝2
    let img9 = layers[9].children[0];
    let img9StartX = GetTranslate(img9,'x');
    //10
    let img10 = layers[10].children[0];
    let img10StartX = GetTranslate(img10,'x');
    //11
    let img11 = layers[11].children[0];
    let img11StartX = GetTranslate(img11,'x');
    //12
    let img12 = layers[12].children[0];
    let img12StartX = GetTranslate(img12,'x');
    //13
    let img13 = layers[13].children[0];
    let img13StartX = GetTranslate(img13,'x');
    //14
    let img14 = layers[14].children[0];
    let img14StartX = GetTranslate(img14,'x');
    
    //图片移动
    function imgMove(moveX){
    	//背景
    	img0.style.transform = "scale(1) translate("+(img0StartX+moveX*0.1)+"px, -15px) rotate(0deg)";
    	//左山
    	img1.style.transform = "scale(1) translate("+(img1StartX+moveX*0.1)+"px, 0px) rotate(0deg)";
    	//右山
    	img2.style.transform = "scale(1) translate("+(img2StartX+moveX*0.03)+"px, 0px) rotate(0deg)";
    	//左桥
    	img3.style.transform = "scale(1) translate("+(img3StartX+moveX*0.11)+"px, 0px) rotate(0deg)";
    	//右船
    	img4.style.transform = "scale(1) translate("+(img4StartX+moveX*0.1)+"px, 45px) rotate(0deg)";
    	//中草坪+树
    	img6.style.transform = "scale(1) translate("+(img6StartX+moveX*0.5)+"px, 25px) rotate(0deg)";
    	//中草坪+风筝
    	img7.style.transform = "scale(1) translate("+(img7StartX+moveX*0.5)+"px, 49px) rotate(0deg)";
    	//中树
    	img10.style.transform = "scale(1) translate("+(img10StartX+moveX*1)+"px, 13.5px) rotate(0deg)";
    	//中草坪+树
    	img11.style.transform = "scale(1) translate("+(img11StartX+moveX*1)+"px, 0px) rotate(0deg)";
    	//中人物
    	img12.style.transform = "scale(1) translate("+(img12StartX+moveX*1)+"px, 13.5px) rotate(0deg)";
    	//右树
    	img13.style.transform = "scale(1) translate("+(img13StartX+moveX*1.5)+"px, 0px) rotate(0deg)";
    	//左树
    	img14.style.transform = "scale(1) translate("+(img14StartX+moveX*1.5)+"px, 0px) rotate(0deg)";
    	//人物的显示
    	if(moveX < 200){
    		img5.style.transform = "scale(1) translate("+(img5StartX-moveX*0.25)+"px, 36.4px) rotate(0deg)";
    		img5.style.opacity = 1;
    		img8.style.opacity = 0;
    		img9.style.opacity = 0;
    	}else{
    		img8.style.transform = "scale(1) translate("+(img8StartX+moveX*0.2)+"px, 16px) rotate(0deg)";
    		img9.style.transform = "scale(1) translate("+(img9StartX+moveX*0.4)+"px, 32px) rotate(0deg)";
    		img5.style.opacity = 0;
    		img8.style.opacity = 1;
    		img9.style.opacity = 1;
    	}
    }
    
    //图片复位
    function imgRest(){
    	img0.style.transform = "scale(1) translate("+(img0StartX)+"px, -15px) rotate(0deg)";
    	img1.style.transform = "scale(1) translate("+(img1StartX)+"px, 0px) rotate(0deg)";
    	img2.style.transform = "scale(1) translate("+(img2StartX)+"px, 0px) rotate(0deg)";
    	img3.style.transform = "scale(1) translate("+(img3StartX)+"px, 0px) rotate(0deg)";
    	img4.style.transform = "scale(1) translate("+(img4StartX)+"px, 45px) rotate(0deg)";
    	img5.style.transform = "scale(1) translate("+(img5StartX)+"px, 36.4px) rotate(0deg)";
    	img6.style.transform = "scale(1) translate("+(img6StartX)+"px, 25px) rotate(0deg)";
    	img7.style.transform = "scale(1) translate("+(img7StartX)+"px, 49px) rotate(0deg)";
    	img8.style.transform = "scale(1) translate("+(img8StartX)+"px, 16px) rotate(0deg)";
    	img9.style.transform = "scale(1) translate("+(img9StartX)+"px, 32px) rotate(0deg)";
    	img10.style.transform = "scale(1) translate("+(img10StartX)+"px, 13.5px) rotate(0deg)";
    	img11.style.transform = "scale(1) translate("+(img11StartX)+"px, 0px) rotate(0deg)";
    	img12.style.transform = "scale(1) translate("+(img12StartX)+"px, 13.5px) rotate(0deg)";
    	img13.style.transform = "scale(1) translate("+(img13StartX)+"px, 0px) rotate(0deg)";
    	img14.style.transform = "scale(1) translate("+(img14StartX)+"px, 0px) rotate(0deg)";
    	//人物隐藏
    	img5.style.opacity = 0;
    	img8.style.opacity = 0;
    	img9.style.opacity = 0;
    }
    
    //js获取x偏移
    function GetTranslate(node, sty) {
        var translates = document.defaultView.getComputedStyle(node, null).transform.substring(7);
    	var result = translates.match(/\(([^)]*)\)/);// 正则()内容
    	var matrix = result ? result[1].split(',') : translates.split(',');
    	if (sty == "x" || sty == undefined) {
    	    return matrix.length > 6 ? parseFloat(matrix[12]) : parseFloat(matrix[4]);
    	} else if (sty == "y") {
    	    return matrix.length > 6 ? parseFloat(matrix[13]) : parseFloat(matrix[5]);
    	} else if (sty == "z") {
    	    return matrix.length > 6 ? parseFloat(matrix[14]) : 0;
    	} else if (sty == "rotate") {
    	    return matrix.length > 6 ? getRotate([parseFloat(matrix[0]), parseFloat(matrix[1]), parseFloat(matrix[4]), parseFloat(matrix[5])]) : getRotate(matrix);
    	}
    }
    

    五、花瓣飘落实现

    这一部分代码主要是在/js/canvas.js文件中

    1. 具体实现思路

    1. 获取canva节点和getContext
    2. loading方法加载图片资源
    3. 定义花瓣类Flower
    4. 设置Flower的基本属性和渲染方法draw以及飘落方法updata
    5. createFlower创建指定数量花瓣
    6. allDraw方法渲染所有画面内花瓣
    7. 其中使用工具函数rand生成随机数
    8. 花瓣飘落出画面时会被清除
    9. 花瓣位置速度大小都是随机生成
    10. 在index.html中调用相关方法并开启定时器

    2./js/canvas.js

    let canvas = document.querySelector('#canvas');
    let ctx = canvas.getContext('2d');
    let sw = canvas.width;
    let sh = canvas.height;
    let imgs = {
    	"f1": 'image/f1.png',
    	"f2": 'image/f2.png',
    };
    //图片加载
    function loading() {
    	//全部资源数量
    	let AllAmount = Object.keys(imgs).length;
    	//加载完毕的资源数量
    	let count = 0;
    
    	for (k in imgs) {
    		//图片地址
    		let src = imgs[k]
    		//创建图片
    		imgs[k] = new Image()
    		//赋值图片地址
    		imgs[k].src = src;
    		imgs[k].onload = function() {
    			count++;
    			if (count == AllAmount) {
    				start();
    			}
    		}
    	}
    }
    
    //定义花瓣类
    class Flower {
    	constructor(img, x, y, w, h) {
    		this.img = img;
    		this.x = x;
    		this.y = y;
    		this.w = w;
    		this.h = h;
    		this.del = false;
    	}
    	//渲染
    	draw() {
    		ctx.drawImage(this.img, this.x, this.y, this.w, this.h);
    	}
    	//飘落
    	updata() {
    		if (this.x > sw || this.y > sh) {
    			this.del = true;
    		}else{
    			this.del = false;
    		}
    		this.x += Math.random() * 3 + 1;
    		this.y += Math.random() * 3 + 1;
    	}
    }
    
    let fs = [];
    //创建花瓣
    function createFlower() {
    	let wh = rand(0, 15);
    	let flower = null;
    	if(fs.length<35){
    		if (rand(0, 10) > 5) {
    			flower = new Flower(imgs['f1'], rand(20, sw - 20), rand(0,10), wh, wh);
    		} else {
    			flower = new Flower(imgs['f2'], rand(20, sw - 20), rand(0,10), wh, wh);
    		}
    		fs.push(flower);
    	}
    }
    //渲染
    function allDraw(){
    	for (i = 0; i < fs.length; i++) {
    		let flower = fs[i];
    		if(flower.del){		
    			fs.splice(i,1);
    			i--;
    			continue;
    		}
    		flower.draw();
    		flower.updata();
    	}
    }
    
    //随机数
    function rand(min, max) {
    	return Math.round(Math.random() * (max - min) + min);
    }
    

    3./index.html

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>Banner</title>
    		<link href="css/index.css" rel="stylesheet" />
    	</head>
    	<body>
    		<div class="bili-banner">
    			<div class="animated-banner">
    				<!-- 背景 -->
    				<div class="layer">
    					<img 
    						src="image/bg1.png" 
    						data-height="360" data-width="9666" height="180" width="4833" 
    						style="transform: scale(1) translate(0px, -15px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 左山 -->
    				<div class="layer">
    					<img 
    						src="image/bg2.png" 
    						data-height="360" data-width="9666" height="180" width="4833" 
    						style="transform: scale(1) translate(1100px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 右山 -->
    				<div class="layer">
    					<img 
    						src="image/bg3.png"
    						data-height="360" data-width="3523" height="162" width="1585"
    						style="transform: scale(1) translate(675px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 左桥 -->
    				<div class="layer">
    					<img 
    						src="image/bg4.png"
    						data-height="360" data-width="2938" height="176" width="1439"
    						style="transform: scale(1) translate(-637px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 右船 -->
    				<div class="layer">
    					<img 
    						src="image/bg5.png"
    						data-height="139" data-width="556" height="62" width="250"
    						style="transform: scale(1) translate(607.5px, 45px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 开船人物 -->
    				<div class="layer">
    					<img 
    						src="image/p4.png"
    						data-height="302" data-width="734" height="84" width="205"
    						style="transform: scale(1) translate(252px, 36.4px) rotate(0deg); opacity: 0;"
    					>
    				</div>
    				<!-- 中草坪+树 -->
    				<div class="layer">
    					<img 
    						src="image/bg6.png"
    						data-height="180" data-width="1757" height="125" width="1229"
    						style="transform: scale(1) translate(112px, 25px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 中草坪+风筝 -->
    				<div class="layer">
    					<img 
    						src="image/bg7.png"
    						data-height="116" data-width="1757" height="81" width="1229"
    						style="transform: scale(1) translate(-350px, 49px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 人物 -->
    				<div class="layer">
    					<img 
    						src="image/p2.png"
    						data-height="346" data-width="497" height="138" width="198"
    						style="transform: scale(1) translate(-240px, 16px) rotate(0deg); opacity: 0;"
    					>
    				</div>
    				<!-- 人物 -->
    				<div class="layer">
    					<img 
    						src="image/p1.png"
    						data-height="256" data-width="146" height="102" width="58"
    						style="transform: scale(1) translate(-340px, 32px) rotate(0deg); opacity: 0;"
    					>
    				</div>
    				<!-- 中树 -->
    				<div class="layer">
    					<img 
    						src="image/t1.png"
    						data-height="254" data-width="602" height="114" width="270"
    						style="transform: scale(1) translate(-90px, 13.5px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 中草坪+树 -->
    				<div class="layer">
    					<img 
    						src="image/bg8.png"
    						data-height="360" data-width="4277" height="180" width="2138"
    						style="transform: scale(1) translate(100px, 0px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 中人物 -->
    				<div class="layer">
    					<img 
    						src="image/p3.png"
    						data-height="327" data-width="933" height="147" width="419"
    						style="transform: scale(1) translate(216px, 13.5px) rotate(0deg); opacity: 1;"
    					>
    				</div>
    				<!-- 右树 -->
    				<div class="layer">
    					<img 
    						src="image/t3.png"
    						data-height="353" data-width="740" height="211" width="444"
    						style="transform: scale(1) translate(2100px, 0px) rotate(0deg); filter: blur(2px); opacity: 1;"
    					>
    				</div>
    				<!-- 左树 -->
    				<div class="layer">
    					<img 
    						src="image/t2.png"
    						data-height="360" data-width="1916" height="180" width="958"
    						style="transform: scale(1) translate(-1000px, 0px) rotate(0deg); filter: blur(1px); opacity: 1;"
    					>
    				</div>
    				<canvas width="1519" height="155" style="position: absolute; top: 0px; left: 0px;" id="canvas"></canvas>
    			</div>
    		</div>
    	</body>
    	<script src="js/index.js"></script>
    	<script src="js/canvas.js"></script>
    	<script>
    		let timer = null;
    		window.onload = ()=>{
    			loading();
    		}
    		function start(){
    			timer = setInterval(begin,1000/60)
    		}
    		function begin(){
    			ctx.clearRect(0,0,sw,sh);
    			createFlower();
    			allDraw();
    		}
    	</script>
    </html>
    

    五、结束

    至此就完成了Bilibili网站首图动态Banner开发实现

    本文演示视频:点击浏览

    更多前端内容欢迎关注公众号:天小天个人网


    起源地下载网 » Bilibili网站首图动态Banner开发实现

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元