一、实现效果
二、实现功能
- 搜索框滑动固定,伸缩动画
- 菜单栏左右滑动,超出区域自动弹回
- banner自动轮播,手指左右滑动,滑动距离一半以内自动弹回,超过一半自动跳到下一张,圆点跟随显示颜色,以及实现相应的动画效果
- 京东秒杀倒计时
- 页面到达商品的位置显示返回顶部,点击后,速度由快到慢返回顶部
三、核心知识点
- 动画效果
transition
来实现,也可以封装一个animate函数 - 移动端的事件
touchstart、touchmove、touchend
的运用时机 classList
事件来获取包含某个class的元素。- 页面滚动事件
scoll
,把控距离 - 封装一个时间函数,倒计时用某个时间点去减。
- 返回顶部
backtop
,速度由快到慢,使用scrollTop+speed
来控制返回顶部速度的快慢。
四、不足之处
- 手机端兼容性做的不好,页面主要采用百分比来控制页面大小,测试界面采用iphone 12pro
- js函数有一些重复代码,有没有整理封装好
- 动画效果做的不够自然,都是凭想象力做的
附录:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<title>Document</title>
<link rel="stylesheet" href="./Mobile.css">
<link rel="stylesheet" href="./img/font-awesome-4.7.0/css/font-awesome.min.css">
<script src="./animate.js"></script>
<script src="./Mobile.js"></script>
</head>
<body>
<div class="header">
<div class="top">
<a href="" class="jdlogo"><img src="./img/icon/jd_logo.png" alt=""></a>
<div class="top-nav">
<ul>
<li class="top-nav-souye"><a href="">首页</a></li>
<li><a href="">附近</a></li>
</ul>
</div>
<a href="" class="message"><img src="./img/icon/message.png" alt=""></a>
<div class="topback">
</div>
</div>
<div class="search ">
<form action="">
<a href=""><i class="fa fa-search" aria-hidden="true"></i></a>
<input type="text" placeholder="能量手环">
<a href="" class="camera"><img src="./img/icon/camera.png" alt=""></a>
<a href="" class="scan"><img src="./img/icon/scan.png" alt=""></a>
</form>
</div>
<div class="nav">
<ul>
<li><a href="">推荐</a></li>
<li><a href="">数码</a></li>
<li><a href="">手机</a></li>
<li><a href="">电脑办公</a></li>
<li><a href="">生鲜</a></li>
<li><a href="">二手</a></li>
<li><a href="">医药健康</a></li>
<li><a href="">食品</a></li>
<li><a href="">男装</a></li>
<li><a href="">酒水饮料</a></li>
<li><a href="">女装</a></li>
<li><a href="">男鞋</a></li>
<li><a href="">家电</a></li>
</ul>
<div class="sort"><a href=""><i class="fa fa-list-ul"></i>分类</a></div>
</div>
</div>
<div class="main">
<div class="main-banner">
<ul>
<li>
<a href=""><img src="./img/banner/banner6.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner1.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner2.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner3.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner4.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner5.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner6.jpg" alt=""></a>
</li>
<li>
<a href=""><img src="./img/banner/banner1.jpg" alt=""></a>
</li>
</ul>
<ol>
<li class="current"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</div>
<div class="main-ad">
<a href=""><img src="./img/ad/ad_left.jpg" alt=""><img src="./img/ad/ad_centre.jpg" alt=""><img src="./img/ad/ad_right.jpg" alt=""></a>
</div>
<div class="main-nav">
<ul>
<li>
<a href=""><img src="./img/icon/icon1.jpg" alt=""><br>京东超市</a>
</li>
<li>
<a href=""><img src="./img/icon/icon2.jpg" alt=""><br>数码电器</a>
</li>
<li>
<a href=""><img src="./img/icon/icon3.jpg" alt=""><br>京东新百货</a>
</li>
<li>
<a href=""><img src="./img/icon/icon4.jpg" alt=""><br>京东新鲜</a>
</li>
<li>
<a href=""><img src="./img/icon/icon5.jpg" alt=""><br>京东到家</a>
</li>
<li>
<a href=""><img src="./img/icon/icon6.jpg" alt=""><br>充值缴费</a>
</li>
<li>
<a href=""><img src="./img/icon/icon7.jpg" alt=""><br>附近好店</a>
</li>
<li>
<a href=""><img src="./img/icon/icon8.jpg" alt=""><br>领券</a>
</li>
<li>
<a href=""><img src="./img/icon/icon9.jpg" alt=""><br>领金贴</a>
</li>
<li>
<a href=""><img src="./img/icon/icon10.jpg" alt=""><br>PLUS会员</a>
</li>
</ul>
</div>
<div class="main-seckill">
<div class="main-seckill-nav">
<span class="jdseckill">京东秒杀</span>
<div class="main-time">
<span>20点场</span>
<span class="main-hours"></span>
<span>:</span>
<span class="main-min"></span>
<span>:</span>
<span class="main-second"></span>
</div>
<div class="main-seckill-product">
<ul>
<li>
<a href=""><img src="./img/ad/ad_seckill1.jpg" alt="">¥5899</a>
</li>
<li>
<a href=""><img src="./img/ad/ad_seckill2.jpg" alt="">¥80</a>
</li>
<li>
<a href=""><img src="./img/ad/ad_seckill3.jpg" alt="">¥99.9</a>
</li>
<li>
<a href=""><img src="./img/ad/ad_seckill4.jpg" alt="">¥2739</a>
</li>
<li>
<a href=""><img src="./img/ad/ad_seckill5.jpg" alt="">¥52.9</a>
</li>
<li>
<a href=""><img src="./img/ad/ad_seckill6.jpg" alt="">¥1638</a>
</li>
</ul>
</div>
</div>
</div>
<div class="main-product">
<ul>
<li>
<a href="">
<img src="./img/prodcut/product1.jpg" alt=""> 【旗舰新品|以旧换新】海尔全自动波轮洗衣机...
<span>¥1698.00</span>
</a>
</li>
<li>
<a href=""><img src="./img/prodcut/product2.jpg" alt="">森 马 【同款特价】2022秋季新款英伦风高...
<span>¥69.00</span></a>
</li>
<li>
<a href=""><img src="./img/prodcut/product3.jpg" alt="">LG纤慧烘套装10kg蒸汽除菌洗衣机+9kg原...
<span>¥10999.00</span></a>
</li>
<li>
<a href=""><img src="./img/prodcut/product4.jpg" alt="">雷蛇 (Razer) 朋克键盘鼠标套装 机械 手感发...
<span>¥119.00</span></a>
</li>
<li>
<a href=""><img src="./img/prodcut/product5.jpg" alt="">海尔空调挂机变频空调客厅空调壁挂式 自清...
<span>¥2149.00</span></a>
</li>
<li>
<a href=""><img src="./img/prodcut/product6.jpg" alt="">雷蛇 (Razer) USB便携彩色硅胶可折叠弯卷...
<span>¥99.00</span></a>
</li>
<li>
<a href="">
<img src="./img/prodcut/product1.jpg" alt=""> 【旗舰新品|以旧换新】海尔全自动波轮洗衣机...
<span>¥1698.00</span>
</a>
</li>
<li>
<a href=""><img src="./img/prodcut/product2.jpg" alt="">森 马 【同款特价】2022秋季新款英伦风高...
<span>¥69.00</span></a>
</li>
</li>
<li>
<a href=""><img src="./img/prodcut/product5.jpg" alt="">海尔空调挂机变频空调客厅空调壁挂式 自清...
<span>¥2149.00</span></a>
</li>
<li>
<a href=""><img src="./img/prodcut/product6.jpg" alt="">雷蛇 (Razer) USB便携彩色硅胶可折叠弯卷...
<span>¥99.00</span></a>
</li>
</ul>
</div>
<div class="main-backtop">
<a href="javascript:;"><img src="./img/icon/backtop.png" alt=""></a>
</div>
</div>
<div class="bottom">
<div class="bottom-nav">
<ul>
<li>
<a href="" class="bottom-home">
<i class="fa fa-home fa-2x" aria-hidden="true"></i><br>首页
</a>
</li>
<li>
<a href="" class="">
<i class="fa fa-th-large fa-2x"></i><br> 分类
</a>
</li>
<li>
<a href="">
<i class="fa fa-codepen fa-2x"></i><br> 发现
</a>
</li>
<li>
<a href="">
<i class="fa fa-shopping-cart fa-2x" aria-hidden="true"></i><br>购物车
</a>
</li>
<li>
<a href=""><i class="fa fa-user fa-2x" aria-hidden="true"></i><br> 我的 </a>
</li>
</ul>
</div>
</div>
</body>
</html>
CSS:
* {
margin: 0 auto;
padding: 0 auto;
}
ul li {
list-style: none;
}
a {
text-decoration: none;
font-family: ”黑体”;
}
body {
background-color: #EB4B35;
}
/* top 头部 */
.header .top {
text-align: center;
position: relative;
margin-top: 2rem;
width: 100%;
height: 2rem;
}
.header .top .jdlogo img {
width: 3rem;
height: 3rem;
position: fixed;
left: 0.6rem;
top: 1.4rem;
z-index: 999;
}
.message img {
position: fixed;
right: 1rem;
top: 2.2rem;
width: 1.5rem;
height: 1.5rem;
z-index: 999;
}
.header .top .top-nav {
height: 2rem;
width: 8rem;
border-radius: 1.5rem;
background-color: #EB4B35;
padding: 0;
}
.header .top .top-nav .top-nav-souye {
border: 0.1rem solid #EB4B35;
border-radius: 1.5rem;
width: 4rem;
height: 1.8rem;
background-color: whitesmoke;
}
.header .top .top-nav .top-nav-souye a {
color: #EB4B35;
}
.header a {
color: white;
}
.header .top ul {
margin: 0;
padding: 0;
}
.header .top ul li {
display: inline-block;
/* margin-left: 1rem; */
width: 3.5rem;
line-height: 1.8rem;
}
.topback {
width: 100%;
height: 4.2rem;
background-color: #EB4B35;
position: fixed;
top: 0;
left: 0;
z-index: 997;
display: none;
}
.search {
margin-top: 1rem;
text-align: center;
position: relative;
/* border: 1px solid black; */
width: 92%;
height: 1.8rem;
border-radius: 1.2rem;
padding: 0;
background-color: white;
transition: .2s all;
/* cubic-bezier(.46, 1, .23, 1.52); */
}
.searchwidth {
width: 70% !important;
/* position: fixed; */
/* top: 1rem !important; */
/* left: 5% !important; */
z-index: 999;
/* transition: 0.3s all; */
}
.search input {
width: 72%;
height: 1.4rem;
/* border-radius: 1rem; */
/* padding: 0 0.4rem; */
outline: none;
border: 0.1rem solid white;
}
.search .fa-search {
position: absolute;
left: 0.8rem;
top: 0.4rem;
color: red;
}
.search .camera img {
width: 1.2rem;
height: 1.2rem;
position: absolute;
right: 2.2rem;
top: 0.4rem;
border-right: 1px solid #f5f6f7d8;
padding-right: 0.4rem;
}
.search .scan img {
width: 1.2rem;
height: 1.2rem;
position: absolute;
right: 0.6rem;
top: 0.4rem;
}
.nav {
position: relative;
width: 24.2rem;
height: 2rem;
/* border: 1px solid black; */
overflow: hidden;
}
.nav ul li {
display: inline-block;
margin-left: 0.8rem;
line-height: 2rem;
}
.nav ul {
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 2rem;
/* background-color: pink; */
padding: 0;
margin: 0;
/* display: inline-block!important;
display: inline; */
}
.nav .sort {
position: absolute;
top: 0;
right: 0;
width: 4.5rem;
height: 2rem;
background-color: #EB4B35;
text-align: center;
line-height: 2rem;
}
.nav .sort i {
margin-right: 0.2rem;
}
/* main 主体 */
.main {
position: relative;
top: 0.2rem;
width: 100%;
height: 100%;
display: inline-block !important;
background-color: white;
padding: 0;
margin-top: 0.2rem;
border-radius: 0.8rem 0.8rem 0 0;
/* text-align: center; */
}
/* banner */
.main .main-banner {
position: relative;
width: 90%;
margin: 1rem 1rem;
border-radius: 0.8rem;
padding: 0;
overflow: hidden;
height: 8.7rem;
}
.main .main-banner ul {
position: absolute;
left: -351px;
top: 0;
padding: 0;
width: 800%;
height: 8.7rem;
}
.main .main-banner ul li {
width: 12.5%;
height: 100%;
/* display: inline-block; */
float: left;
padding: 0;
margin: 0;
}
.main .main-banner img {
width: 100%;
}
.main .main-banner ol {
position: absolute;
left: 40%;
bottom: -0.5rem;
width: 40%;
height: 2rem;
padding: 0;
margin: 0;
}
.main .main-banner ol li {
width: 0.3rem;
height: 0.3rem;
border-radius: 50%;
background-color: #e1e1e1;
z-index: 999;
list-style: none;
margin: 0 0.2rem;
display: inline-block;
transition: all .3s;
transition: all .3s;
}
.current {
width: 0.5rem!important;
border-radius: 0.1rem !important;
background-color: red!important;
}
/* ad */
.main .main-ad {
width: 100%;
}
.main .main-ad img {
width: 33%;
}
/* main-nav */
.main .main-nav {
width: 99%;
height: 10rem;
background-color: #fff;
}
.main .main-nav ul li {
float: left;
width: 4.8rem;
height: 5rem;
text-align: center;
}
.main .main-nav ul li a {
font-size: 4px;
}
.main .main-nav ul {
padding: 0;
margin: 0;
}
.main .main-nav img {
width: 45%;
height: 45%;
/* margin: 0 1rem; */
}
/*bottom 底部 */
.bottom {
width: 101%;
height: 6rem;
position: fixed;
bottom: 0;
background-color: #fff;
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 5%);
}
.bottom .bottom-nav ul {
margin: 0;
padding: 0;
text-align: center;
}
.bottom .bottom-nav ul li {
float: left;
width: 4.8rem;
height: 3rem;
margin-top: 0.6rem;
}
.bottom .bottom-nav ul li i {
color: #808080;
}
.bottom .bottom-nav .bottom-home {
color: #EB4B35;
}
.bottom .bottom-nav .fa-home {
color: #EB4B35;
}
/* seckill */
.main .main-seckill {
width: 100%;
height: 9rem;
background-color: #f6f6f6;
padding-top: 0.4rem;
position: relative;
}
.main .main-seckill .main-time {
position: absolute;
left: 95px;
top: 10px;
font-size: 12px;
}
.main .main-seckill .main-time .main-hours {
background-color: red;
color: white;
border-radius: 0.2rem;
padding: 0.1rem;
}
.main .main-seckill .main-time span {
color: red;
}
.main .main-seckill .main-time .main-min {
background-color: red;
color: white;
border-radius: 0.2rem;
padding: 0.1rem;
}
.main .main-seckill .main-time .main-second {
background-color: red;
color: white;
border-radius: 0.2rem;
padding: 0.1rem;
}
.main .main-seckill .main-seckill-nav {
width: 94%;
height: 8rem;
background-color: #fff;
border-radius: 1rem;
}
.main .main-seckill .jdseckill {
font-size: 16px;
font-weight: 600;
margin: 10px;
}
.main .main-seckill .main-seckill-product {
margin-top: 20px;
}
.main .main-seckill .main-seckill-product ul {
width: 100%;
padding: 0;
margin-left: 0.4rem;
}
.main .main-seckill .main-seckill-product ul li {
float: left;
width: 16%;
text-align: center;
}
.main .main-seckill .main-seckill-product ul li a {
color: red;
font-size: 8px;
font-weight: 600;
}
.main .main-seckill .main-seckill-product img {
width: 80%;
}
/* product */
.main .main-product {
width: 100%;
height: 100rem;
background-color: #f6f6f6
}
.main .main-product ul {
padding: 0;
}
.main .main-product ul li {
float: left;
width: 45%;
height: 16rem;
border-radius: 0.6rem;
margin: 5px 9px;
overflow: hidden;
background-color: #fff;
}
.main .main-product ul li img {
width: 100%;
height: 60%;
}
.main .main-product ul li span {
color: red;
font-size: 22px;
margin-top: 16px;
}
/* backtop */
.main .main-backtop {
position: fixed;
right: 1rem;
bottom: 7rem;
width: 3rem;
height: 3rem;
background-color: white;
border-radius: 50%;
text-align: center;
display: none;
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 5%);
transition: all 3s;
}
.main .main-backtop img {
width: 2.4rem;
height: 2.4rem;
margin-top: 0.3rem;
}
JS:
这里可以不用使用这个函数,可以用transition来实现动画
animate.js
//动画函数
//obj 动画绑定对象 traget 目标移动距离 callback回调函数
function animate(obj, target, callback) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
//步长 = (目标值-现在位置)/10 取整
let step = (target - obj.offsetLeft) / 10;
//大于0向上取整 小于0向下取整
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
//停止动画定时器
clearInterval(obj.timer);
if (callback) {
callback();
}
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 15)
}
Mobile.js:
window.addEventListener('load', function() {
let nav = document.querySelector('.nav');
let nav_ul = document.querySelector('.nav').querySelector('ul')
let sort = document.querySelector('.sort')
let main_banner = document.querySelector('.main-banner');
let main_ul = main_banner.children[0];
let main_ol = main_banner.children[1];
let main_hours = document.querySelector('.main-hours');
let main_min = document.querySelector('.main-min');
let main_second = document.querySelector('.main-second');
//菜单栏滑块
let startX = 0
//盒子初始位置
let x = 0;
nav_ul.addEventListener('touchstart', function(e) {
startX = e.targetTouches[0].pageX;
x = this.offsetLeft;
})
nav_ul.addEventListener('touchmove', function(e) {
let moveX = e.targetTouches[0].pageX - startX;
this.style.left = x + moveX + 'px';
})
nav_ul.addEventListener('touchend', function() {
let moveright = this.offsetWidth - nav.offsetWidth + sort.offsetWidth;
let moveleft = this.offsetLeft;
if (moveleft >= 0) {
animate(nav_ul, 0);
}
if (-moveleft >= moveright) {
animate(nav_ul, -moveright);
}
})
//banner滑动
let index = 0;
let bannerWidth = main_banner.offsetWidth;
//定时器
let main_banner_timer = setInterval(banner_timer, 2000);
function banner_timer() {
index++;
main_ul.style.transform = 'translateX(' + -bannerWidth * index + 'px)';
main_ul.style.transition = 'all .3s';
}
//过渡动画完成后执行
main_ul.addEventListener('transitionend', function() {
if (index >= main_ul.children.length - 2) {
index = 0;
//去掉过渡 继续播放banner
main_ul.style.transition = 'none';
main_ul.style.transform = 'translateX(' + -bannerWidth * index + 'px)';
} else if (index < 0) {
index = main_ul.children.length - 3;
main_ul.style.transition = 'none';
main_ul.style.transform = 'translateX(' + -bannerWidth * index + 'px)';
}
main_ol.querySelector('.current').classList.remove('current');
main_ol.children[index].classList.add('current');
})
//手指拖动banner
let banner_startX = 0;
let banner_moveX = 0;
let flag = false;
main_ul.addEventListener('touchstart', function(e) {
//手指按下的初始坐标X
banner_startX = e.targetTouches[0].pageX;
clearInterval(main_banner_timer);
main_banner_timer = null;
})
main_ul.addEventListener('touchmove', function(e) {
//移动距离
banner_moveX = e.targetTouches[0].pageX - banner_startX;
//移动盒子
let translateX = -index * bannerWidth + banner_moveX;
main_ul.style.transition = 'none';
main_ul.style.transform = 'translateX(' + translateX + 'px)';
clearInterval(main_banner_timer);
main_banner_timer = null;
flag = true;
// 阻止页面的默认滚动行为
e.preventDefault();
})
main_ul.addEventListener('touchend', function() {
if (flag) {
if (Math.abs(banner_moveX) > bannerWidth / 2) {
if (banner_moveX > 0) {
index--;
} else {
index++;
}
main_ul.style.transition = 'all .3s';
main_ul.style.transform = 'translateX(' + -bannerWidth * index + 'px)';
main_banner_timer = setInterval(banner_timer, 2000);
} else {
main_ul.style.transition = 'all .3s';
main_ul.style.transform = 'translateX(' + -bannerWidth * index + 'px)';
main_banner_timer = setInterval(banner_timer, 2000);
}
}
})
// 京东秒杀倒计时
getNowTime();
let seckilltimer = setInterval(getNowTime, 1000) //刷新时间
// 封装时间函数
function getNowTime() {
let date = new Date();
//年 getFullYear():四位数字返回年份
let year = date.getFullYear(); //getFullYear()代替getYear()
//月 getMonth():0 ~ 11
let month = date.getMonth() + 1;
//日 getDate():(1 ~ 31)
let day = date.getDate();
//时 getHours():(0 ~ 23)
let hour = date.getHours();
//分 getMinutes(): (0 ~ 59)
let minute = date.getMinutes();
//秒 getSeconds():(0 ~ 59)
let second = date.getSeconds();
let seckillhours = addZero(hour);
let seckillminute = addZero(minute);
let seckillsecond = addZero(second);
let remainseckillhours = 20 - seckillhours;
let remainseckillminute = 60 - addZero(minute);
let remainseckillsecond = 60 - addZero(second);
main_hours.innerHTML = addZero(remainseckillhours);
main_min.innerHTML = addZero(remainseckillminute);
main_second.innerHTML = addZero(remainseckillsecond);
}
function addZero(s) {
return s < 10 ? ('0' + s) : s;
}
// 向上下滑动做搜索框伸缩效果
// backtop返回顶部效果
let search = document.querySelector('.search')
let topback = document.querySelector('.topback')
let backtop = document.querySelector('.main-backtop');
let main_product = document.querySelector('.main-product')
window.addEventListener('scroll', function() {
// 页面滚动距离
let moveTop = window.pageYOffset;
// nav高度
let navheight = nav.offsetHeight;
// search伸缩效果
if (moveTop > navheight) {
if (moveTop >= 48) {
search.style.position = 'fixed';
search.style.left = '15%';
search.style.top = '1rem';
}
search.classList.add('searchwidth');
topback.style.display = 'block';
} else {
search.style.position = '';
search.style.left = '';
search.style.top = '';
search.classList.remove('searchwidth');
topback.style.display = 'none';
}
// backtop
let main_product_top = document.documentElement.scrollTop;
if (moveTop >= 610) {
backtop.style.display = 'block';
} else {
backtop.style.display = 'none';
}
})
backtop.addEventListener('click', function() {
// window.scroll(0, 0);
goTop();
})
// 缓慢向上返回顶部动画
function goTop() {
// 由快到慢 (每次开启定时器都重新计算速度)
let goToptimer = setInterval(function() {
//获取滚动条的滚动高度
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
//用于设置速度差,产生缓动的效果
var speed = Math.floor(-scrollTop / 8);
document.documentElement.scrollTop = document.body.scrollTop = scrollTop + speed; //用纯数字赋值
// isTop = true; //用于阻止滚动事件清除定时器
if (scrollTop == 0) {
clearInterval(goToptimer);
}
}, 50);
}
})
*如果需要原程序包可以去下载 *