概述

在移动应用开发中,悬浮、点击、双击、长按、上下文菜单、拖拽、轻扫、滚动/平移、缩放和旋转这10种交互事件,在不同的移动端设备上可以分别由触控屏、触控板或鼠标这三类输入设备的不同操作方式触发。开发多设备项目时,必须考虑不同输入设备的交互方式。而使用交互归一组件,保证不同交互场景下的体验一致性,开发者只需要调用归一后的交互事件接口,无需为每个输入设备单独适配,从而大幅简化开发流程。

如下图为不同交互事件在不同设备上的触发方式:

如下表为不同框架上提供的交互归一组件实现不同交互事件的接口:

  

RN(组件地址)

Flutter(组件地址

H5(组件地址

悬浮

Gesture.Hover()

onHover

onHover

点击

Gesture.Pan()

onTap

onClick

双击

Gesture.Tap()

onDoubleTap

onDoubleClick

长按

Gesture.LongPress()

onLongPress

onLongPressStart

onLongPressEnd

onLongPressCancel

 

上下文菜单

react-native-popup-menu

onContentMenu

onContextMenu

拖拽

Gesture.Pan()

onDragStart

onDragUpdate

onDragEnd

onDragStart

onDragEnter

onDragMove

onDragLeave

onDrop

轻扫

Gesture.Fling()

onSwipe

onSwipe

滚动

Gesture.Pan()

onPanStart

onPanUpdate

onPanEnd

onPanCancel

onScroll

缩放

Gesture.Pinch()

Transform

onPinchStart

onPinchUpdate

onPinchStart

onPinchMove

onPinchEnd

旋转

Gesture.Rotation()

Transform

onRotateStart

onRotateUpdate

onRotateEnd

onRotateStart

onRotateMove

onRotateEnd

交互归一事件适配

1、悬浮事件

  • RN适配指导

    1. 主要方法和参数

    Gesture.Hover():悬浮手势对象。

    onHover:悬浮手势触发的回调方法。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    // 用手势组件包裹元素
    <GestureDetector gesture={onHoverForTest}>
      <Text style={[isHovered && styles.mouseHovered]}>
        {title}
      </Text>
    </GestureDetector>
    
    //在鼠标悬浮的时候改变样式
     const onHoverForTest = Gesture.Hover().onHover(e => {
        // 鼠标移入或移除的时候触发
        setIsHovered(e.isHover);
      });
  • Flutter适配指导

    1. 主要方法

    onHover:悬浮事件名,在onHover方法实现悬浮需要实现的具体事件效果。

    2. 回调参数

    isHover:鼠标是否悬浮。

    3. 示例

    UnifiedGestureDetector(
      onHover: onHover,
      child: Container(...) //需要实现悬浮效果的widget,例如Text
    )
    void onHover(bool isHover, PointerEvent event) {
      if (mounted) {
        setState(() {
           if (isHover) { 
             // 悬浮,按钮变大逻辑
           }else {
             // 取消悬浮,按钮还原
            }  
         });
      }
    }
  • H5适配指导

    1. 主要方法

    onHover:悬浮进入或退出事件。

    onHoverMove:悬浮事件,在onHoverMove方法实现悬浮的具体效果。

    2. 示例

    <template>
      <div ref="testDom" id="testId"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const handleHover = (state: PointerState) => {
      // 代码编辑
    };
    const handleHoverMove = (state: PointerState) => {
      // 代码编辑
    };
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onHover: handleHover,
        onHoverMove: handleHoverMove,
      };
      const configs: PointerOptions = {
        enableHover: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      //destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 200px;
      height: 200px;
      background-color: aquamarine;
    }
    </style>

2、点击和双击事件

  • RN适配指导

    1. 主要方法和参数

    Gesture.Tap():手势点击对象,单击或双击的时候触发。

    numberOfTaps:属性,用于触发点击手势所需的点击次数。

    minPointers:属性,用于触发手势需要的手指数目。

    onStart:回调方法,手势触发开始时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    // 用手势组件包裹元素
    <GestureDetector gesture={Gesture.Exclusive(doubleTap, singleTap)}>
      <View>
        <Animated.View style={[animatedStyle]}>
          {songsList.map((item, index) => (
            <View key={index}>
              <Text>1233333333333333</Text>
            </View>
          ))}
        </Animated.View>
      </View>
    </GestureDetector>
    
    // 通过点击的次数区分单双击事件
    // 单击事件
    const singleTap = Gesture.Tap()
      .numberOfTaps(1)
      .onStart(() => {
        console.log('Single tap!');
      });
    // 双击事件
    const doubleTap = Gesture.Tap()
      .numberOfTaps(2)
      .onStart(() => {
        console.log('Double tap!')
      });
  • Flutter适配指导

    1. 主要方法

    onTap:单击事件名,在onTap方法实现单击具体操作。

    onDoubleTap:双击事件名,在onDoubleTap方法实现双击具体操作。

    2. 示例

    UnifiedGestureDetector(
      onTap: onTap,
      onDoubleTap: onDoubleTap,
      child: Container(...) //需要实现单击和双击的widget
    )
    void onTap() {
        //单击进入歌曲详情页,歌曲暂停状态下不播放歌曲
    }
    void onDoubleTap() {
        //双击进入歌曲详情页,歌曲暂停状态下播放歌曲
    }
  • H5适配指导

    1. 主要方法

    onClick:单击事件,在onClick方法实现单击具体操作。

    onDoubleClick:双击事件,在onDoubleClick方法实现双击具体操作。

    2. 示例

    <template>
      <div ref="testDom" id="testId"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const handleClick = (state: PointerState) => {
      // 代码编辑
    };
    const handleDoubleClick = (state: PointerState) => {
      // 代码编辑
    };
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onClick: handleClick, // 点击
        onDoubleClick: handleDoubleClick // 双击
      };
      const configs: PointerOptions = {
        enableClick: true,
        enableDoubleClick: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      //destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 200px;
      height: 200px;
      background-color: aquamarine;
    }
    </style>

