Skip to content

触控功能

JsView中的触控功能,设计思路是js定规则后尽最大可能减少触控处理过程中和js的交互,以提升触控处理中drag/fling的性能。 (TBD: 当前版本PC浏览器模拟环境上的click/drag/fling暂时未实现)

1. 普通div元素上追加点击支持

在div元素上追加 @click="回调函数" 即可

例如:

vue
  <script setup>
  /* 声明click的回调函数 */
  const click_callback = ()=>{
    console.log("on clicked");
  }
  <script/>

  <template>
    <div 
      :style="{
        width:100, 
        height:100, 
        backgroundColor: '#FF00FF'}"
      @click="click_callback"/>
  </template>

2. 普通元素中追加长按的响应

通过element的ref句柄,调用 jsvSetTapListener 函数来设置,具体参考

Element的jsvSetTapListener

3. MetroWidget支撑的列表触控处理(drag/fling/click)

列表触控由 JsvTouchModeSwitcher + MetroWidgettouchFlag 共同完成。

3.1 JsvTouchModeSwitcher

JsvTouchModeSwitcher 用于按键模式 / 触控模式的自动切换与监听。将其作为页面内容的包裹层后:

  • 用户触摸屏幕 → 进入触控模式(TOUCH_MODE = 2
  • 用户按下指定按键(默认方向键)→ 切回按键模式(KEY_MODE = 1
  • 子组件可通过回调感知模式变化,据此控制焦点框显示、缩放等 UI

该组件会维护全局交互模式 JsvInteractionModeMetroWidgettouchFlag > 0 时,只有检测到页面存在 JsvTouchModeSwitcher,才会真正开启触控相关能力。

引入

js
import { JsvTouchModeSwitcher } from "jsview";

模板中标签名为:jsv-touch-mode-switcher

基本用法

建议放在页面根内容外层,width / height 与可视区域一致:

vue
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { JsvTouchModeSwitcher } from "jsview";

const switcherRef = ref();

const onModeChange = (mode) => {
  // mode: 1 = 按键, 2 = 触控
  console.log("当前模式:", mode === 1 ? "key" : "touch");
};

onMounted(() => {
  switcherRef.value.registerCallback(onModeChange);
});

onBeforeUnmount(() => {
  switcherRef.value.unregisterCallback(onModeChange);
});
</script>

<template>
  <jsv-touch-mode-switcher
    ref="switcherRef"
    :width="1280"
    :height="720"
  >
    <!-- 页面内容 -->
    <metro-widget :touchFlag="1" ... />
  </jsv-touch-mode-switcher>
</template>

Props

属性类型默认值说明
widthNumber容器宽度,建议与页面可视宽一致
heightNumber容器高度,建议与页面可视高一致
initModeNumber1初始模式:1 按键,2 触控
changeModeKeyListArray[37, 38, 39, 40]从触控切回按键的按键列表(默认左/上/右/下)

实例方法(通过 ref 调用)

方法签名说明
registerCallback(callback: (mode: number) => void) => void注册模式变化回调
unregisterCallback(callback: (mode: number) => void) => void注销回调(必须与 register 成对使用)
getInteractiveMode() => number获取当前模式:1 按键,2 触控
changeMode(mode: "key" | "touch") => void主动切换模式

模式常量

  • KEY_MODE = 1(按键)
  • TOUCH_MODE = 2(触控)

模式切换规则

  • 触控 → 按键:在触控模式下,按下 changeModeKeyList 中的键会切回按键模式。该次按键不会继续向下传递(用于切换模式,不触发焦点移动等业务逻辑)。
  • 按键 → 触控:在 Switcher 包裹区域内触发 touchStart 时,自动进入触控模式。
  • 主动切换
js
switcherRef.value.changeMode("key");   // 切到按键
switcherRef.value.changeMode("touch"); // 切到触控

切换时会同步调用 window.JsView.changeInteractionMode,并更新全局 JsvInteractionMode

3.2 与 MetroWidget 配合

开启列表触控时,需要同时满足:

  1. 页面使用 JsvTouchModeSwitcher 包裹
  2. MetroWidget 设置 :touchFlag="1"

仅设置 touchFlag > 0 不够;内部还会检查是否存在 JsvTouchModeSwitcher,两者同时满足才会启用 drag / fling 等触控行为。

vue
<jsv-touch-mode-switcher
  ref="switcherRef"
  :width="1280"
  :height="720"
>
  <metro-widget
    :touchFlag="1"
    :mouseFlag="1"
    ...
  />
</jsv-touch-mode-switcher>

MetroWidget 相关 props:

js
// props
//   touchFlag {int} 触控的开关,
//       0:关闭, 1:打开. 打开后支持drag, fling;
//       另外,item要支持点击(click)时,要在measure回调函数中,对measuresObject的追加enableTap:true
//   mouseFlag {int} 支持鼠标滚轮的开关, 0:关闭, 1:打开, 打开后可以用鼠标滚轮来对mw进行scroll

从触控切回按键后如何重新落焦,见 第 9 节

4. 长文本滚动

使用JsvScrollBox和JsvScrollFollow组件来完成。可以参考“功能实例->长文字”的样例(如下)

node_modules/@shijiu/jsview-vue-samples/LongText/LongTextScroll.vue

因为是整篇文字是一部分可见,所以用JsvScrollBox的抽屉模式(drawer)

这两个组件的参数说明见:
node_modules/@shijiu/jsview-vue/utils/JsViewVueWidget/JsvScrollBox/JsvScrollBox.vue
node_modules/@shijiu/jsview-vue/utils/JsViewVueWidget/JsvScrollBox/JsvScrollFollow.vue

5. 进度条拖动(视频进度条)

使用JsvScrollBox和JsvScrollFollow组件来完成。可以参考“触控->可拖拽进度条”的样例(如下)

node_modules/@shijiu/jsview-vue-samples/ScrollBoxTest/SizeDivBar.vue
node_modules/@shijiu/jsview-vue-samples/ScrollBoxTest/ClipBar.vue
node_modules/@shijiu/jsview-vue-samples/ScrollBoxTest/NinePatchBar.vue

因为是进度条游标在滚动区域中滑动,所以用JsvScrollBox的Pin模式

这两个组件的参数说明见:
node_modules/@shijiu/jsview-vue/utils/JsViewVueWidget/JsvScrollBox/JsvScrollBox.vue
node_modules/@shijiu/jsview-vue/utils/JsViewVueWidget/JsvScrollBox/JsvScrollFollow.vue

6. div拖动和对齐检测

使用JsvDragBox组件来完成此功能,可参考"触控->DragBox"的样例(如下)

node_modules/@shijiu/jsview-vue-samples/DragBox/App.vue

使用此组件时,注意 setSensor 的敏感度设置,越小性能开销越大(js是单线程,过于频繁的消息和回调会导致其他任务无法执行)

此组件的参数说明见:
node_modules/@shijiu/jsview-vue/utils/JsViewVueWidget/JsvDragBox/JsvDragBox.vue

7. 鼠标的hover进出检测

在div元素上追加 @mouseenter="回调函数" 和 @mouseleave="回调函数" 即可

例如:

vue
  <script setup>
  /* 声明click的回调函数 */
  const click_callback = ()=>{
    console.log("on clicked");
  }
  <script/>

  <template>
    <div 
      :style="{
        width:100, 
        height:100, 
        backgroundColor: '#FF00FF'}"
      @mouseenter="mouseenter_callback"
      @mouseleave="mouseleave_callback"
      />
  </template>

8. 使用蒙层屏蔽下层元素的触控与鼠标事件

在需要临时禁止用户操作(例如弹窗、加载中、全屏遮罩)时,可在界面上方叠一层全屏或局部的 蒙层 div,使其盖住下层可交互元素。蒙层本身通过 jsvSetTapListenerjsvConsumeMouseEvent 吞掉触控与鼠标事件,下层元素便不会收到 click、长按、hover、滚轮等响应。

实现要点:

  • 蒙层需覆盖待屏蔽区域,并设置合适DOM层级,使其位于下层元素之上。
  • 触控:对蒙层调用 jsvSetTapListener,为各触控回调注册空实现,避免事件继续下发。
  • 鼠标:对蒙层调用 jsvConsumeMouseEvent({ hover: true, scroll: true }),屏蔽 hover 与滚轮;蒙层挡住区域内的 MetroWidget 等下层控件的鼠标事件同样会被挡住。

API 说明见:
Element 的 jsvSetTapListener / jsvConsumeMouseEvent

例如(Vue 3,使用 ref 拿到蒙层 div 句柄 overlay):

vue
<script setup>
import { ref, onMounted } from "vue";

const overlay = ref(null);

// 空回调,用于吞掉触控事件
const emptyListener = () => {};

onMounted(() => {
  if (!overlay.value) return;

  let emptyListener = ()=>{};
  // 屏蔽所有触控事件
  overlay.value.jsvSetTapListener({
    onClick: emptyListener,
    onTouchStart: emptyListener,
    onTouchEnd: emptyListener,
    onLongPress: emptyListener,
    onLongPressRelease: emptyListener,
    onCancel: emptyListener,
  });

  // 屏蔽所有鼠标 hover 与滚轮事件
  overlay.value.jsvConsumeMouseEvent({ hover: true, scroll: true });
});
</script>

<template>
  <!-- 下层:正常可点击、可 hover 的内容 -->
  <div :style="{ width: 1920, height: 1080 }">
    <!-- ... -->
  </div>

  <!-- 蒙层:盖住下层,屏蔽触控与鼠标事件 -->
  <div
    ref="overlay"
    :style="{
      width: 1920,
      height: 1080,
      backgroundColor: 'rgba(0,0,0,0.5)',
    }"
  />
