react写小球动画偏js原生

一、具体思路
主要靠css3里面的贝塞尔曲线完成
1.创建div元素
2.给div定位
3.计算div具体左侧和顶部的距离
4.设置小球样式
5.贝塞尔曲线动画
6.用appendChild向父元素最后添加
7.添加动画属性Getcollect(),以定时器进行处理
8.添加ontransitionend事件 在 transition过度完之后执行的

二、贝塞尔曲线的介绍
        cubic-bezier 又称三次贝塞尔,主要是为 animation 生成速度曲线的函数,是 animation-timing-function 和 transition-timing-function 中的一个重要值。
语法:
​​​​​

cubic-bezier(x1,y1,x2,y2)

流程图和取值范围

 

cubic-bezier 的取值范围:

  • p0:默认值(0,0)
  • p1:取值(x1,y1)
  • p2:取值(x2,y2)
  • p3:默认值(1,1)

 在 cubic-bezier 中,p0和p3是默认的点,所以我们只需要定义p1和p2两个点,同时x1、y1、x2、y2的取值范围为[0,1]
注意:当取值超出范围时, cubic-bezier 就会失效。

在CSS3的动画中,有几个常用的固定值。
        ease: cubic-bezier(.25, .1, .25, 1)
        linear: cubic-bezier(0, 0, 1, 1)
        ease-in:cubic-bezier(.42,0,1,1)
        ease-out:cubic-bezier(0,0,.58,1)
        ease-in-out:cubic-bezier(.42,0,.58,1)
ease: cubic-bezier(.25, .1, .25, 1)

linear: cubic-bezier(0, 0, 1, 1)

 ease-in:cubic-bezier(.42,0,1,1)


ease-out:cubic-bezier(0,0,.58,1)


ease-in-out:cubic-bezier(.42,0,.58,1)

 三、页面结构代码
​​​​​​​

<div className='list'>
      <Header />
      <Tabs style={{
        "--active-title-color": "orange"
      }}>
        <Tabs.Tab title='商品' key='fruits'>
          <div className="box">
            <div className="menu-wrapper">
              <ul>
                {
                  menuList.length > 0 && menuList.map((item, index) => (
                    <li className={act === index ? 'active' : ''} onClick={() => FnTable(index)} key={index}>
                      <span className="text border-1px">
                        <span style={item.type > 0 ? {} : { display: 'none' }}></span>{item.name}                                    </span>
                    </li>
                  ))
                }
              </ul>
            </div>
            <div className="floor-list" ref={leftItem} >
              {menuList.map((floor, index) => (
                <div key={index} className="floor-item" id={`floor-${index}`}>
                  <div className="img">
                    <span>{floor.name}</span>
                  </div>
                  <div className="tp">
                    {
                      floor.foods.map((food, idx) => (
                        <div className='fot_id' key={idx}>
                          <div className="icon">
                            <img width="57" height="57" src={food.icon} />
                          </div>
                          <div className="content">
                            <h2 className="name">{food.name}</h2>
                            <p className="desc">{food.description}</p>
                            <div className="extra">
                              <span className="count">月售{food.sellCount}份</span><span>好评率{food.rating}%</span>
                            </div>
                            <div className="price">
                              <span className="now">¥{food.price}</span><span className='old' style={food.oldPrice ? {} : { display: 'none' }}>¥{food.oldPrice}</span>
                            </div>
                            <button onClick={(e) => {
                              Fnstart(e)
                            }}>+</button>
                          </div>
                        </div>
                      ))
                    }
                  </div>

                </div>
              ))}
            </div>
          </div>
        </Tabs.Tab>
        <Tabs.Tab title='评价' key='vegetables'>
          <Rates />
        </Tabs.Tab>
        <Tabs.Tab title='商家' key='animals'>
          商家
        </Tabs.Tab>
      </Tabs>
      <div className="footer" ref={collect}>
        <div className="left">
          <Badge content={total[1]}>
            <div className={styles.box}>
              <TruckOutline fontSize={"40px"} />
            </div>
          </Badge>
          <span style={{ marginRight: "10px" }}>¥{total[0]}</span>
          <span>另需配送费4元</span>
        </div>
        <div className="right">
          <button style={{ background: total[1] > 0 ? "green" : "#e3e3e3" }}>去结算</button>
        </div>
      </div>
    </div >

 四、实现代码

//加入购物车的小球动画
  // 创建小球添加动画
  const createBall = (left, top) => {
    // 创建div元素
    let Ball = document.createElement('div')
    // 给div定位
    Ball.style.position = "absolute";
    // div的left和span的left是一样的
    Ball.style.left = (left - 10) + 'px'
    // div的top和span的top是一样的
    Ball.style.top = (top - 10) + 'px'
    Ball.style.width = '20px'
    Ball.style.height = '20px'
    Ball.style.borderRadius = '50%'
    Ball.style.backgroundColor = 'green'
    // 贝塞尔曲线
    Ball.style.transition = "left .6s linear, top .6s cubic-bezier(0.5, -0.5, 1, 1)";
    // 向父元素最后添加
    document.body.appendChild(Ball);
    // 添加动画属性
    setTimeout(() => {
      Ball.style.left = Getcollect().offsetLeft + Getcollect().offsetWidth / 2 + "px";
      Ball.style.top = Getcollect().offsetTop + "px";
    }, 0);    //动画结束后,删除自己
    // ontransitionend事件 是在 transition过度完之后执行的 
    Ball.ontransitionend = function () {
      Ball.remove();
    };
  }

  // 获取收藏盒子
  const collect = useRef()
  const Getcollect = () => collect.current
  // 给每个span绑定点击事件
  const Fnstart = (e) => {
    // 获取当前span的x和y的值
    let x = e.clientX
    let y = e.clientY
    // 点击一次让state中的num+1
    setNum(num += 1)
    // 调用函数把x和y当参数传过去
    createBall(x, y)
    // 判断收藏的数量不为0的时候让收藏盒子显示
    if (num != 0) {
      Getcollect().style.opacity = '1'
    }
  }

  let total = useMemo(() => {
    let totalprice = 0
    let totalnum = 0
    menuList.forEach((good, index) => {
      good.foods.forEach((item, idx) => {
        totalprice += item.price * num
        totalnum += num
      })
    })
    return [totalprice, totalnum]
  })

五、css样式

.tab {
  --active-line-color: #ffffff;
  --active-title-color: orange;
}

.box {
  background: #eee;
  width: 40px;
  height: 40px;
  display: block;
  border-radius: 8px;

  &.dark {
    background: #666666;
  }
}

.overlayContent {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 150px;
  height: 150px;
  margin-top: -75px;
  margin-left: -75px;
  background-color: black;
  border-radius: 16px;

}
.fade{
  color: white;
}
.fade-enter {
  opacity: 0;
  animation-duration: .5s;
  animation-fill-mode: both;
  animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
  animation-play-state: paused;
}

.fade-appear {
  opacity: 0;
  animation-duration: .5s;
  animation-fill-mode: both;
  animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
  animation-play-state: paused;
}

.fade-leave {
  animation-duration: .5s;
  animation-fill-mode: both;
  animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
  animation-play-state: paused;
}

.fade-enter.fade-enter-active {
  animation-name: fadeIn;
  animation-play-state: running;
}

.fade-appear.fade-appear-active {
  animation-name: fadeIn;
  animation-play-state: running;
}

.fade-leave.fade-leave-active {
  animation-name: fadeOut;
  animation-play-state: running;
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes fadeOut {
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

​​​​​​​

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值