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