</template>

需要恢复交互时,移除或隐藏蒙层即可;若蒙层长期存在,可在显示/隐藏时分别调用上述 API 与对应清理逻辑(按业务需要处理)。

9. Touch 模式切换到 Key 模式时的焦点定位

触控操作后切回按键(Key)模式时,需要重新确定焦点应落在哪个可见、可获焦的 item 上。对相关 MetroWidget 调用 getItemVisibleState,根据返回的可视状态树选择落焦目标。

getItemVisibleState

获取指定区间内可视 item 的可见状态树(支持 MetroWidget 嵌套场景)。

  • 传入的 leftTop / rightBottom 已是真实区域坐标,无需再减去 padding。
  • 会按滚动方向取区间与 item 求交,仅返回在区间内可见(含部分可见)的 item。
  • 对嵌套的子 MetroWidget 会递归查询,结果放入 children
  • 当前 gaze(落焦/注视)对应的 item 会排在返回列表最前。

参数

参数类型说明
leftTop{x: number, y: number}查询区域左上角
rightBottom{x: number, y: number}查询区域右下角

返回值

array,每项结构为:

字段类型说明
namestring当前 MetroWidget 的 name
indexnumberitem 的 index
visibleState"partial" | "full"部分可见或完全可见
childrenarray嵌套子 MetroWidget 的可见状态列表,无嵌套时为空数组
widgetHandlerobject当前 MetroWidget 的 handler
focusablebooleanitem 是否可获焦

