携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
1、长按或左划触发回调
onLongPress:手指触摸屏幕超过350ms后离开,触发onLongPress事件回调函数onTouchStart:手指触摸屏幕时记录触摸点的位置,触发onTouchStart事件回调函数onTouchEnd:手指离开屏幕是记录离开点的位置,触发onTouchEnd事件回调函数
应用场景:
- 购物车商品长按或右滑显示删除按钮,可进行删除操作
<View style={deleteSkuCode === product.skuCode ? { 'paddingRight': '60px'} : {}} key={product.id} onLongPress={() => handleProductDelete(product)} onTouchStart={(e) => touchStartProduct(e)} onTouchEnd={(e) => touchEndProduct(e, product)}>
</View>
{
deleteSkuCode === product.skuCode && <View className="product_deltet" onClick={() => deleteCartItem(product.id)}>删除</View>
}
- 记录长按位置对应的商品信息
const handleProductDelete = (sku) => {
setDeleteSkuCode(sku.skuCode);
}
- 记录开始点击屏幕触摸点位置
const touchStartProduct = e => {
setStartX(e.changedTouches[0].clientX)
}
- 记录结束点击屏幕触摸点位置,并记录对应需要显示删除按钮的商品
const touchEndProduct = (e, sku) => {
let endX = e.changedTouches[0].clientX;
if (startX - endX > 60) {
setDeleteSkuCode(sku.skuCode);
} else if (startX - endX < -60) {
setDeleteSkuCode(null);
}
}
2、封装右侧弹窗
useImperativeHandle:使用ref时自定义暴露给父组件的实例值,useImperativeHandle应当与forwardRef一起使用。forwardRef:会创建一个React组件,这个组件能够将其接受的ref属性转发到其组件树下的另一个组件中。Taro.createAnimation: 创建一个动画实例animation。调用实例的方法来描述动画。最后通过动画实例的export方法导出动画数据传递给组件的animation属性。
涉及属性值:
children:类似插槽值width:弹窗宽度,默认305show:显示或隐藏,默认隐藏
import React, { useState, useImperativeHandle, forwardRef } from 'react'
import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'
import Mask from '@/components/mask/index'
import './index.less'
function Index (props, ref) {
const app = Taro.getApp();
const { navHeight } = app.$app.state;
const { children = '', width = 305, show = false } = props
// 暴露给父组件的实例值
useImperativeHandle(ref, () => ({
handleShowModal,
handleHideModal
}))
// 创建一个动画实例 animation
let animation = Taro.createAnimation({
duration: 600, // 动画持续时间,单位 ms
timingFunction: "ease", // 动画的效果,可选linear、ease、ease-in、ease-in-out、ease-out、step-start、step-end
delay: 0 // 动画延迟时间,单位 ms
});
const [animationData, setAnimationData] = useState({})
const [showModal, setShowModal] = useState(show)
const handleShowModal = () => {
setShowModal(true)
setTimeout(() => {
fadeInOrDown(0)
}, 100)
}
const handleHideModal = () => {
fadeInOrDown(width)
setTimeout(() => {
setShowModal(false)
}, 600)
}
const fadeInOrDown = (postionX) => {
// step()表示一组动画完成。可以在一组动画中调用任意多个动画方法,一组动画中的所有动画会同时开始,一组动画完成后才会进行下一组动画。
animation.translateX(postionX).step() // 对 X 轴平移
setAnimationData(animation.export())//动画实例的export方法导出动画数据传递给组件的animation属性
}
return (
<>
{
showModal &&
<>
<Mask controlMask={() => handleHideModal()} />
<View className="filter_container" style={`top: ${navHeight}px; transform: translate(${show ? 0 : width}px, 0)`} animation={animationData}>
{
children
}
</View>
</>
}
</>
)
}
export default forwardRef(Index)
.filter_container {
position: fixed;
left: 70px;
top: 0;
right: 0;
bottom: 0;
background: #fff;
z-index: 98;
}
遮罩层封装(handleMask为点击遮罩层回调)
import React from 'react'
import { View } from '@tarojs/components'
import './index.less'
function Index(props) {
const { controlMask } = props
const handleMask = () => {
controlMask && controlMask()
}
return (
<View className="mask" onClick={() => handleMask()}></View>
)
}
export default Index
.mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(74,74,74,0.48);
z-index: 98;
}
使用组件:
import HorizontalModal from '@/components/horizontalModal/index'
...
export default PageWrapper(function Index () {
const childrenRef = useRef(null);
const handleShowModal = () => {
childrenRef.current.handleShowModal()
}
return (
<HorizontalModal ref={childrenRef}>
<View className="filter_item">
<View className="content">
<Form onSubmit={e => filterSubmit(e)} onReset={() => filterReset()} >
<View className='input_item'>
<View className="left">收货人</View>
<Input className="right" name="name" type='text' />
</View>
<View className="form_content">
<Button className="btn reset" formType="reset">重置</Button>
<Button className="btn" formType="submit">确定</Button>
</View>
</Form>
</View>
</View>
</HorizontalModal>
)
})
3、设置设计稿为375标准下开发,其他尺寸自适应
designWidth:设计稿尺寸deviceRatio:Taro支持 750、 640 、 828 三种尺寸设计稿,换算规则
const path = require('path')
const config = {
projectName: 'mini-taro',
date: '2021-8-14',
designWidth: 375,
deviceRatio: {
'640': 2.34 / 2,
'750': 1,
'828': 1.81 / 2,
'375': 2 / 1
},
//...
alias: {
'@/components': path.resolve(__dirname, '..', 'src/components'),
}
}
module.exports = function (merge) {
if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev'))
}
return merge({}, config, require('./prod'))
}
4、Taro的外部样式和全局样式
externalClasses:需要使用短横线命名法 (kebab-case),否则无效addGlobalClass:组件外样式类能够完全影响组件内部,将组件构造器中的options.addGlobalClass字段置为true
class Index extends Component {
static externalClasses = ['my-class']
render () {
return <View className="my-class">组件外的class决定</View>
}
}
class Index extends Component {
static options = {
addGlobalClass: true
}
render () {
return <View className="my-class">组件外的class决定</View>
}
}