3、长按事件

  • RN适配指导

    1. 主要方法和参数

    Gesture.LongPress():长按事件对象,长按时创建。

    repeat:boolean类型属性,是否允许长按手势重复触发。

    minDuration:number类型属性,长按手势识别所需的最短持续时间(毫秒)。

    onStart:回调方法,手势开始时的回调。

    onCancel:回调方法,手势取消时的回调。

    onEnd:回调方法,手势结束时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    // 用手势组件包裹元素
    <GestureDetector gesture={longPressGesture}>
      <Image
        source={require('../../../asset/share.svg')}
        style={styles.imageSmall}
      />
    </GestureDetector>
    // 长按的时候触发提醒
    const longPressGesture = Gesture.LongPress().onEnd(() => Alert.alert('无法分享'));
  • Flutter适配指导

    1. 主要方法

    onLongPress:长按事件名,在onLongPress方法中实现长按需要的效果。

    2. 示例

    UnifiedGestureDetector(
      pointerOptions: const PointerOptions(),
      onLongPress: onLongPress,
      child:  Container(...) //需要实现长按效果的widget
    )
    void onLongPress() {
        //长按事件触发后的处理逻辑
    }
  • H5适配指导

    1. 主要方法

    onLongPressStart:长按开始事件,在onLongPressStart方法实现长按的具体操作。

    onLongPressEnd:长按结束事件。

    onLongPressCancel:长按取消事件,在onLongPressCancel方法实现长按取消的具体操作。

    2. 示例

    <template>
      <div ref="testDom" id="testId"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const handlePressStart = (state: PointerState) => {
      // 代码编辑
    };
    const handlePressEnd = (state: PointerState) => {
      // 代码编辑
    };
    const handlePressCancel = (state: PointerEvent) => {
      // 代码编辑
    };
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onLongPressStart: handlePressStart, // 长按开始
        onLongPressEnd: handlePressEnd, // 长按结束
        onLongPressCancel: handlePressCancel, // 长按取消
      };
      const configs: PointerOptions = {
        enableLongPress: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 200px;
      height: 200px;
      background-color: aquamarine;
    }
    </style>