落焦策略(业务自选)

拿到可见状态列表后,可按业务需要择一处理(落焦前请结合 focusable 判断):

  1. Gaze 优先:取返回列表第一项。API 已将当前 gaze 对应的 item 排在最前,适合希望尽量贴近触控前落焦/注视位置的场景。
  2. 完全可见优先:在列表中筛选 visibleState === "full" 的项,再取其中第一项(或嵌套树中业务认定的优先项)。适合希望焦点落在完整可见 item 上的场景。

嵌套场景下,可沿 children 递归,直到选中叶子 item,再通过对应 widgetHandler / name + index 完成落焦。

10. 支持鼠标滚轮

JsView 对鼠标滚轮(含触控板滚动)的支持,主要由列表组件 MetroWidget / ListWidget、滚动组件 JsvScrollBox,以及可选的全局倍率配置共同完成。需要临时屏蔽滚轮时,可对蒙层调用 jsvConsumeMouseEvent(见 第 8 节)。

10.1 MetroWidget / ListWidget:列表滚轮滚动

通过 mouseFlag 打开列表的鼠标滚轮滚动;MetroWidget 还可用 mouseConfig.wheelRate 调节滚动速度。

MetroWidget props

属性类型默认值说明
mouseFlagNumber0鼠标滚轮开关:0 关闭,1 打开;打开后可用滚轮对列表 scroll
mouseConfigObject仅在 mouseFlag === 1 时生效
mouseConfig.wheelRateNumber10鼠标滚轮滚动速度
vue
<metro-widget
  :touchFlag="1"
  :mouseFlag="1"
  :mouseConfig="{ wheelRate: 10 }"
  ...
