这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
介绍
双十一将至,不知道大家做过商城类的项目没有,大多比较枯燥死板的加减数量,同质化也都比较强,咱们本期就做一个清新风的购物卡片的React组件,让他增加一些用户体验,当然本文面相新手向和创意流,技术大佬请绕路。
先康康我们做完的效果吧:
我们可以看到啊,每次点击加减数量都会出现一个产生一个总数累加的动效,当归0时,再减小,就会数量0就会放大提示用户我已经是0了别往下减了,这样虽然没有任何一句提示,仅仅通过动画就没有表达给用户。接下来,我们就开始实现它吧。
正文
1.基本逻辑
我们用react hook先写出一个卡片结构吧:
import './style.scss';
import { useState, useEffect, useRef } from "react"
function BuyCard(props) {
const { src, price } = props;
const [num, setNum] = useState(0);
const [total, setTotal] = useState(0);
const totalRef = useRef(null);
const amountRef = useRef(null)
useEffect(() => {
setTotal(num * price)
}, [num, price])
function handleAdd() {
setNum(num + 1)
}
function handleMinus() {
setNum(num - 1)
}
return (
<div className="buy-card">
<img src={src} alt="" />
<div className="buy-info">
<div className="buy-title"><label>PRICE:</label><span>$</span><span>{price}</span></div>
<div className="buy-title"><label>TOTAL:</label><span>$</span><span ref={totalRef}>{total}</span></div>
<div className="btns">
<button className="minus btn" onClick={handleMinus}>-</button>
<p className="amount"><span ref={amountRef}>{num}</span></p>
<button className="add btn" onClick={handleAdd}>+</button>
</div>
</div>
</div>
);
}
export default BuyCard;
我们期望从外界传入图片和价格,如果真是开发应该还有数量等信息,本次我们先将数量不暴露出来,方便观看效果,其逻辑只做简单表述。点击加减都绑定了对应的事件去改变数量,而改变后会因useEffect执行改变其总价格。
当然,现在还不能看,我们还要写点样式出来,非常简约的卡片样式,这里就不做赘述了:
.buy-card{
width: 200px;
height: 300px;
background-color: #ffffff;
box-sizing: border-box;
box-shadow: 1px 1px 10px rgba(0,0,0,.1);
border-radius: 5px;
border: 2px dashed #c33;
padding-top: 10px;
transition: .2s all;
&:hover{
box-shadow: 1px 1px 12px rgba(0,0,0,.3);
}
&>img{
width: 150px;
height: 150px;
display: block;
margin: 0 auto;
}
.btns{
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
.btn{
border: 0;
cursor: pointer;
border-radius: 50%;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
color: white;
&.add{
background-color: rgb(118, 201, 98);
}
&.minus{
background-color: rgb(233, 93, 88);
}
}
.amount{
width: 50px;
text-align: center;
height: 24px;
line-height: 24px;
border-bottom: 1px solid rgb(73, 73, 72);
margin: 0 10px;
span{
font-size: 18px;
font-weight: bold;
width: 100%;
height: 24px;
display: block;
line-height: 24px;
}
}
}
.buy-info{
width:100%;
box-sizing: border-box;
padding: 15px;
font-family: 'Courier New', Courier, monospace;
.buy-title{
margin-bottom: 13px;
font-size: 16px;
font-weight: bold;
height: 24px;
line-height: 24px;
border-bottom: 1px solid rgb(73, 73, 72);
label{
margin-right: 5px;
}
}
}
}
现在我们就可以在App.js引用他,就可以看到样式了。
import BuyCard from "./components/buyCard"
const f0 = require("./assets/food0.png")
function App() {
return (
<div className="App">
<div className="cards">
<BuyCard price="100" src={f0.default} />
</div>
</div>
);
}
2.累计与触零动画
我们实现这两个动画的方案很多,今天就来说一种个人感觉最佳的方案——animejs,他可以自由实现各种各样的网页动画,而且十分流程。接下来就用它来实现效果吧。
import anime from "animejs"
function BuyCard(props) {
// ...
useEffect(() => {
anime({
targets: totalRef.current,
textContent: [total, num * price],
round: 1,
easing: 'easeOutCirc',
duration: 300,
complete() {
setTotal(num * price)
}
});
}, [num, price])
// ...
}
这是那个累计动画,获取到要改变的dom元素后,让他在textContent去改变,可以设置缓动,周期等,但是一定要注意的是,在react中不像正常操作dom那样光改了就行了,还要在其动画结束后的complete函数内再给total赋值一遍,这样才会在react中改变total值,不然不光在react拿不到值,也会出现每次都是从0开始执行动画的bug。
function handleMinus() {
if(num===0){
return anime({
targets: amountRef.current,
easing: 'linear',
duration: 400,
scale: [{ value: 1.6 }, { value: 1 }]
});
}
setNum(num - 1)
}
后面的触零动画就简单多了,我们每次如果数量到了0如果再往下减就阻断他,并且利用animejs改变scale属性,使其出现一个由大至小的动画,用动画来表明数量不可为负数了。
说到这里其实已经完成了全部,是不是很容易,在线演示。
结语
本次其实是react hook与animejs的一次结合使用,不知道是否激发了你新的创意,别看一个小小的卡片其实还可以增加很多东西,比如加减归零等触发声音,或再给商品加入一些趣味动画,与诸多扩展。等你来发挥~