4、上下文菜单

  • RN适配指导

    1. 组件

    MenuTrigger: 组件,用于嵌套在触发上下文的组件的外层。

    MenuOptions: 组件,用于嵌套在上下文组件的外层,其内层可以有多个MenuOption 组件。

    Menu: 组件,上下文外层组件,用于嵌套在MenuTrigger 和MenuOptions组件外。

    MenuProvider: 组件,上下文外层组件,用于嵌套在Menu组件外。

    2. 示例

    import {
      GestureDetector,
      Gesture,
      MenuProvider,
      Menu,
      MenuOptions,
      MenuOption,
      MenuTrigger,
      ViewWithKeyEvent,
    } from '@hadss/react-native-uniinput';
    
    //MenuTrigger播过触发上下文的组件   
    //MenuOptions包裹弹出的上下文的内容
    //MenuTrigger和MenuTrigger都是Menu的子组件,而Menu又是MenuProvider的子组件
    <MenuProvider
      style={{alignItems: 'center', marginTop: 20}}
      onTouchStart={calculateMenuPosition}>
      <Menu
        onClose={() => {
          setTimeout(() => setIsLongPressed(false), 500);
        }}>
        <MenuTrigger
          triggerOnLongPress={true}
          onPress={() => {
            setIsLongPressed(true);
          }}>
          <Image
            source={require('../../../../asset/song_item_icon.svg')}
            style={styles.songIcon}
          />
        </MenuTrigger>
        <MenuOptions
          optionsContainerStyle={[
            styles.optionList,
            {
              position: 'absolute',
              top: position.top,
              left: position.left,
            },
          ]}>
          <MenuOption
            onSelect={() => deleteItem(index)}
            style={{flexDirection: 'row'}}>
            <Image
              source={require('../../../../asset/delete.svg')}
              style={styles.songDelete}
            />
            <Text style={styles.optionBtn}>{'删除'}</Text>
          </MenuOption>
          {item.hasCollect ? (
            <MenuOption
              onSelect={() => deleteCollectItem(index)}
              style={{flexDirection: 'row'}}>
              <Image
                source={require('../../../../asset/deleteCollect.svg')}
                style={styles.songDelete}
              />
              <Text style={styles.optionBtn}>{'移除收藏'}</Text>
            </MenuOption>
          ) : (
            <MenuOption
              onSelect={() => collectItem(index)}
              style={{flexDirection: 'row'}}>
              <Image
                source={require('../../../../asset/collect.svg')}
                style={styles.songDelete}
              />
              <Text style={styles.optionBtn}>{'收藏到歌单'}</Text>
            </MenuOption>
          )}
        </MenuOptions>
      </Menu>
    </MenuProvider>
  • Flutter适配指导

    1. 主要方法

    onContentMenu:下拉菜单功能,鼠标或者触控点位置存在的情况下,显示上下文菜单UI效果。

    2. 回调参数

    event.localPosition:当前鼠标或者触控点位置。

    3. 示例

    UnifiedGestureDetector(
      pointerOptions: const PointerOptions(),
      onContentMenu: onContentMenu,
      child: CustomWidget(...) //需要实现上下文菜单的widget
    )
    void onContentMenu(GestureEvent event) {
      //event.localPosition:当前鼠标或者触控点的位置
      if (event.localPosition == null) {
        return;
      }
      //创建上下文菜单Widget,使用PopupMenu的showMenu方法自定义PopupMenuItem实现上下文菜单UI
    }
  • H5适配指导

    1. 主要方法

    onContextMenu:下拉菜单功能,鼠标或者触控点位置存在的情况下,显示上下文菜单UI效果。

    2. 示例

    <template>
      <div ref="testDom" id="testId"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const handleContextMenu = (state: MouseEvent) => {
      // 代码编辑
    };
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onContextMenu: handleContextMenu, // 上下文菜单
      };
      const configs: PointerOptions = {
        enableContextMenu: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 200px;
      height: 200px;
      background-color: aquamarine;
    }
    </style>

