Taro中常见问题(一)

792 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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:弹窗宽度,默认305
  • show:显示或隐藏,默认隐藏
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:设计稿尺寸
  • deviceRatioTaro支持 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>
  }
}