Soybean Admin 使用tv-focusable兼容电视TV端支持遥控器移动焦点

环境

window10
pnpm 8.15.4
node 8.15.4
vite 5.1.4
soybean admin: 1.0.0
native-ui: 2.38.0
vue-tv-focusable: 2.0.1
小米电视 MIUI TV版本:MiTV OS 2.7.1886(稳定版)
飞视浏览器:https://www.fenxm.com/1220.html

这里必须使用飞视浏览器,其它浏览器都不行,在“测试”步骤会告诉你原因。在小米电视安装飞视浏览器可以去小红书查安装教程:苹果手机小米电视安装第三方 app 教程

描述

其实最好的做法还是安卓开发,前端网页开发远没有安卓开发那么方便的去管理焦点,不过简单支持还是能做到的!

大部分的电视浏览器都支持模拟鼠标,就是在浏览器打开一个网页,会出现一个蓝色鼠标,可以通过遥控器去移动这个鼠标,点遥控器的OK键就是鼠标单击键

但是客户反馈说,无法点击登录按钮,点了没有反应。那这时候要借助 vue 插件去实现获取登录按钮的焦点了,不能用浏览器自带的模拟鼠标

实现

找了一圈,找到了一个 vue-tv-focusable 插件。如果有更好用的,欢迎评论区补充

vue-tv-focusable csdn
vue-tv-focusable 官方文档
vue-tv-focusable 案例
vue-tv-focusable 案例源码

安装插件

pnpm i vue-tv-focusable --save

引入插件

新建一个插件文件,位置:src\plugins\tvfocusable.ts

import type { App } from 'vue';
import focusable from 'vue-tv-focusable'

export function setupTvFocusable(app: App) {
  
  // 1.注册
  app.use(focusable);

  // 2.初始化配置
  app.config.globalProperties.$tv.init({
    focusClassName: 'tv-focus',
  })
  
}

引入插件,位置:src\main.ts

import 'core-js/stable';
import 'regenerator-runtime/runtime'; // 如果你的代码使用了生成器(Generator),你也需要这个 Polyfill
import { createApp } from 'vue';
import './plugins/assets';
import { setupDayjs, setupIconifyOffline, setupLoading, setupNProgress, setupTvFocusable } from './plugins';
import { setupStore } from './store';
import { setupRouter } from './router';
import { setupI18n } from './locales';
import App from './App.vue';

async function setupApp() {
  setupLoading();

  setupNProgress();

  setupIconifyOffline();

  setupDayjs();

  const app = createApp(App);

  setupStore(app);

  setupTvFocusable(app); // 这里

  await setupRouter(app);

  setupI18n(app);

  app.mount('#app');
}

setupApp();

引入完成之后,使用插件,实现聚焦登录按钮

<NButton
  v-focusable
   class="flex-1"
   block
   @click="toggleLoginModule('pwd-login')"
 >
   {{ $t(loginModuleRecord["pwd-login"]) }}
</NButton>

仅仅使用 v-focusable 指令还不够,还要写聚焦样式,不然登录按钮被选中都不知道。有两种方式,

一个是全局聚焦样式,位置:src\styles\css\global.css

.tv-focus {
  transform: scale(1.01);
  border: 1px solid #FF9933;
  box-shadow: 0 0 20px #FF9933;
}

一个是局部聚焦样式

<style scoped>
.tv-focus {
  /* 电视用遥控器选择或者电脑键盘选择的时候,能给聚焦对象一个放大+灰色阴影的聚焦效果 */
  transform: scale(1.02);
  box-shadow: 0 0 15px rgb(207, 207, 207);
}
</style>

看需求去选择,我这里直接全局设置元素聚焦样式(橙色边框,橙色阴影,放大效果)。

那么如何去测试呢,开发环境不可能一直打开电视去测试,这里我们可以用键盘事件模拟遥控器事件:

键盘遥控器
enter键OK键
方向键方向键

看看测试效果

在这里插入图片描述
下面是实现输入框聚焦

<script setup lang="ts">
function handleUserNameClick() {
  const el: HTMLInputElement | null = document.querySelector(".tv-focus input");
  if (el) el.focus();
}

function handleUserNameBlur() {
  const el: HTMLInputElement | null = document.querySelector(".tv-focus input");
  if (el) el.blur();
}

function handlePasswordClick() {
  const el: HTMLInputElement | null = document.querySelector(".tv-focus input");
  if (el) el.focus();
}