5、拖拽事件

  • RN适配指导

    1. 主要方法和参数

    Gesture.Pan():手势拖拽对象。

    minDistance:number类型属性,触发拖拽手势所需的最小距离。

    minPointers:number类型属性,触发拖拽手势需要的手指数目。

    direction:string类型属性,定义手势的方向或方向组合, 可能的值有:'all', 'horizontal', 'vertical', 'left', 'right', 'up', 'down', 'none' 。也可以使用&和|组合。

    onStart:回调方法,手势拖拽开始时的回调。

    onUpdate:回调方法,手势拖拽变化时的回调。

    onCancel:回调方法,手势拖拽取消时的回调。

    onEnd:回调方法,手势拖拽结束时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    //手势组件包裹需要进行拖动的内容
     <GestureDetector gesture={panProgress}>
        <View>
          <View style={[styles.progressFill, {width: `${position}%`}]} />
        </View>
      </GestureDetector
     const panProgress = Gesture.Pan()
        .minPointers(1)
        .direction('left | right')
        .onUpdate(event => {
          console.log('Gesture Update:', event);
        })
    const styles = StyleSheet.create({
      progressFill: {
        height: '100%',
        backgroundColor: 'rgba(255, 255, 255, 0.8)',
        borderRadius: 5,
      },
    })
  • Flutter适配指导

    1. 主要方法

    onDragStart:拖拽开始。

    onDragUpdate:拖拽中,更新进度条宽度,localPosition返回拖拽的坐标。

    onDragEnd:拖拽结束,播放器播放指定进度,刷新页面。

    2. 回调参数

    detail.localPosition.dx:拖拽的x轴坐标。

    detail.localPosition.dy:拖拽的y轴坐标。

    3. 示例

    UnifiedGestureDetector(
      pointerOptions: const PointerOptions(
        enableRotate: false,
      ),
      onDragStart: onDragStart,
      onDragUpdate: onDragUpdate,
      onDragEnd: onDragEnd,
      child: Container(...) //自定义拖拽的控件
    )
    
    void onDragStart(DragStartDetails detail) {}
    void onDragUpdate(DragUpdateDetails detail) {
        //detail.localPosition.dx拖拽x轴坐标,根据x轴坐标更新播放器进度条,控制拖拽范围[0,进度条控件宽度]
    }
    void onDragEnd(DragEndDetails detail) {
      //拖拽结束获取最后x轴坐标位置,计算当前进度百分比,调整播放器进度
    }
  • H5适配指导

    1. 主要方法

    onDragStart:拖拽开始事件,绑定A组件,在触控屏上长按并移动或长按鼠标左键并移动触发。

    onDragEnter:拖拽进入事件,绑定B组件,进入B组件时触发。

    onDragMove:拖拽移动事件,绑定B组件,在B组件移动时触发。

    onDragLeave:拖拽离开事件,绑定B组件,离开B组件时触发。

    onDrop:拖拽结束事件,松开鼠标左键或手指在触控屏抬起触发。

    2. 示例

    <template>
      <div id="testId">
        <div ref="dragStart" draggable="true" class="dragStart"></div>
        <div ref="drag" class="drag"></div>
      </div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const dragStart = ref<HTMLElement | null>(null);
    const drag = ref<HTMLElement | null>(null);
    const instance1 = ref(null);
    const instance2 = ref(null);
    const handleDragStart = (state: DragEvent) => {
      // 代码编辑
    };
    const handleDragEnter = (state: DragEvent) => {
      // 代码编辑
    };
    const handleDragMove = (event: DragEvent) => {
      event.preventDefault();
      // 代码编辑
    };
    const handleDragLeave = (state: DragEvent) => {
      // 代码编辑
    };
    const handleDrop = (state: DragEvent) => {
      // 代码编辑
    };
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers1: GestureHandlers = {
        onDragStart: handleDragStart, // 拖拽开始
      };
      const handlers2: GestureHandlers = {
        onDragEnter: handleDragEnter, // 拖拽进入
        onDragMove: handleDragMove, // 拖拽中
        onDragLeave: handleDragLeave, // 拖拽离开
        onDrop: handleDrop, // 拖拽放下
      };
      const configs: PointerOptions = {
        enableDrag: true,
      };
      if (dragStart.value) {
        instance1.value = PointerGestureManager(dragStart.value, handlers1, configs);
      }
      if (drag.value) {
        instance2.value = PointerGestureManager(drag.value, handlers2, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance1.value) {
        instance1.value.destroy();
      }
      if (instance2.value) {
        instance2.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 100%;
      height: 900px;
    }
    .dragStart {
      width: 100px;
      height: 100px;
      background-color: aquamarine;
    }
    .drag {
      width: 100px;
      height: 100px;
      background-color: red;
    }
    </style>

6、轻扫事件

  • RN适配指导

    1. 主要方法和参数

    Gesture.Fling(): 手势轻扫对象。

    minPointers: number类型属性,触发手势需要的手指数目。

    direction:定义手势的方向或方向组合, 可能的值有:'all', 'horizontal', 'vertical', 'left', 'right', 'up', 'down', 'none' 。也可以使用'&'和'|'组合。

    onStart: 回调方法,手势轻扫开始时的回调。

    onCancel: 回调方法,手势轻扫取消时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    //手势组件包裹需要进行拖动的内容
    <GestureDetector gesture={flingSongsList}>
      <Animated.View >
         {songsList.map((item, index) => (
            <View key={index} style={styles.item}>
              <Text>1233333333333333</Text>
            </View>
          ))}
      </Animated.View>
    </GestureDetector>
    
    const panSongsList = Gesture.Fling()
      .minVelocity(200)
      .onStart(() => {
        // 处理轻扫事件回调
      });
  • Flutter适配指导

    1. 主要方法

    onSwipe:轻扫事件,可以设置pointerOptions属性,在轻扫的时候禁掉其他手势行为,例如缩放 enableRotate属性可以设置为false。

    2. 主要方法

    event.speed:可以根据属性来约束轻扫的速度,单位为db/s,示例中speed大于等于600才判定为轻扫操作,执行轻扫功能。

    event.angle:滑动角度,顺时针旋转为0度到180度,逆时针旋转为-180度到0度,根据角度正负判断向上轻扫还是向下轻扫。

    3. 示例

    UnifiedGestureDetector(
      pointerOptions: PointerOptions(
        speed: 150,
        direction: SwipeDirection.all,
        enableSwipe: true,
        panDirection: panDirection,
      ),
      onSwipe: onSwipe,
      child:  ListView(...) //自定义轻扫控件,可以自定义一个ListView实现列表滚动
    )
    
    void onSwipe(GestureEvent event) {
      //约束轻扫条件:滑动速度大于等于600才能判定为轻扫
      if (event.speed! < 600) {
        return;
      }
      //注意:手势轻扫和鼠标滚动一格获取的event.angle取值有差异,根据用户设置鼠标滚轮上下滚动调整,存在鼠标向上滚动event.angle<0,具体根据pc鼠标设置调整
      if (event.angle > 0){
         //向上轻扫
      }else{
         //向下轻扫
      }
    }
  • H5适配指导

    1. 主要方法

    onSwipe:轻扫事件,onSwipe和onScroll不要同时使用,会存在冲突。

    2. 示例

    <template>
      <div id="testId" ref="testDom"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    const handleSwipe = (state: PointerState) => {
      // 代码编辑
    };
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onSwipe: handleSwipe, // 轻扫
      };
      const configs: PointerOptions = {
        enableSwipe: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 100%;
      height: 900px;
      background-color: aquamarine;
    }
    </style>

