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

目录

购物车效果

先准备好原始数据和素材

在下面数据的基础上,编写index.js

分析数据

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

单件商品的数据

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

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

  • 代码如下:
javascript 复制代码
// 单件商品的数据
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类,并进行测试(大家可以自行在控制台进行测试)

  • 代码如下
javascript 复制代码
// 整个界面的数据
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类

  • 代码如下:
javascript 复制代码
// 整个界面
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

  • 代码如下
javascript 复制代码
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);
    }
  });

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

相关推荐
咬人喵喵11 分钟前
CSS Flexbox:拥有魔法的排版盒子
前端·css
LYFlied12 分钟前
TS-Loader 源码解析与自定义 Webpack Loader 开发指南
前端·webpack·node.js·编译·打包
yzp011214 分钟前
css收集
前端·css
暴富的Tdy14 分钟前
【Webpack 的核心应用场景】
前端·webpack·node.js
遇见很ok14 分钟前
Web Worker
前端·javascript·vue.js
elangyipi12315 分钟前
JavaScript 高级错误处理与 Chrome 调试艺术
开发语言·javascript·chrome
风舞红枫17 分钟前
前端可配置权限规则案例
前端
前端不太难19 分钟前
RN Navigation vs Vue Router:从架构底层到工程实践的深度对比
javascript·vue.js·架构
zhougl99627 分钟前
前端模块化
前端
暴富暴富暴富啦啦啦43 分钟前
Map 缓存和拿取
前端·javascript·缓存