一、技术思考
通过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. 具体实现思路
- 获取到所有.layer节点,从而更方便的获取img节点
- 监听Banner的mouseenter事件记录鼠标移入的clientX值
- 监听Banner的mousemove事件计算鼠标X方向移动距离并调用图片移动方法move
- 监听Banner的mouseleave事件清除监听并调用图片复位方法方法imgRest
- 至此就实现banner通过鼠标移动控制图片平移
- 具体的mova和imgRest方法其实就是改变对应图片的style中transform和opacity实现
- 其中涉及到一个工具函数GetTranslate来获取图片原始移动距离
- 注:没有做代码的精简方便大家理解
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. 具体实现思路
- 获取canva节点和getContext
- loading方法加载图片资源
- 定义花瓣类Flower
- 设置Flower的基本属性和渲染方法draw以及飘落方法updata
- createFlower创建指定数量花瓣
- allDraw方法渲染所有画面内花瓣
- 其中使用工具函数rand生成随机数
- 花瓣飘落出画面时会被清除
- 花瓣位置速度大小都是随机生成
- 在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开发实现
本文演示视频:点击浏览
更多前端内容欢迎关注公众号:天小天个人网
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!