当前位置: 代码迷 >> 综合 >> 商品下架
  详细解决方案

商品下架

热度:86   发布时间:2023-10-01 01:16:06.0
  • 控制多个倒计时同时进行;
  • 使用动画框架mTween制作下架动画(缩放+抖动);
  • 可修改到期时间;

思路:

1. 布局

2. 设置默认到期时间

3. 倒计时:页面一开始刷新和修改时间后都需要进行倒计时。

        因为需要给多个li加定时器,不能只定义一个timer,可以使用li.timer给li加个timer属性的方法记录定时器编号;这样就不会和其他li的定时器编号重复

4. 修改到期时间

5. 到期之后,下架动画

  • 1. 显示遮罩
  • 2. 抖动:使用动画框架mTween()和shake()方法结合
  • 3. 下落消失
  • 4. 生成内容

HTML:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><link href="css/index.css" rel="stylesheet" />
</head>
<body>
<h1 id="logo"><img src="img/miaov.png" />
</h1> 
<div class="wrap"><ul class="shopList"><li><header><input type="datetime-local" class="datetime" /><a class="btn">确定</a></header><p class="remainingTime">剩余<time><span>0</span><span>0</span>:<span>0</span><span>0</span>:<span>0</span><span>0</span></time></p><div class="shop-img"><img src="img/shop0.jpg" /></div><h2 class="shop-title">Apple iPhone 7 Plus 64g</h2><p class="shop-price">抢购价:<span>¥5799</span></p><div class="over"></div></li><li><header><input type="datetime-local" class="datetime" /><a class="btn">确定</a></header><p class="remainingTime">剩余<time><span>0</span><span>0</span>:<span>0</span><span>0</span>:<span>0</span><span>0</span></time></p><div class="shop-img"><img src="img/shop1.jpg" /></div><h2 class="shop-title">27 英寸配备 Retina 5K显示屏</h2><p class="shop-price">抢购价:<span>¥15999</span></p><div class="over"></div></li><li><header><input type="datetime-local" class="datetime" /><a class="btn">确定</a></header><p class="remainingTime">剩余<time><span>0</span><span>0</span>:<span>0</span><span>0</span>:<span>0</span><span>0</span></time></p><div class="shop-img"><img src="img/shop2.jpg" /></div><h2 class="shop-title">iPad mini 4</h2><p class="shop-price">抢购价:<span>?1799</span></p><div class="over"></div></li><li><header><input type="datetime-local" class="datetime" /><a class="btn">确定</a></header><p class="remainingTime">剩余<time><span>0</span><span>0</span>:<span>0</span><span>0</span>:<span>0</span><span>0</span></time></p><div class="shop-img"><img src="img/shop3.jpg" /></div><h2 class="shop-title">Apple Watch</h2><p class="shop-price">抢购价:<span>¥3799</span></p><div class="over"></div></li></ul><section class="overList"><header><h3>商品名称</h3><h3>价格</h3></header><ul class="list"><!-- <li><p>Apple iPhone 7 Plus 64g </p><p>?5799</p><div class="shop-img"><img src="img/shop0.jpg" /></div></li>--></ul></section>
</div>
<script src="js/mTween.js"></script>
<script>
/*1. 布局2. 设置默认到期时间3. 倒计时:页面一开始刷新和修改时间后都需要进行倒计时。因为需要给多个li加定时器,不能只定义一个timer,可以使用li.timer给li加个timer属性的方法记录定时器编号;这样就不会和其他li的定时器编号重复4. 修改到期时间5. 到期之后,下架动画1. 显示遮罩2. 抖动:使用动画框架mTween()和shake()方法结合3. 下落消失4. 生成内容
*/   
{let wrap = document.querySelector(".wrap");let shopList = wrap.querySelector(".shopList");let overList = wrap.querySelector(".overList");let lis = shopList.querySelectorAll("li");let list = wrap.querySelector(".list");//时间补零操作let fillZero = (date) =>{return date<10?"0"+date:""+date;}//生成datetime-local的时间格式:2014-06-01T10:55let formatToDatetime =(time)=>{//获取默认时间的年月日let date = new Date(time);//Tue Aug 06 2019 15:48:18 GMT+0800 (中国标准时间)let year = date.getFullYear();let mon = fillZero(date.getMonth() + 1);let nowDate = fillZero(date.getDate());let hour = fillZero(date.getHours());let minute = fillZero(date.getMinutes());//将时间设置到默认时间区域 2014-06-01T10:55//注意:datetime-local的时间格式为:2017-06-01T08:30return year+'-'+mon+'-'+nowDate+'T'+hour+':'+minute;}//商品下架let fullDownGoods = (li)=>{let over = li.querySelector(".over");over.style.display = "none";li.style.opacity = "0";let shopPrice = li.querySelector(".shop-price span").innerHTML;let shopTitle = li.querySelector(".shop-title").innerHTML;list.innerHTML += `<li><p>${shopTitle}</p><p>${shopPrice}</p><div class="shop-img"><img src="img/shop0.jpg" /></div></li>`;}//设置商品下架抖动动画let fullDownAnimation = (li)=>{let over = li.querySelector(".over");over.style.display = "block";//要设置transform相关属性,必须先手动获取css(over,"translateX",0);css(over,"scale",1);//注意mTween()和shake()方法返回的不是同一个Promise对象,所以不能通过异步执行实现mTween({el:over,attr:{scale:1.1},duration:10,fx:'backIn',cb:function(){shake({el:over,attr:'translateX',shakeLength:20,//在所有动画执行成功后,下架动画cb:function(){// 下架:并添加到下架商品处fullDownGoods(li);}});}});}//设置倒计时spanlet countDown = (li,time)=>{let overTime = time - Date.now();let timeSpan = li.querySelector(".remainingTime time");//把剩余的时间转成时分秒let h = 60*60*1000;let m = 60*1000;let s = 1000;//注意:取模是用总时间戳%对应时间的毫秒数,不足的需要补零let hour = fillZero(parseInt(overTime/h)).split("");let min = fillZero(parseInt(overTime%h/m)).split("");let sec = fillZero(parseInt(overTime%m/s)).split("");//时间的小时数可能不设置的很大,会超过4位,因此需要获取到各个时间,根据位数动态设置span个数hourSpan = "<span>"+hour.join("</span><span>")+"</span>:";minSpan = "<span>"+min.join("</span><span>")+"</span>:";secSpan = "<span>"+sec.join("</span><span>")+"</span>";timeSpan.innerHTML = hourSpan+minSpan+secSpan;}//设置剩余时间let setOverTime = (li,time)=>{//第一次先显示时间,如果已经有定时器先清空定时器countDown(li,time);clearInterval(li.timer);//设置定时器:如果剩余时间小于等于0就直接下架商品,如果大于0继续倒计时li.timer = setInterval(()=>{let overTime = time - Date.now();//注意剩余时间需要在定时器里面计算,否则只会计算一次if(overTime<=0){clearInterval(li.timer);//如果到时间了直接执行下架动画fullDownAnimation(li);}else{countDown(li,time);}},1000);}//设置默认时间let setDefaultTime =()=>{//有多个不同的li所以需要循环设置lis.forEach((item,index)=>{//默认设置10分钟以内的随机事件// Math.random()+1让倒计时至少有1分钟 再*就至少有5分钟mins = Math.round(Math.random()*10) * 60 * 1000;//计算出默认倒计时的时间毫秒数        let defaultTime = Date.now() + mins;formatToDatetime(defaultTime);//设置默认倒计时时间let datetime = item.querySelector(".datetime");datetime.value = formatToDatetime(defaultTime);//页面刷新时就需要设置剩余时间setOverTime(lis[index],defaultTime);});};setDefaultTime();//修改到期时间let updateTime = () =>{lis.forEach((item,index)=>{let btn = item.querySelector(".btn");let datetime = item.querySelector(".datetime");btn.onclick = () =>{//获取设置的时间的时间戳let updateTime = new Date(datetime.value);//2019-08-06T16:42  //将设置的时间戳减去当前时间的时间戳,即为倒计时需要的时间。如果时间戳小于0就代表设置的时间在当前时间以前,就不能进行设置overTime = updateTime.getTime() - Date.now();//90162if(overTime<0){alert("设置时间必须在现在以后");return;}//设置过期时间,lis[index]传入的是具体的某个li setOverTime(lis[index],updateTime.getTime());};});}updateTime();
}</script>
</body>
</html>