7、滚动/平移事件

  • RN适配指导

    1. 主要方法和参数

    Gesture.Pan():手势滚动对象。

    minDistance:number类型属性,触发拖动手势所需的最小距离。

    minPointers:number类型属性,触发拖动手势需要的手指数目。

    direction:string类型属性,定义手势的方向或方向组合, 可能的值有:'all', 'horizontal', 'vertical', 'left', 'right', 'up', 'down', 'none' 。也可以使用'&'和'|'组合。

    onStart:回调方法,手势拖动开始时的回调。

    onUpdate:回调方法,手势拖动变化时的回调。

    onCancel:回调方法,手势拖动取消时的回调。

    onEnd:回调方法,手势拖动结束时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    //手势组件包裹需要进行拖动的内容
    <GestureDetector gesture={panSongsList}>
        <Animated.View >
           {songsList.map((item, index) => (
                <View key={index} style={styles.item}>
                    <Text>1233333333333333</Text>
                 </View>
             ))}
         </Animated.View>
    </GestureDetector>
    
    const panSongsList = Gesture.Pan()
        .minDistance(150)
        .onStart(() => {
          console.log('开始拖动');
        })
  • Flutter适配指导

    1. 主要方法

    onPanStart:滚动开始。

    onPanUpdate:滚动中,根据event.offsetY判断向上滚动还是向下滚动。

    onPanEnd:滚动结束,重置页面行为,例如同页面轻扫功能在滚动的时候禁止,此时可以放开限制。

    onPanCancel:滚动取消。

    2. 回调参数

    event.velocityX:当前手势的x轴方向速度,单位为db/s,从左往右为正,反之为负。

    event.velocityY:当前手势的y轴方向速度,单位为db/s,从上而下为正,反之为负。

    event.offsetX:手势事件偏移量X,单位为vp,从左往右滑动offsetX为正,反之为负。

    event.offsetY:手势事件偏移量Y,单位为vp,从上往下滑动offsetY为正,反之为负。

    3. 示例

    UnifiedGestureDetector(
      pointerOptions: PointerOptions(
        speed: 150,
        direction: SwipeDirection.all,
        enableSwipe: false,
        enablePan: true,
        panDirection: panDirection,
      ),
      onPanStart: onPanStart,
      onPanUpdate: onPanUpdate,
      onPanEnd: onPanEnd,
      onPanCancel: onPanCancel,
      child:  //自定义滚动控件,可以自定义一个ListView实现列表滚动
    );
    void onPanStart(GestureEvent event) {}
    void onPanUpdate(GestureEvent event) {
      if (event.offsetX == null || event.offsetY == null) {
        return;
      }
      //约束滚动行为
      //上滑:y轴速度大于-600小于-15为上滑
      //下滑:y轴速度大于15且小于600为下滑
      if (event.velocityY! < 600 && event.velocityY! > -600) {
        if (event.velocityY! > 15 || event.velocityY! < -15) {
            if (event.offsetY! < 0) {
              //上滑:根据offsetY设置widget的offset
            } else {
              //下滑:根据offsetY设置widget的offset
            }  
        }
      }
    }
    void onPanEnd(GestureEvent event) {}
    void onPanCancel(GestureEvent event) {}
  • H5适配指导

    1. 主要方法

    onScroll:滚动事件,PC端根据滚轮的滚动或者手机端根据手势的移动进行触发,以此实现相应的滚动效果。

    2. 示例

    <template>
      <div id="testId" ref="testDom"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    const handleScroll = (state: PointerState) => {
      // 代码编辑
    };
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onScroll: handleScroll, // 轻扫
      };
      const configs: PointerOptions = {
        enableScroll: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 100%;
      height: 900px;
      background-color: aquamarine;
    }
    </style>

