Web前端—(原生JS)购物车效果

购物车效果

在这里插入图片描述

先准备好原始数据和素材
在下面数据的基础上,编写index.js
在这里插入图片描述

分析数据

  • 编写程序要从数据入手,从数据到界面最后到事件
  • 在分析数据的过程中,要分析数据是通过属性出现还是通过方法出现

单件商品的数据

  • 我们观察data.js中的数据:商品数组goods

    • pic:图片
    • title:标题
    • desc:描述
    • sellNumber:月售
    • favorRate:好评率
    • price:价格
  • 为了避免改变原始数据。我们创建一个单件商品的数据的类(class)
    在这里插入图片描述

  • 代码如下:
// 单件商品的数据
class UIGoods{
    constructor(g){
        this.data = g;
        this.choose = 0; // 每件商品被选中的数量
    }
    // 获取商品的总价
    getTotalPrice(){
        return this.data.price * this.choose;
    }
    // 是否选中此商品
    isChoose(){
        return this.choose > 0;
    }
    // 商品选择数量+1
    increase(){
        this.choose++;
    }
    // 商品选择数量-1
    decrease(){
        this.choose--;
    }

}
  • 对这个类进行测试 var uig = new UIGoods(goods[0]);
  • 测试成功进行下一步在这里插入图片描述

整个界面的数据

分析整个页面需要用到的数据,创建UIData类,并进行测试(大家可以自行在控制台进行测试)
在这里插入图片描述

  • 代码如下
// 整个界面的数据
class UIData{
    constructor(){
        var uiGoods = [];
        for(let i=0; i<goods.length; i++){
            let uig = new UIGoods(goods[i]);
            uiGoods.push(uig);
        }
        this.uiGoods = uiGoods;
        this.deliveryThreshold = 30; //起送费
        this.deliverPrice = 5; //配送费
    }

    // 获取总价
    getTotalPrice(){
        var sum = 0;
        for(let i=0; i<this.uiGoods.length; i++){
            let g = this.uiGoods[i];
            sum += g.getTotalPrice();
        }
        return sum;
    }

    // 增加某件商品的数量
    increase(index){
        this.uiGoods[index].increase();
    }
    // 减少某件商品的数量
    decrease(index){
        this.uiGoods[index].decrease();
    }
    // 得到总共的选中数量
    getTotalChooseNumber(){
        var sum = 0;
        for(let i=0; i<this.uiGoods.length; i++){
            sum += this.uiGoods[i].choose;
        }
        return sum;
    }
    // 判断购物车中有没有商品
    hasGoodsInCar(){
        return this.getTotalChooseNumber() > 0;
    }
    // 判断是否跨过了配送标准
    isCrossDeliveryThreshold(){
        return this.getTotalPrice() >= this.deliveryThreshold;
    }
    // 判断该商品是否被选中
    isChoose(index){
        return this.uiGoods[index].isChoose();
    }

}

分析界面

在分析完数据逻辑之后,我们来分析界面之间的逻辑关系
创建一个UI类

  • 代码如下:
// 整个界面
class UI{
    constructor(){
        this.uiData = new UIData();
        this.doms = {
            goodsContainer:document.querySelector('.goods-list'),
            deliverPrice:document.querySelector('.footer-car-tip'),
            footerPay:document.querySelector('.footer-pay'),
            footerPayInnerSpan:document.querySelector('.footer-pay span'),
            totalPrice:document.querySelector('.footer-car-total'),
            car:document.querySelector('.footer-car'),
            badge:document.querySelector('.footer-car-badge')
        }

        var carRect = this.doms.car.getBoundingClientRect();
        var jumpTarget = {
            x: carRect.left + carRect.width / 2,
            y: carRect.top + carRect.height / 5,
        };
        this.jumpTarget = jumpTarget;

        this.createHTML();
        this.updateFooter();
        this.listenEvent();

    }
    // 监听各种事件
    listenEvent(){
        this.doms.car.addEventListener('animationend', function(){
            this.classList.remove('animate');
        });
    }
    // 根据商品数据,创建商品列表
    createHTML(){
        // 1. 生成html字符串(parse html) 执行效率低,开发效率高
        // 2. 一个一个创建元素 执行效率高,开发效率低
        // 这里我们采用第一种方式
        var html = '';
        for(let i=0; i<this.uiData.uiGoods.length; i++){
            var g = this.uiData.uiGoods[i];
            html += `<div class="goods-item">
            <img src="${g.data.pic}" alt="" class="goods-pic" />
            <div class="goods-info">
              <h2 class="goods-title">${g.data.title}</h2>
              <p class="goods-desc">
                ${g.data.desc}
              </p>
              <p class="goods-sell">
                <span>月售 ${g.data.sellNumber}</span>
                <span>好评率${g.data.favorRate}</span>
              </p>
              <div class="goods-confirm">
                <p class="goods-price">
                  <span class="goods-price-unit">¥</span>
                  <span>${g.data.price}</span>
                </p>
                <div class="goods-btns">
                  <i index="${i}" class="iconfont i-jianhao"></i>
                  <span>${g.choose}</span>
                  <i index="${i}" class="iconfont i-jiajianzujianjiahao"></i>
                </div>
              </div>
            </div>
          </div>`;
        }
        this.doms.goodsContainer.innerHTML = html;
    }