CSS:

body {margin: 0;background:#e15671 url("../img/bg.png") center center no-repeat;background-size: 100% 100%;min-height: 100vh;overflow: auto;
}
img {vertical-align: top;
}
h2,
h3,
p {margin: 0;
}
ul {margin: 0;padding: 0;list-style: none;
}
input,
a {outline: none;
}
#logo {padding: 55px 0;text-align: center;
}
.wrap {margin: 0 auto;width: 990px;overflow: hidden;
}
.shopList {margin: 0 -5px;position: relative;height: 400px;
}
.shopList li {position: relative;float: left;margin: 0 5px;width: 240px;height: 400px;background: #fff;
}
.shopList header {padding: 8px;height: 24px;background: #191919;
}
.shopList .datetime {float: left;width: 170px;height: 24px;background: none;border: none;color: #e15671;
}
.btn {float: right;padding: 0 13px;font: 12px/22px "宋体";color: #e15671;border: 1px solid #e15671;border-radius: 3px;
}
.remainingTime {margin-top: 30px;font: 14px/28px "宋体";text-align: center;
}
.remainingTime time {display: inline-block;padding-left: 10px;font-size: 20px;vertical-align: top;color: #e15671;
}
.remainingTime span {display: inline-block;width: 20px;margin: 0 2px;color: #fff;background: #e15671;text-align: center;perspective-origin: center center;
}
.shop-img {display: flex;height: 194px;
}
.shop-img img {margin: auto;max-width: 100%;max-height: 100%;
}
.shop-title {margin-top: 10px;font: bold 16px/24px Arial,"微软雅黑";color: #000;text-align: center;
}
.shop-price {font: 14px/46px "宋体";text-align: center;color: #8f8f8f;
}
.shop-price span {font-size: 18px;color: #e15671;
}
.shopList .over {display: none;position: absolute;left: 0;top: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, .8)url("../img/over.png") center center no-repeat;
}
.overList {margin-top: 10px;
}
.overList header {height: 40px;background: #191919;
}
.overList header h3 {float: left;font: 14px/40px "宋体";color: #fff;
}
.overList .list li {padding: 10px 0;height: 60px;background: #fff;
}
.overList .list li:nth-child(2n) {background: #f6f5f6;
}
.overList .list p {float: left;font: bold 16px/60px "宋体";
}
.overList h3:nth-child(1),
.overList .list p:nth-child(1) {width: 450px;text-indent: 20px;
}
.overList h3:nth-child(2),
.overList .list p:nth-child(2) {width: 100px;text-align: center;
}
.overList .list p:nth-child(2) {color: #e15671;
}
.overList .shop-img {float: right;margin-right: 60px;width: 50px;height: 50px;padding: 4px;border: 1px solid #bfbfbf;background: #fff;
}