8、缩放事件

  • RN适配指导

    1. 主要方法和属性

    Gesture.Pinch():手势缩放对象。

    minDistance:number类型属性,触发缩放手势所需的最小距离。

    minPointers:number类型属性,触发缩放手势需要的手指数目。

    onStart:回调方法,手势缩放开始时的回调。

    onUpdate:回调方法,手势缩放变化时的回调。

    onCancel:回调方法,手势缩放取消时的回调。

    onEnd:回调方法,手势缩放结束时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    //给需要的缩放的图片绑定手势组件
    <GestureDetector gesture={pinchGesture}>
      <Animated.Image
        style={[styles.imageArtworkBig, animatedStyle]}
        source={require('../../../../asset/artwork.png')}
        resizeMode="contain"
      />
    </GestureDetector>
    
    let scale = useSharedValue(0.9);
    const savedScale = useSharedValue(0.9);
    const pinchGesture = Gesture.Pinch()
    .onStart(e => {
      // 处理缩放开始时的回调事件
    })
    .onUpdate(e => {
      // 处理缩放过程中的回调事件
    })
    .onEnd(() => {
      // 处理缩放结束时的回调事件
    });
  • Flutter适配指导

    1主要方法

    Transform:包裹需要缩放的控件,利用scale属性改变来控制控件的缩放比例。

    onPinchStart:缩放开始。

    onPinchUpdate:缩放中,获取缩放比例event.scale,获取的比例较大,可以根据需求适当调整缩放比例,示例中是判断放大还是缩小的行为,从而控制控件固定比例放大和缩小。

    onPinchEnd:缩放结束。

    2. 回调参数

    event.scale:缩放比例,用于PinchGesture手势触发场景,取值范围[0, +∞)。

    3. 示例

    UnifiedGestureDetector(
      pointerOptions: const PointerOptions(
        enablePinch: true,
      ),
      onPinchStart: onPinchStart,
      onPinchUpdate: onPinchUpdate,
      onPinchEnd: onPinchEnd,
      child: Transform(
        alignment: Alignment.center,
        transform: Matrix4.identity()
          ..translate(_offset.dx, _offset.dy)
          ..rotateZ(_rotation)
          ..scale(_scale),
        child: //需要缩放的widget
      ),
    );
    void onPinchStart(GestureEvent event) {}
    void onPinchUpdate(GestureEvent event) {
       if (event.scale! < 0) {
        setState(() {
          // 缩小效果:图片的scale-0.005,通过Transform的scale属性实现缩小
        });
      } else if (event.scale! > 0) {
        setState(() {
          // 放大效果:图片的scale+0.005,通过Transform的scale属性实现放大
        });
      }
      // 限制缩放范围:防止widget缩放太小或者太大,体验不好
      _scale = _scale.clamp(0.3, 1.0);
    }
    void onPinchEnd(GestureEvent event) {}
  • H5适配指导

    1. 主要方法

    onPinchStart:缩放开始事件,在执行这个事件会默认禁止旋转事件。

    onPinchMove:缩放中事件,通过event.scale缩放比例判断是放大还是缩小。

    onPinchEnd:缩放结束事件,默认放开旋转事件。

    2. 示例

    <template>
      <div id="testId" ref="testDom"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    const handlePinchStart = (state: PointerState) => {
      // 代码编辑
    };
    const handlePinchMove = (state: PointerState) => {
      // 代码编辑
    };
    const handlePinchEnd = (state: PointerState) => {
      // 代码编辑
    };
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onPinchStart: handlePinchStart, // 缩放开始
        onPinchMove: handlePinchMove, // 缩放中
        onPinchEnd: handlePinchEnd, // 缩放结束
      };
      const configs: PointerOptions = {
        enableScale: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 100%;
      height: 900px;
      background-color: aquamarine;
    }
    </style>