function handlePasswordBlur() {
  const el: HTMLInputElement | null = document.querySelector(".tv-focus input");
  if (el) el.blur();
}
</script>
<template>
	...
	<NFormItem path="userName">
      <NInput
        v-focusable
        v-model:value="model.userName"
        :placeholder="$t('page.login.common.userNamePlaceholder')"
        @click="handleUserNameClick"
        @on-blur="handleUserNameBlur"
      />
    </NFormItem>
    <NFormItem path="password">
      <NInput
        v-focusable
        v-model:value="model.password"
        type="password"
        show-password-on="click"
        :placeholder="$t('page.login.common.passwordPlaceholder')"
        @click="handlePasswordClick"
        @on-blur="handlePasswordBlur"
      />
    </NFormItem>
    ...
</template>

除了加入 v-focusable指令,还监听了输入框的 click事件、on-blur事件

来看看测试效果

在这里插入图片描述
输入账号密码登录进去之后,到了首页,还有按钮聚焦(全屏按钮,切换语言按钮,切换主题按钮),这个就不重复代码了,都是按钮聚焦。

在这里插入图片描述

下面实现下拉菜单的聚焦,按理说电视app根本不会做这种交互,但是这里还是可以分享一下做法

src\components\common\lang-switch.vue

<script setup lang="ts">
import type { VNode } from 'vue';
import type { DropdownOption, DropdownGroupOption } from "naive-ui";
import { computed, h, getCurrentInstance } from 'vue';
import { $t } from '@/locales';
import FocusableWrapper from "@/components/custom/focusable-wrapper.vue";

defineOptions({
  name: 'LangSwitch'
});

interface Props {
  /** Current language */
  lang: App.I18n.LangType;
  /** Language options */
  langOptions: App.I18n.LangOption[];
  /** Show tooltip */
  showTooltip?: boolean;
}

const { proxy, ctx } = getCurrentInstance();

const props = withDefaults(defineProps<Props>(), {
  showTooltip: true
});

type Emits = {
  (e: 'changeLang', lang: App.I18n.LangType): void;
};

const emit = defineEmits<Emits>();

const tooltipContent = computed(() => {
  if (!props.showTooltip) return '';

  return $t('icon.lang');
});

const renderOption = (props: { node: VNode, option: DropdownOption | DropdownGroupOption }) => {
  return h(FocusableWrapper,
    {
      class: "tv-focus-lang-dropdown-option"
    },
    [
      h(props.node)
    ]
  )
}

function changeLang(lang: App.I18n.LangType) {
  emit('changeLang', lang);
}

function handleUpdateShow(value: boolean) {
  if (value === true) {
    // 下拉菜单开启状态
    const oDropdown = document.querySelector(".tv-focus-lang-dropdown");
    proxy.$tv.limitingEl = oDropdown; // 点击浮层,限制只能在下拉浮层里移动焦点
  } else {
    proxy.$tv.resetLimitingEl(); // 收起浮层,解除限制
  }
}
</script>

<template>
  <NDropdown class="tv-focus-lang-dropdown" :value="lang" :options="langOptions" trigger="click"
    :render-option="renderOption" :on-update:show="handleUpdateShow" @select="changeLang">
    <div>
      <ButtonIcon focusable :tooltip-content="tooltipContent" tooltip-placement="left">
        <SvgIcon icon="heroicons:language" />
      </ButtonIcon>
    </div>
  </NDropdown>
</template>

<style scoped></style>

src\components\custom\focusable-wrapper.vue

<script setup lang="ts">
defineOptions({
  name: "FocusableWrapper",
});

</script>

<template>
  <div v-focusable v-bind="$attrs">
    <slot></slot>
  </div>
</template>

主要通过renderOption 这属性给下拉菜单的菜单项去设置元素焦点, 然后 handleUpdateShow 方法去限制焦点的范围。

在聚焦全屏按钮,并使用全屏按钮的时候遇到了问题:当页面全屏标签页内容时,摁方向键全部元素都无法聚焦

具体什么原因还分析不出来,全屏后,不论怎么摁键盘的方向键,所有元素都失去了聚焦效果,解决方案如下:

src\layouts\modules\global-tab\index.vue

<script setup lang="ts">
import { nextTick, reactive, ref, watch, getCurrentInstance } from 'vue';

const { proxy } = getCurrentInstance();