动画框架:mTween

var Tween = {linear: function (t, b, c, d){  //匀速return c*t/d + b;},easeIn: function(t, b, c, d){  //加速曲线return c*(t/=d)*t + b;},easeOut: function(t, b, c, d){  //减速曲线return -c *(t/=d)*(t-2) + b;},easeBoth: function(t, b, c, d){  //加速减速曲线if ((t/=d/2) < 1) {return c/2*t*t + b;}return -c/2 * ((--t)*(t-2) - 1) + b;},easeInStrong: function(t, b, c, d){  //加加速曲线return c*(t/=d)*t*t*t + b;},easeOutStrong: function(t, b, c, d){  //减减速曲线return -c * ((t=t/d-1)*t*t*t - 1) + b;},easeBothStrong: function(t, b, c, d){  //加加速减减速曲线if ((t/=d/2) < 1) {return c/2*t*t*t*t + b;}return -c/2 * ((t-=2)*t*t*t - 2) + b;},elasticIn: function(t, b, c, d, a, p){  //正弦衰减曲线(弹动渐入)if (t === 0) { return b; }if ( (t /= d) == 1 ) {return b+c; }if (!p) {p=d*0.3; }if (!a || a < Math.abs(c)) {a = c; var s = p/4;} else {var s = p/(2*Math.PI) * Math.asin (c/a);}return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;},elasticOut: function(t, b, c, d, a, p){    //*正弦增强曲线(弹动渐出)if (t === 0) {return b;}if ( (t /= d) == 1 ) {return b+c;}if (!p) {p=d*0.3;}if (!a || a < Math.abs(c)) {a = c;var s = p / 4;} else {var s = p/(2*Math.PI) * Math.asin (c/a);}return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;},    elasticBoth: function(t, b, c, d, a, p){ if (t === 0) {return b;}if ( (t /= d/2) == 2 ) {return b+c;}if (!p) {p = d*(0.3*1.5);}if ( !a || a < Math.abs(c) ) {a = c; var s = p/4;}else {var s = p/(2*Math.PI) * Math.asin (c/a);}if (t < 1) {return - 0.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;}return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;},backIn: function(t, b, c, d, s){     //回退加速(回退渐入)if (typeof s == 'undefined') {s = 1.70158;}return c*(t/=d)*t*((s+1)*t - s) + b;},backOut: function(t, b, c, d, s){if (typeof s == 'undefined') {s = 1.70158;  //回缩的距离}return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;}, backBoth: function(t, b, c, d, s){if (typeof s == 'undefined') {s = 1.70158; }if ((t /= d/2 ) < 1) {return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;}return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;},bounceIn: function(t, b, c, d){    //弹球减振(弹球渐出)return c - Tween['bounceOut'](d-t, 0, c, d) + b;},       bounceOut: function(t, b, c, d){//*if ((t/=d) < (1/2.75)) {return c*(7.5625*t*t) + b;} else if (t < (2/2.75)) {return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;} else if (t < (2.5/2.75)) {return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;}return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;},      bounceBoth: function(t, b, c, d){if (t < d/2) {return Tween['bounceIn'](t*2, 0, c, d) * 0.5 + b;}return Tween['bounceOut'](t*2-d, 0, c, d) * 0.5 + c*0.5 + b;}
};
(function(){if(!window.requestAnimationFrame){var lastTime = 0;window.requestAnimationFrame = function(callback){var nowTime = Date.now();var dely = Math.max(0,16.7 - (nowTime - lastTime));lastTime = nowTime;return setTimeout(callback,dely);};window.cancelAnimationFrame = function(index){clearTimeout(index);};}
})();
var transformAttr = ["rotate","rotateX","rotateY","rotateZ","translateX","translateY","translateZ","scale","scaleX","scaleY","skewX","skewY"
]; 
var normalAttr = ["width","height","left","top","right","bottom","marginBottom","marginleft","marginRight","marginTop","paddingLeft","paddingRight","paddingTop","paddingBottom"
];
function css(el,attr,val){if(typeof attr == "object"){for(var s in attr){css(el,s,attr[s]);}return ;}if(transformAttr.indexOf(attr) >= 0){return setTransform(el,attr,val);}if(val === undefined){val = getComputedStyle(el)[attr]; return normalAttr.indexOf(attr)>=0||!isNaN(val)?parseFloat(val):val;} else {if(attr == "opacity"){el.style[attr] = val;el.style.filter = "alpha(opacity="+(val*100)+")";} else if(normalAttr.indexOf(attr)>=0) {el.style[attr] = val + "px";} else if(attr == "zIndex") {el.style[attr] = Math.round(val);} else {el.style[attr] = val;}}
}
function setTransform(el,attr,val){el.transform = el.transform||{};if(val === undefined){return  el.transform[attr];}el.transform[attr] = val;var transformVal = "";for(var s in  el.transform){switch(s){case "rotate":case "rotateX":case "rotateY":case "rotateZ":case "skewX":case "skewY":transformVal += s+'('+ el.transform[s]+'deg) ';break;case "translateX":case "translateY":case "translateZ":transformVal += s+'('+ el.transform[s]+'px) ';break;case "scale":case "scaleX":case "scaleY":transformVal += s+'('+ el.transform[s]+') ';break;       }}el.style.WebkitTransform = el.style.transform = transformVal.trim();
}
function mTween(op){return new Promise((resolve,reject)=>{var el = op.el,attr = op.attr,fx = op.fx||"easeOut",duration = op.duration||400,maxC = 0;if(el.animationTimer){return;}var t = 0;var b = {};var c = {};for(var s in attr){b[s] = css(el,s);c[s] = attr[s] - b[s];maxC = Math.max(maxC,Math.abs(c[s]));}if(typeof duration === "object"){var durationOption = duration;durationOption.multiple =  durationOption.multiple||2;duration = maxC * duration.multiple;duration =  durationOption.max?Math.min(duration,durationOption.max):duration;duration =  durationOption.min?Math.max(duration,durationOption.min):duration;}var d = Math.ceil(duration/(1000/60));move();function move(){el.animationTimer = requestAnimationFrame(function(){t++;if(t > d){el.animationTimer = null;//虽然返回Promise对象,也让其可以执行回调,使得同时使用mTween()和shake()方法时,可回调op.cb&&op.cb();resolve();} else {for(var s in attr){var val = Tween[fx](t,b[s],c[s],d);css(el,s,val);}move();}});}});
}
mTween.stop = function(el){cancelAnimationFrame(el.animationTimer);el.animationTimer = null;
};
function shake(op){return new Promise((resolve,reject)=>{var el = op.el,attr = op.attr,shakeLength = op.shakeLength||15,shakeArr = [];el.shakeStart = {};if(el.shake) {return ;} if(typeof attr === "object" ){for(var i = 0; i < attr.length; i++){el.shakeStart[attr[i]] = css(el,attr[i]);}} else {el.shakeStart[attr] = css(el,attr);}for(var i = shakeLength; i >= 0; i--){shakeArr.push(i%2?i:-i);}move();function move(){el.shake = requestAnimationFrame(function(){if(shakeArr.length <= 0){el.shake = false;op.cb&&op.cb();resolve();} else {var nub = shakeArr.shift();for(var s in  el.shakeStart){css(el,s, el.shakeStart[s] + nub);}move();}});}});
}    
shake.stop = function(el){cancelAnimationFrame(el.shake);el.shake = false;for(var s in  el.shakeStart){css(el,s, el.shakeStart[s]);}
};

 

  相关解决方案