9、旋转事件

  • RN适配指导

    1. 主要方法和属性

    Gesture.Rotation():手势旋转对象。

    minAngle:number类型属性,触发旋转手势所需达到的最小角度。

    minPointers:number类型属性,触发旋转手势需要的手指数目。

    onStart:回调方法,手势旋转开始时的回调。

    onUpdate:回调方法,手势旋转变化时的回调。

    onCancel:回调方法,手势旋转取消时的回调。

    onEnd:回调方法,手势旋转结束时的回调。

    2. 示例

    import { GestureDetector, Gesture} from '@hadss/react-native-uniinput';
    
    //给需要旋转的的图片绑定手势组件
    <GestureDetector gesture={rotationGestureForBackGround}>
      <Animated.Image
        style={[styles.imageArtworkBig, animatedStyle]}
        source={require('../../../../asset/artwork.png')}
        resizeMode="contain"
      />
    </GestureDetector>
    
    const rotation = useSharedValue(0);
    const savedRotation = useSharedValue(0);
    // 创建旋转手势
    const rotationGestureForBackGround = Gesture.Rotation()
      .onStart(() => {
        // 处理旋转开始时的回调事件
      })
      .onUpdate(e => {
        // 处理旋转过程中的回调事件
      })
      .onEnd(() => {
        // 处理旋转结束后的回调事件
      });
  • Flutter适配指导

    1. 主要方法

    Transform:包裹需要旋转的控件,利用rotateZ属性改变来控制控件的旋转角度。

    onRotateStart:旋转开始。

    onRotateUpdate:旋转中,获取旋转角度event.angle。

    onRotateEnd:旋转结束。

    2. 回调参数

    event.angle:RotationGesture手势触发场景时,表示旋转角度,顺时针旋转为0度到180度,逆时针旋转为-180度到0度。

    3. 示例

    UnifiedGestureDetector(
      pointerOptions: const PointerOptions(
          enableRotate: true,
      ),
      onRotateStart: onRotateStart,
      onRotateUpdate: onRotateUpdate,
      onRotateEnd: onRotateEnd,
      child: Transform(
        alignment: Alignment.center,
        transform: Matrix4.identity()
          ..translate(_offset.dx, _offset.dy)
          ..rotateZ(_rotation)
          ..scale(_scale),
        child: //需要旋转的widget
      ),
    );
    void onRotateStart(GestureEvent event) {}
    void onRotateUpdate(GestureEvent event) {
      setState(() {
        //赋值_ratation,通过Transform的rotateZ属性实现widget的旋转功能
        _rotation = event.angle!;
      });
    }
    void onRotateEnd(GestureEvent event) {}
  • H5适配指导

    1. 主要方法

    onRotateStart:旋转开始事件,在执行这个事件会默认禁止缩放事件。

    onRotateMove:缩放中事件,通过event.angle旋转角度判断是正向旋转还是逆向旋转。

    onRotateEnd:旋转结束事件,默认放开缩放事件。

    2. 示例

    <template>
      <div id="testId" ref="testDom"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, onUnmounted, ref } from "vue";
    import {
      PointerGestureManager,
      PointerOptions,
      GestureHandlers,
      PointerState,
    } from "@hadss/web_uni_input";
    const testDom = ref<HTMLElement | null>(null);
    const instance = ref(null);
    const handleRotateStart = (state: PointerState) => {
      // 代码编辑
    };
    const handleRotateMove = (state: PointerState) => {
      // 代码编辑
    };
    const handleRotateEnd = (state: PointerState) => {
      // 代码编辑
    };
    onMounted(() => {
      // 如果要获取DOM元素,请在DOM元素挂载之后获取,比如VUE3生命周期的onMounted中获取DOM元素
      const handlers: GestureHandlers = {
        onRotateStart: handleRotateStart, // 旋转开始
        onRotateMove: handleRotateMove, // 旋转中
        onRotateEnd: handleRotateEnd, // 旋转结束
      };
      const configs: PointerOptions = {
        enableRotate: true,
      };
      if (testDom.value) {
        instance.value = PointerGestureManager(testDom.value, handlers, configs);
      }
    });
    onUnmounted(() => {
      // destroy销毁实例,实例销毁以后交互归一事件不再触发
      if (instance.value) {
        instance.value.destroy();
      }
    });
    </script>
    <style scoped>
    #testId {
      width: 100%;
      height: 900px;
      background-color: aquamarine;
    }
    </style>

