Touchable 3.0.0

Touchable 组件是一个"虚拟"组件,它不会真的在文档中创建一个 dom 节点作为根节点,而是返回它唯一的子组件的一个克隆,并给它绑定一些手势事件。 除了能给 dom 绑定 tap 事件之外,它还解决了一些移动端的手势"顽疾",例如触摸反馈和滚动/触摸的冲突问题。在需要绑定 tap 事件的情况下,应该优先使用 Touchable, 而不是直接把 tap 事件回调绑定给 dom

使用说明

移动端的触摸事件

"所有能够响应触屏操作的元素在触屏后都应该有一个视觉上的反馈。这也是为什么一个"web"应用总是显得不够"原生"的主要原因之一。"—— React Native 官方文档,TouchableWithoutFeedback

在大多数原生 App 中,点按操作(Press)会有一个明确的反馈。这个反馈可以是改变了背景色(例如从白色变成灰色),也可以是改变元素的透明度,或者改变背景图片等等。总之,这种触摸反馈能够让整个 App 变得更加立体,也让用户更能直接地体验到交互感。

但是由于移动端浏览器的种种限制,这种“触摸反馈”变得没那么容易实现。使用 PC 端常用的 :active 伪类并不能完全满足需求。因为滚动视图(Scroller)组件的存在,各种触摸事件都需要解决和滚动的冲突问题,即在滚动时不允许触发 Press,这靠 css 是无法独自解决的。

出于以上两点考虑,我们借鉴了 React-Native 的 Touchable 系列组件,为网页也设计实现了同样的组件。我们鼓励你在所有需要绑定 tap 事件的场景使用它。

引用方式

import { Touchable } from '$yo-component';

// 如果你的项目中未使用 ykit-config-yo 插件,可能无法使用上面这个语法糖
// 你仍然可以通过下面这种方式来引用
import Touchable from 'yo3/component/touchable';

使用方式

Touchable 是一个“虚拟”的组件,它没有根节点,而是以传给它的唯一子元素作为根节点。换句话说,它不会改变原本的 dom 结构。需要注意的是,它只能有一个子元素,如果传入多个子元素 React 会抛出一个错误,以下是例子:

<Touchable
  touchClass="touchable-highlight"
  onTap={()=> {
    Toast.show('touchable pressed.');
  }}
>
  <div style={{ height: 200 }}>
      点击会出现深色背景的Touchable
  </div>
</Touchable>

这里有两个比较重要的属性 onTaptouchClass : onTap是你希望绑定给它的唯一子元素的 Tap 事件回调,而 touchClass 是触摸过程中(touchstarttouchend 过程中)为唯一子元素添加的 className。

如果你没有设置 touchClass,将不会有触摸反馈效果,onTap 的绑定依然生效。但是这个时候组件会给出一个警告,我们的建议是尽量为所有的可触摸区域添加反馈效果。

需要注意的是 Touchable 可以和自定义组件一起使用,但是你的自定义组件必须能够接收以下四个属性: onTouchStartonTouchMoveonTouchEndonTouchCancel,否则 Touchable 将无法将这几个属性注入到你的组件根节点上,也就无法正常工作了。

实际上,大部分 Yo 的组件都是不能直接嵌套在 Touchable 内部的,因为它们本身并不是适合绑定触摸对象的组件(我想你也不太可能把 List 这样的组件放在一个 Touchable 里面),唯一的例外是 LazyImage 组件。

嵌套的Touchable

Touchable虽然允许互相嵌套,但是不能够同时触发内层和外层的 touchClass 改变和 tap 事件,而总是触发手指触摸到的最内层的 Touchable 的触摸反馈和事件, 这与没有 stopPropagation 的 onTouchTap 事件不一致。换句话说,在同一时刻只能有一个 Touchable 响应用户的触摸事件。 以下是一个互相嵌套的 Touchable 的例子:

<Touchable touchClass='green' onTap={() => {console.log('点我变绿。')}}>
  <div className="comment-wrap">
    <Touchable touchClass='yellow' onTap={() => {
      console.log('点我变黄');
    }}>
      <h2 className="comment-title ellipsis">如此美景,难怪志明要带春娇来这里</h2>
    </Touchable>
    <p className="comment-detail ellipsis">北京长城脚下的公社</p>
    <div className="tags ellipsis">
      度假 / 亲子 / 浪漫 / 美景 / 格调
    </div>
  </div>
</Touchable>

解决和Scroller的冲突问题

Touchable 组件在内部解决了和 Scroller 以及所有基于 Scroller 实现的组件(List系列等)的手势冲突, 你不需要做任何额外的配置, 正常使用即可 。 下面是一个在 Scroller 内部使用 Touchable 的例子,这些代码就是右侧 Demo 的源码:

class TouchableDemo extends Component {
  render() {
    return (
      <Page title="Touchable Demo" onLeftPress={() => {
        location.href = '../index/index.html';
      }}>
        <Scroller extraClass="yo-scroller-fullscreen">
          <Touchable onTap={()=> {
            Toast.show('touchable pressed.');
          }}>
            <div className="demo">
              这是一个没有任何反馈的Touchable。
            </div>
          </Touchable>
          <Touchable touchClass="touchable-highlight" onTap={()=> {
            Toast.show('touchable pressed.');
          }}>
            <div className="demo">
              点击会出现深色背景的Touchable
            </div>
          </Touchable>
          <Touchable touchClass="touchable-opacity" onTap={()=> {
            Toast.show('touchable pressed.');
          }}>
            <div className="demo">
              点击后透明度变为0.7的Touchable
            </div>
          </Touchable>
          <Touchable touchClass="touchable-opacity touchable-highlight" onTap={()=> {
            Toast.show('touchable pressed.');
          }}>
            <div className="demo">
              同时具有以上两种效果的Touchable
            </div>
          </Touchable>
        </Scroller>
      </Page>
    );
  }
}
属性

touchClass { String } #

触摸Touchable时附加的className,可以用来实现Native常见的触摸反馈功能(例如给触摸区域添加深色背景或者改变透明度等等)。

默认值: null

onTap { Function } #

给Touchable绑定的onTap事件。

默认值: null

方法参数:

参数名 类型 描述 支持版本
target DOMElement tap事件的target

disabled { Bool } 3.0.7 #

Touchable是否处于可点击状态,如果设为true,那么onTap事件回调和触摸反馈效果都不可用。

默认值: false