watch(
  () => appStore.fullContent,
  async (val) => {
    if (val === true) {
      // 全屏状态
      // fix: 解决全屏标签页内容时按方向键全部元素都无法聚焦的问题
      // 找不到原因,莫名其妙无法聚焦,手动用next聚焦
      await nextTick();
      const oBtn = document.querySelector("#tv-tab-fullscreen-btn");
      proxy.$tv.next(oBtn);
    }
  },
  { immediate: true }
);
</script>

<template>
	...
	<FullScreen id="tv-tab-fullscreen-btn" :full="appStore.fullContent" @click="appStore.toggleFullContent" />
	...
</template>

简单兼容电视TV端遥控器交互,常用的基本就是以上的场景。


### 测试

前面提供了飞视浏览器的下载地址,需要关闭鼠标模拟才能聚焦成功。经过测试发现只有飞视浏览器才有这个关闭模拟鼠标的设置,其它浏览器没有这样的设置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置完了,在地址栏输入访问地址,使用遥控器查看你的元素聚焦效果

最后

做了一大通操作,才能支持兼容电视TV端,而且只能在飞视浏览器访问项目,很局限,有条件还是安卓原生开发

博客分享了vue-tv-focusable 插件,实现按钮聚焦、输入框聚焦、下拉菜单聚焦,解决了当页面全屏标签页内容时,摁方向键全部元素都无法聚焦的问题。有疑问或者遇到什么问题可以评论区补充,感谢观看

内容概要:本文详细探讨了制造业工厂中两条交叉轨道(红色和紫色)上的自动导引车(AGV)调度问题。系统包含2辆红色轨道AGV和1辆紫色轨道AGV,它们需完成100个运输任务。文章首先介绍了AGV系统的背景和目标,即最小化所有任务的完成时间,同时考虑轨道方向性、冲突避免、安全间隔等约束条件。随后,文章展示了Python代码实现,涵盖了轨道网络建模、AGV初始化、任务调度核心逻辑、电池管理和模拟运行等多个方面。为了优化调度效果,文中还提出了冲突避免机制增强、精确轨道建模、充电策略优化以及综合调度算法等改进措施。最后,文章通过可视化与结果分析,进一步验证了调度系统的有效性和可行性。 适合人群:具备一定编程基础和对自动化物流系统感兴趣的工程师、研究人员及学生。 使用场景及目标:①适用于制造业工厂中多AGV调度系统的开发与优化;②帮助理解和实现复杂的AGV调度算法,提高任务完成效率和系统可靠性;③通过代码实例学习如何构建和优化AGV调度模型,掌握冲突避免、路径规划和电池管理等关键技术。 其他说明:此资源不仅提供了详细的代码实现和理论分析,还包括了可视化工具和性能评估方法,使读者能够在实践中更好地理解和应用AGV调度技术。此外,文章还强调了任务特征分析的重要性,并提出了基于任务特征的动态调度策略,以应对高峰时段和卸载站拥堵等情况。
内容概要:本文介绍了一个使用MATLAB编写的基于FDTD(时域有限差分)方法的电磁波在自由空间中传播的仿真系统。该系统采用了ABC(吸收边界条件)和正弦脉冲激励源,并附有详细的代码注释。文中首先介绍了关键参数的选择依据及其重要性,如空间步长(dx)和时间步长(dt),并解释了它们对算法稳定性和精度的影响。接着阐述了电场和磁场的初始化以及Yee网格的布局方式,强调了电场和磁场分量在网格中的交错排列。然后详细讲解了吸收边界的实现方法,指出其简单而有效的特性,并提醒了调整衰减系数时需要注意的问题。最后,描述了正弦脉冲激励源的设计思路,包括脉冲中心时间和宽度的选择,以及如何将高斯包络与正弦振荡相结合以确保频带集中。此外,还展示了时间步进循环的具体步骤,说明了磁场和电场分量的更新顺序及其背后的物理意义。 适合人群:对电磁波传播模拟感兴趣的科研人员、高校学生及工程技术人员,尤其是那些希望深入了解FDTD方法及其具体实现的人群。 使用场景及目标:适用于教学演示、学术研究和技术开发等领域,旨在帮助使用者掌握FDTD方法的基本原理和实际应用,为后续深入研究打下坚实基础。 阅读建议:由于本文涉及较多的专业术语和技术细节,建议读者提前熟悉相关背景知识,如电磁理论、MATLAB编程等。同时,可以通过动手实践代码来加深理解和记忆。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值