按键事件适配

RN按键适配

ViewWithKeyEvent :按键组件。

onKeyEvent:触发按键事件时的回调。

eventType:对象属性,按键类型,值:unknown、up、down。

keyCode:按键的键码。

keyValue:按键的键值。

keySourceType:按键输入设备类型,值:unknown、mouse、keyboard、joystick。

import { ViewWithKeyEvent } from '@hadss/react-native-uniinput';

//  ViewWithKeyEvent 组件包裹按键元素
<ViewWithKeyEvent onKeyEvent={downKeyEvent}>
   <View>
     <Text>按键</Text>
   <View>
 </ViewWithKeyEvent>

const downKeyEvent = event => {
  if (!event || !event.nativeEvent || event.nativeEvent.eventType !== 'up') {
    // 按键有按下、抬起两种事件类型,此处取类型为抬起的手势做处理。
    return;
  }
  const keyValue = event.nativeEvent.keyValue || '';
  console.log('event:', event.nativeEvent);
  switch (keyValue) {
    case 'KEYCODE_DPAD_UP':
      //上键:切换歌曲为上一曲
      skipToPrevious();
      break;
    case 'KEYCODE_DPAD_DOWN':
      //下键:切换歌曲为下一曲
      skipToNext();
      break;
    case 'KEYCODE_DPAD_LEFT':
      //左键:控制播放器快退5s播放
      if (position - 5 >= 0) {
        setPosition(position - 5);
        seekTo(position - 5);
      } else {
        setPosition(0);
        seekTo(0);
      }
      break;
    case 'KEYCODE_DPAD_RIGHT':
      //右键:控制播放器快进5s播放
      if (position + 5 <= duration) {
        setPosition(position + 5);
        seekTo(position + 5);
      } else {
        setPosition(duration);
        seekTo(duration);
      }
      break;
    case 'KEYCODE_ENTER':
      //enter:当歌曲暂停状态时,按下enter键歌曲开始播放
      togglePlayPause();
      break;
    case 'KEYCODE_ESCAPE':
      //esc:回到上一级页面
      changePageShow(true);
      break;
    default:
      break;
  }
};

Flutter按键适配

通过RawKeyboardListener监听键盘行为。

RawKeyboardListener(
  focusNode: FocusNode(debugLabel: 'global_raw_keyboard'), // 确保焦点节点存在且可用
  onKey: (RawKeyEvent event) {
    if (event is RawKeyDownEvent) {
      if (event.logicalKey == LogicalKeyboardKey.escape) {
        //esc:回到上一级页面
        Navigator.pop(context);
      } else if (event.logicalKey == LogicalKeyboardKey.space) {
        //空格:当歌曲播放状态时,按下空格键歌曲暂停
      }else if (event.logicalKey == LogicalKeyboardKey.enter) {
        //enter:当歌曲暂停状态时,按下enter键歌曲开始播放
      }else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
        //上键:切换歌曲为上一首
      }else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
        //下键:切换歌曲为下一首
      }else if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
        //左键:控制播放器快退5s播放
      }else if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
        //右键:控制播放器快进5s播放
      }
    }
  },
  child: Container(...) //此处是需要监控的主页面widget;
)

H5按键适配

通过document.addEventListener('keyup', (event) => { })监听键盘行为。

document.addEventListener("keyup", (event) => {
  switch (event.key) {
    case "ArrowUp":
      //上键:切换歌曲为上一曲
      break;
    case "ArrowDown":
      //下键:切换歌曲为下一曲
      break;
    case "ArrowLeft":
      //左键:控制播放器快退5s播放
      break;
    case "ArrowRight":
      //右键:控制播放器快进5s播放
      break;
    case "Enter":
      //enter:当歌曲暂停状态时,按下enter键歌曲开始播放
      break;
    case "Escape":
      //esc:回到上一级页面
      break;
    case " ":
      //空格:当歌曲播放状态时,按下空格键歌曲暂停
      break;
  }
})

组件及sample地址

RN:rn_multidevice_layout_scenepkg,Flutter:flutter_multidevice_layout_scenepkg,H5:web_adaptive_ui

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