/>

ListWidget

ListWidget(单列/单行 MetroWidget)同样提供 mouseFlag0 关闭 / 1 打开),用法与 MetroWidget 一致。

10.2 JsvScrollBox:滚动区域滚轮

长内容抽屉滚动、视频进度条等场景,通过 JsvScrollBox 的滚轮开关启用:

属性类型默认值说明
enableMouseWheelBooleanfalse是否支持鼠标滚轮滚动(非响应式)
mouseWheelRateNumber10鼠标滚轮滚动速度比率(非响应式)
vue
<jsv-scroll-box
  :direction="VERTICAL"
  :mode="ScrollBoxStyle.DrawerMode"
  :enableFling="true"
  :enableMouseWheel="true"
  :mouseWheelRate="10"
  :style="{ width: 1000, height: 500 }"
  :sliderSize="{ height: scrollHeight }"
>
  <template #SliderBox>
    <!-- 长内容 -->
  </template>
</jsv-scroll-box>

组件参数完整说明见:
node_modules/@shijiu/jsview-vue/utils/JsViewVueWidget/JsvScrollBox/JsvScrollBox.vue

10.3 屏蔽滚轮事件

对蒙层或指定元素调用(常用于在遮挡层/蒙层上屏蔽滚轮事件,避免下层组件仍接收滚轮事件而产生误操作):

js
element.jsvConsumeMouseEvent({ scroll: true }); // 仅屏蔽滚轮
// 或
element.jsvConsumeMouseEvent({ hover: true, scroll: true }); // 同时屏蔽 hover 与滚轮

API 说明见:Element 的 jsvConsumeMouseEvent

10.4 全局滚轮倍率

可通过 JsView.setRenderGlobalConfig 设置全局滚轮/触控板滚动倍率,与视图级滚动速率(如 wheelRate / mouseWheelRate)相乘:

js
JsView.setRenderGlobalConfig({
  wheelRate: 1, // 默认 1
});

详见:setRenderGlobalConfig

10.5 相关样例(Vue)

可在 DemoHomepage 的 router 中按路径定位(以下为 Vue 样例):

样例router 路径说明关键用法
长文字/feature/LongTextJsvScrollBox 典型场景:长文本 + 右侧进度条联动,支持触控拖动 / fling / 进度条点击跳转 / 鼠标滚动:enableMouseWheel="true"
长图片/Operations/LongImage长图滚动,同样基于 JsvScrollBox:enableMouseWheel="true"
触控示例/touch/TouchWidgetMetroWidget 开启滚轮;OK 键可开蒙层,屏蔽下层触控与鼠标(含滚轮):mouseFlag="1";蒙层 jsvConsumeMouseEvent({ hover: true, scroll: true })

对应源码:

  • node_modules/@shijiu/jsview-vue-samples/LongText/LongTextScroll.vue
  • node_modules/@shijiu/jsview-vue-samples/LongImage/LongImageScroll.vue
  • node_modules/@shijiu/jsview-vue-samples/TouchWidget/App.vue
  • node_modules/@shijiu/jsview-vue-samples/TouchWidget/WidgetItem.vue(嵌套 MetroWidget 同样 :mouseFlag="1"