    // 界面的增加减少
    increase(index){
        this.uiData.increase(index);
        this.updateGoodsItem(index);
        this.updateFooter();
        this.jump(index);
    }
    decrease(index){
        this.uiData.decrease(index);
        this.updateGoodsItem(index);
        this.updateFooter();
    }

    // 更新某个商品元素的显示状态
    updateGoodsItem(index){
        var goodsDom = this.doms.goodsContainer.children[index];
        if(this.uiData.isChoose(index)){
            goodsDom.classList.add('active');
        }else{
            goodsDom.classList.remove('active');
        }
        var span = goodsDom.querySelector('.goods-btns span');
        span.textContent = this.uiData.uiGoods[index].choose;
    }

    // 更新页脚
    updateFooter(){
        var total = this.uiData.getTotalPrice(); 
        this.doms.deliverPrice.textContent = `配送费¥${this.uiData.deliverPrice}`;
        if(this.uiData.isCrossDeliveryThreshold()){
            // 到达起送点
            this.doms.footerPay.classList.add('active');
        }else{
            this.doms.footerPay.classList.remove('active');
            // 更新还差多少钱
            var dis = this.uiData.deliveryThreshold - total;
            dis = Math.round(dis); 
            this.doms.footerPayInnerSpan.textContent = `还差¥${dis}元起送`;
        }
        // 总价元素,设置总价
        this.doms.totalPrice.textContent = total.toFixed(2);
        // 设置购物车的样式状态
        if(this.uiData.hasGoodsInCar()){
            this.doms.car.classList.add('active');
        }else{
            this.doms.car.classList.remove('active');
        }
        // 设置购物车中的数量
        this.doms.badge.textContent = this.uiData.getTotalChooseNumber();
    }
    // 购物车动画
    carAnimate(){
        this.doms.car.classList.add('animate');
    }
    // 抛物线跳跃的元素
    jump(index){
        // 找到对应商品的加号
        var btnAdd = this.doms.goodsContainer.children[index].querySelector('.i-jiajianzujianjiahao');
        var rect = btnAdd.getBoundingClientRect();
        var start = {
            x:rect.left,
            y:rect.top
        };
        // 跳
        var div = document.createElement('div');
        div.className = 'add-to-car';
        var i = document.createElement('i');
        i.className = 'iconfont i-jiajianzujianjiahao';
        // 设置初始位置
        div.style.transform = `translateX(${start.x}px)`;
        i.style.transform = `translateY(${start.y}px)`;
        div.appendChild(i);
        document.body.appendChild(div);
        // 强行渲染
        div.clientWidth;
        // 设置结束位置
        div.style.transform = `translateX(${this.jumpTarget.x}px)`;
        i.style.transform = `translateY(${this.jumpTarget.y}px)`;
        var that = this;
        div.addEventListener(
            'transitionend',
            function () {
                div.remove();
                that.carAnimate();
            },
            {
                once: true, // 事件仅触发一次
            }
        );
    }
    
}

分析事件

到这里为止界面上的逻辑已经全部完成,开始添加事件
在这里,为了获取我们到底是点击了哪个,可以添加一个自定义属性来获取index

  • 代码如下
var ui = new UI();

// 事件
ui.doms.goodsContainer.addEventListener('click', function (e) {
    if (e.target.classList.contains('i-jiajianzujianjiahao')) {
      var index = +e.target.getAttribute('index');
      ui.increase(index);
    } else if (e.target.classList.contains('i-jianhao')) {
      var index = +e.target.getAttribute('index');
      ui.decrease(index);
    }
  });
  
  window.addEventListener('keypress', function (e) {
    if (e.code === 'Equal') {
      ui.increase(0);
    } else if (e.code === 'Minus') {
      ui.decrease(0);
    }
  });

到这里,就实现了购物车的全部效果,我会上传我的资源,大家自行下载。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

30ring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值