既製商品

  • 同時にカウントダウン複数を制御します。
  • MTweenアニメーションフレームアニメーション制作の棚(ズーム+ジッタ)。
  • 有効期限を変更することができます。

アイデア:

1.レイアウト

2.デフォルトの有効期限を設定します

3.カウントダウン:開始時間とカウントダウンを変更する必要が後のページ更新。

        なぜなら、複数のタイマーを追加する必要があり、タイマーを定義し、この方法はli.timer李レコードの属性に使用することができるだけでなく、の李は、タイマタイマ番号を追加します。これを繰り返し、他のタイマliの数れることはありません

有効期限を変更します。4.

棚アニメーションの終了後5

  • 1.ディスプレイのマスク
  • 2.ジッタ:アニメーションフレームmTween()と結合シェイク()メソッド
  • 消失の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:55
    let 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:30
        return 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);
                    }
                });
            }
        });
    }

    //设置倒计时span
    let 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();//90162
                if(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]);
    }
};

 

公開された95元の記事 ウォンの賞賛115 ビュー120 000 +

おすすめ

転載: blog.csdn.net/qq_34569497/article/details/99551040