基于H5框架的多设备开发指导
一、概述
在使用H5进行应用页面开发时,开发者往往会面临一多适配的挑战。当前不仅缺乏拿来即用的方案,而且适配过程复杂、工作量大。为达到HarmonyOS一多体验,本文将介绍一套面向多设备适配的H5响应式布局方案,涵盖了自适应显隐、栅格、侧边栏和分栏四部分响应式能力,帮助开发者在保证体验一致性的同时提升多设备适配开发效率。
本文针对自适应显隐、栅格、侧边栏和分栏四个组件进行详细的说明,结合组件效果提供对应场景的开发案例,指导开发实践。
组件库安装
根据您使用的包管理器和H5框架,请选择对应的安装命令。
NPM
// Vue2
npm install @hadss/web_adaptive_layout_vue2
npm install @hadss/web_adaptive_layout_ui
// Vue3
npm install @hadss/web_adaptive_layout_vue3
npm install @hadss/web_adaptive_layout_ui
// React
npm install @hadss/web_adaptive_layout_react
npm install @hadss/web_adaptive_layout_ui
YARN
// Vue2
yarn add @hadss/web_adaptive_layout_vue2
yarn add @hadss/web_adaptive_layout_ui
// Vue3
yarn add @hadss/web_adaptive_layout_vue3
yarn add @hadss/web_adaptive_layout_ui
// React
yarn add @hadss/web_adaptive_layout_react
yarn add @hadss/web_adaptive_layout_ui
二、自适应显示和隐藏实现多设备适配
引入显隐能力管理类DisplayPriorityLayout,为特定元素添加显隐能力。主要是通过在HTML元素上增加data-priority-status自定义属性来控制显隐开关,在子元素上设置布局优先级(data-display-priority)属性来控制显隐的优先级。当布局主轴方向剩余尺寸不足以满足全部元素时,按照布局优先级大小,从小到大依次隐藏,直到容器能够完整显示剩余元素。具有相同布局优先级的元素将同时显示或者隐藏。
显隐能力导入方式
- Vue2/Vue3/React
// xxx.vue
import { initializeDisplayPriorityLayout, DisplayPriorityLayout } from '@hadss/web_adaptive_layout_ui'
布局效果
按照布局优先级大小,从小到大依次隐藏,直到容器能够完整显示剩余元素,效果如下:
sm |
md |
lg |
|
|
|
显隐能力API
- 显隐能力管理类DisplayPriorityLayout
实例化时需传入HTML元素
属性 |
说明 |
参数 |
返回值 |
initializeLayout |
初始化显隐能力 |
- |
- |
destroy |
销毁事件监听 |
- |
- |
- HTML自定义属性
属性 |
说明 |
类型 |
默认值 |
data-priority-status |
设置当前元素是否开启显隐控制功能 |
Boolean |
false |
data-display-priority |
设置当前元素在父容器中的显示优先级 小数点后的数字不做优先级区分,即区间为[x, x+1)内的数字视为相同优先级。例如:1.0和1.9为同一优先级。 属性值均不大于1时,优先级没有区别。 属性值大于1时,值越大则优先级越高。 若父容器空间不足,隐藏低优先级的子元素。若某一优先级的子元素被隐藏,则优先级更低的子元素也都被隐藏。 |
Number |
1 |
- 显隐能力函数
属性 |
说明 |
参数值 |
返回值 |
initializeDisplayPriorityLayout |
识别所有设置data-priority-status属性的元素,为其初始化显隐能力 |
- |
显隐能力管理类的实例集合:DisplayPriorityLayout[] 返回的实例集合可调用destroy()接口来销毁所有元素上的监听事件,具体见使用说明。 |
推荐使用方式(以Vue3为例)
1、导入显隐能力函数和显隐能力管理类
import { initializeDisplayPriorityLayout, DisplayPriorityLayout } from '@hadss/web_adaptive_layout_ui';
2、 在需要使用的HTML元素上增加data-priority-status自定义属性并设置为true,不使用则设置为false。子元素增加data-display-priority优先级属性。
<div ref="containerRef" data-priority-status="true">
<div class="item" data-display-priority="5.0">Item 1</div>
<div class="item" data-display-priority="4">Item 2</div>
<div class="item" data-display-priority="1">Item 3</div>
<div class="item" data-display-priority="4">Item 2</div>
<div class="item" data-display-priority="5">Item 1</div>
</div>
3. 提供两种添加显隐能力的方式,需要在组件实例挂载完成后使用,组件实例卸载时需要销毁事件监听。
- 方式一:调用显隐能力函数,批量添加显隐能力
// 需要在onMounted中执行
let layouts: DisplayPriorityLayout[] = [];
onMounted(() => {
layouts = initializeDisplayPriorityLayout();
});
// 组件实例卸载时销毁事件监听
onUnmounted(() => {
layouts.forEach((layout: DisplayPriorityLayout) => {
layout.destroy();
});
});
- 方式二:引入显隐能力管理类,为特定元素添加显隐能力
// 需要在onMounted中执行
let displayPriorityLayout: DisplayPriorityLayout | null = null;
const containerRef = ref<HTMLElement>(null);
onMounted(() => {
displayPriorityLayout = new DisplayPriorityLayout(containerRef.value);
displayPriorityLayout.initializeLayout();
});
// 组件实例卸载时销毁事件监听
onUnmounted(() => {
displayPriorityLayout?.destroy();
});
使用限制
- 仅支持非表单、表格的块级元素和内联块级元素;
- 容器里面的子元素不能设置绝对定位、固定定位和浮动等会导致子元素脱离文档流的样式;
- 仅支持单行样式,如一行或一列,不支持grid布局。
场景案例
实现效果
sm |
md |
lg |
|
|
|
注意:实现效果中的红色框仅表示组件位置,不代表对应组件的边框。
关键代码(以vue3为例)
- 在容器标签上设置data-priority-status="true"以开启显隐能力,在子标签上设置data-display-priority属性来控制显隐的优先级。
- 实例化显隐能力管理类并传入容器标签dom元素,调用initializeLayout()方法为元素添加显隐能力。
示例如下:
// web_adaptive_ui\samples\vue3_sample\multidevice_sample\src\views\PlayList\components\CurPlaySong.vue
<template>
<div class="bottom-container">
<div style="flex: 1"></div>
<div :class="{ 'all-icon': isLargerPage }">
<div
ref="iconContainer"
data-priority-status="true"
:style="{
width: displayPriorityBoxWidth + 'px',
left: displayPriorityBoxLeft + 'px',
}"
>
<div
v-for="(icon, index) in icons"
:key="index"
:data-display-priority="icon.priority"
class="icon-container"
>
<img
:src="require('../../../assets/playList/' + icon.source)"
:style="icon.style"
/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { DisplayPriorityLayout, BreakpointManager } from '@hadss/web_adaptive_layout_ui';
import { defineProps } from 'vue';
const props = defineProps({
song: {
type: Object,
required: true,
},
});
const iconSpace = ref(0);
const icons = ref([
{ priority: 1, source: 'play_mode.png', style: { width: '24px', height: '24px' } },
{ priority: 1, source: 'control_left.png', style: { width: '28px', height: '28px', right: iconSpace.value + 'px'} },
{ priority: 10, source: 'play_icon.png', style: { width: '24px', height: '24px' } },
{ priority: 1, source: 'control_right.png', style: { width: '28px', height: '28px', left: iconSpace.value + 'px' } },
{ priority: 10, source: 'play_list_icon.png', style: { width: '24px', height: '24px' } },
]);
const displayPriorityLayout = ref<DisplayPriorityLayout | null>(null);
const iconContainer = ref<HTMLElement | null>(null);
const breakpointManager = new BreakpointManager();
const unsubscribe = ref<(() => void) | null>(null);
// 横幅中所有的图标的总宽度.用于显示和隐藏
const displayPriorityBoxWidth = ref(0);
const displayPriorityBoxLeft = ref(0);
const isLargerPage = ref(false);
onMounted(() => {
if (iconContainer.value) {
displayPriorityLayout.value = new DisplayPriorityLayout(iconContainer.value);
displayPriorityLayout.value.initializeLayout();
}
// 订阅断点变化
unsubscribe.value = breakpointManager.subscribeToBreakpoint(() => {
displayPriorityBoxWidth.value = breakpointManager.useBreakpointValue({
sm: 70,
md: 212,
lg: 310,
});
displayPriorityBoxLeft.value = breakpointManager.useBreakpointValue({
md: 0,
lg: -350,
xl: -600,
});
isLargerPage.value = breakpointManager.useBreakpointValue({
md: false,
lg: true,
});
iconSpace.value = breakpointManager.useBreakpointValue({
md: 0,
lg: 20,
});
// 使用新的间距更新图标样式
icons.value[1].style.right = iconSpace.value + 'px';
icons.value[3].style.left = iconSpace.value + 'px';
});
});
onBeforeUnmount(() => {
// 销毁显隐对象
displayPriorityLayout.value?.destroy();
if (unsubscribe.value) {
unsubscribe.value();
}
});
</script>
三、栅格布局响应式组件实现多设备适配
H5栅格布局响应式组件,包含父组件MultiGridRow和子组件MultiGridCol两部分。旨在为布局提供规律性的结构,解决多尺寸多设备的动态布局问题,保证不同设备上各个模块的布局一致性。
组件导入方式
- Vue2
// xxx.vue
import { MultiGridRow, MultiGridCol } from '@hadss/web_adaptive_layout_vue2';
- Vue3
// xxx.vue
import { MultiGridRow, MultiGridCol } from '@hadss/web_adaptive_layout_vue3';
- React
// xxx.tsx
import { MultiGridRow, MultiGridCol } from '@hadss/web_adaptive_layout_react';
布局效果
栅格组件基于默认断点区间,在不同断点下配置不同的栅格数,如下图所示:
sm |
md |
lg |
|
|
|
MultiGridRow父组件API
1. Attribute
属性 |
说明 |
类型 |
可选值 |
默认值 |
columns |
设置布局列数。 1.取值为大于0的整数 2.object类型参数支持{xs: 8, sm: 12, md: 12, lg: 12, xl: 16}类型的参数 |
Number / Object |
- |
12 |
gutter |
栅格布局间距。 1. String/Number类型的值即为横纵布局间距 2. object类型参数支持{x: 12, y: {xs: 5, sm: 8, md: 10}}类型的参数 |
String / Number / Object |
- |
0 |
breakpoints |
设置断点值的断点数列以及基于窗口或容器尺寸的相应参照。 |
Object |
breakpointRefrence值可选“componentSize”、“windowSize”。分别代表参照组件宽度及参照窗口宽度。 |
{ breakpointValue: { xs: 0, sm: 320, md: 600, lg: 840 }, breakpointRefrence: "windowSize", } |
direction |
栅格布局排列方向 |
String |
row / row-reverse |
row |
2. Event
事件名 |
说明 |
参数 |
onGridItemClick |
点击网格子项触发的事件 |
Function({index, name}) "index":选中子项的索引值 "name":选中子项的标识名称 |
onBreakpointChange |
断点发生变化时触发回调 |
Function(breakpoints) 断点值,取值为"xs"、"sm"、"md"、"lg"、"xl"、"xxl"。 |
MultiGridCol子组件API
属性 |
说明 |
类型 |
可选值 |
默认值 |
span |
栅格子组件占用栅格容器组件的列数。span为0表示该元素不参与布局计算,即不会被渲染。 1.取值为大于0的整数 2.object类型参数支持{xs: 8, sm: 12, md: 12, lg: 12, xl: 16}类型的参数 |
Number / Object |
- |
1 |
offset |
栅格子组件相对于原本位置偏移的列数。 1.取值为大于0的整数 2.object类型参数支持{xs: 8, sm: 12, md: 12, lg: 12, xl: 16}类型的参数 |
Number / Object |
- |
0 |
order |
元素的序号,根据栅格子组件的序号,从小到大对栅格子组件做排序。 1.取值为大于0的整数 2.object类型参数支持{xs: 8, sm: 12, md: 12, lg: 12, xl: 16}类型的参数 当子组件不设置order或者设置相同的order, 子组件按照代码顺序展示。 当子组件部分设置order,部分不设置order时,未设置order的子组件依次排序靠前,设置了order的子组件按照数值从小到大排列。 |
Number / Object |
- |
0 |
label |
设置网格项的文本内容(不支持断点) |
String |
- |
- |
icon |
设置网格项的图标链接(不支持断点,只支持网络链接) |
String |
- |
- |
推荐使用方式(以Vue3为例)
<template>
<MultiGridRow
:gutter="gutter"
>
<MultiGridCol :span="spanFirst">
// 子组件内容1
</MultiGridCol>
<MultiGridCol :span="spanSecond">
// 子组件内容2
</MultiGridCol>
<MultiGridCol :span="spanSecond">
// 子组件内容3
</MultiGridCol>
</MultiGridRow>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { MultiGridRow, MultiGridCol } from '@hadss/web_adaptive_layout_vue3';
const gutter = reactive({
x: { sm: 0, md: 240 }
});
const spanFirst = reactive({
md: 12,
lg: 4,
});
const spanSecond = reactive({
sm: 12,
md: 6,
lg: 4,
});
</script>
场景案例
实现效果
sm |
md |
lg |
|
|
|
注意:实现效果中的红色框仅表示组件位置,不代表对应组件的边框。
关键代码(以vue3为例)
代码示例如下:
// web_adaptive_ui\samples\vue3_sample\multidevice_sample\src\views\PlayList\index.vue
<template>
<div class="play-list-container">
<div :class="{ 'container-other-obj': isLargerPage }">
<PlaylistHeaderTable v-if="isLargerPage" />
<div class="table-list">
<div class="tableListInner">
<MultiGridRow
:columns="columns"
:gutter="gutter"
:style="{
marginLeft: distanceBorderLift + 'px',
marginRight: distanceBorderRight + 'px',
}"
>
<MultiGridCol
v-for="(item, index) in songs"
:key="index"
class="song-item"
:span="span"
>
<div class="song-info">
<div class="song-title">{{ item.title }}</div>
<div class="artist-container">
<img
src="../../assets/playList/acoustic.png"
class="acoustic"
/>
<div class="song-artist">{{ item.artist }}</div>
</div>
</div>
<div class="flex-style" />
<img
src="../../assets/playList/song_item_icon.png"
class="song-icon"
:style="{
paddingRight: songIconLift + 'px',
}"
/>
</MultiGridCol>
</MultiGridRow>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { BreakpointManager } from '@hadss/web_adaptive_layout_ui';
import { MultiGridRow, MultiGridCol } from '@hadss/web_adaptive_layout_vue3';
import { songs } from './model';
const breakpointManager = new BreakpointManager();
const isLargerPage = ref(false);
const distanceBorderLift = ref(16);
const distanceBorderRight = ref(16);
const distanceTableTop = ref(0);
const songIconLift = ref(0);
const columns = reactive({
md: 12,
lg: 8,
});
const gutter = reactive({
x: { sm: 0, md: 240 }
});
const span = reactive({
sm: 12,
md: 6,
lg: 4,
});
const unsubscribe = breakpointManager.subscribeToBreakpoint(() => {
isLargerPage.value = breakpointManager.useBreakpointValue({
md: false,
lg: true,
});
distanceBorderLift.value = breakpointManager.useBreakpointValue({
sm: 16,
md: 24,
});
distanceBorderRight.value = breakpointManager.useBreakpointValue({
sm: 16,
md: 0,
});
songIconLift.value = breakpointManager.useBreakpointValue({
sm: 0,
md: 24,
});
});
onBeforeUnmount(() => {
if (unsubscribe) {
unsubscribe();
}
});
</script>
四、侧边栏布局响应式组件实现多设备适配
H5侧边栏响应式组件为显示和隐藏侧边栏提供了容器,通过子组件定义侧边栏和内容区,第一个子组件表示侧边栏,第二个子组件表示内容区。
组件导入方式
- Vue2
// xxx.vue
import { SideBarContainerVue } from '@hadss/web_adaptive_layout_vue2';
- Vue3
// xxx.vue
import { SideBarContainerVue } from '@hadss/web_adaptive_layout_vue3';
- React
// xxx.tsx
import { SideBarContainerReact } from '@hadss/web_adaptive_layout_react';
默认布局效果
侧边栏组件基于屏幕宽度,动态控制侧边栏是否显示,实现二分栏布局,如下图所示:
sm |
md |
lg |
|
|
|
MultiSideBar组件API
1. Attribute
属性 |
说明 |
类型 |
是否必填 |
默认值 |
sideBarContainerType |
设置侧边栏的显示类型 |
SideBarContainerType |
否 |
Embed |
showSideBar |
设置是否显示侧边栏 |
Boolean |
否 |
true |
controlButton |
设置侧边栏控制按钮的属性 |
ButtonStyle |
否 |
- |
showControlButton |
设置是否显示控制按钮 |
Boolean |
否 |
true |
sideBarWidth |
设置侧边栏的宽度。设置为小于0的值时按默认值显示。受最小宽度和最大宽度限制,不在限制区域内取最近的点 |
String / Number |
否 |
240, 单位:px |
minSideBarWidth |
设置侧边栏最小宽度。设置为小于0的值时按默认值显示。值不能超过侧边栏容器本身宽度,超过使用侧边栏容器本身宽度 |
String / Number |
否 |
240, 单位:px |
maxSideBarWidth |
设置侧边栏最大宽度。设置为小于0的值时按默认值显示。值不能超过侧边栏容器本身宽度,超过使用侧边栏容器本身宽度 |
String / Number |
否 |
280, 单位:px |
autoHide |
设置当侧边栏拖拽到小于最小宽度后,是否自动隐藏。受minSideBarWidth属性方法影响,minSideBarWidth属性方法未设置值使用默认值 |
Boolean |
否 |
true |
sideBarPosition |
设置侧边栏显示位置 |
SideBarPosition |
否 |
Start |
divider |
设置分割线的样式 |
DividerStyle / null |
否 |
- |
minContentWidth |
设置SideBarContainer组件内容区可显示的最小宽度 |
String / Number |
否 |
360, 单位:px |
SideBarContainerType类型
名称 |
说明 |
Embed |
侧边栏嵌入到组件内,和内容区并列显示。 组件尺寸小于minContentWidth + minSideBarWidth,并且未设置showSideBar时,侧边栏自动隐藏。 未设置minSideBarWidth或者minContentWidth采用未设置接口的默认值进行计算。 组件在自动隐藏后,如果通过点击控制按钮唤出侧边栏,则侧边栏悬浮在内容区上显示。 |
Overlay |
侧边栏浮在内容区上面 |
AUTO |
组件尺寸大于等于minSideBarWidth+minContentWidth时,采用Embed模式显示。 组件尺寸小于minSideBarWidth+minContentWidth时,采用Overlay模式显示。 未设置minSideBarWidth或minContentWidth时,会使用未设置接口的默认值进行计算,若计算的值小于600px,则使用600px做为模式切换的断点值。 |
ButtonStyle类型
名称 |
类型 |
必填 |
说明 |
left |
Number |
否 |
设置侧边栏控制按钮距离容器左界限的间距。 默认值:16, 单位:px |
top |
Number |
否 |
设置侧边栏控制按钮距离容器上界限的间距。 默认值:48, 单位:px |
width |
Number |
否 |
设置侧边栏控制按钮的宽度。 默认值:24, 单位:px |
height |
Number |
否 |
设置侧边栏控制按钮的高度。 默认值:24, 单位:px |
icons |
{ shown: String; hidden: String } |
否 |
设置侧边栏控制按钮的图标 shown: 设置侧边栏显示时控制按钮的图标。 说明:资源获取错误时,使用默认图标。 hidden: 设置侧边栏隐藏时控制按钮的图标。 图标资源需要用require进行加载和传值 |
SideBarPosition类型
名称 |
说明 |
Start |
侧边栏位于容器左侧 |
End |
侧边栏位于容器右侧 |
DividerStyle类型
名称 |
类型 |
必填 |
说明 |
strokeWidth |
String / Number |
否 |
分割线的线宽。 默认值:1, 单位:px |
color |
String |
否 |
分割线的颜色。 默认值:#000000,3% |
startMargin |
String / Number |
否 |
分割线与侧边栏顶端的距离。 默认值:0, 单位:px |
endMargin |
String / Number |
否 |
分割线与侧边栏底端的距离。 默认值:0, 单位:px |
2. Event
事件名 |
说明 |
参数 |
onChangeStatus |
当侧边栏的状态在显示和隐藏之间切换时触发回调 |
value:Boolean true表示显示,false表示隐藏。 |
推荐使用方式(以Vue3为例)
<template>
<SideBarContainerVue
sideBarWidth="400"
minSideBarWidth="200px"
maxSideBarWidth="900"
:minContentWidth="minContentWidth"
:divider="divider"
:showSideBar="showSideBar"
:sideBarContainerType="sideBarContainerType"
:autoHide="true"
sideBarPosition="Start"
:showControlButton="true"
:controlButton="controlButton"
:onChangeStatus="changeEvent"
>
<div>
侧边栏内容
</div>
<div>
<p>提供侧边栏可以显示和隐藏的侧边栏容器,通过子组件定义侧边栏和内容区,第一个子组件表示侧边栏,第二个子组件表示内容区。</p>
</div>
</SideBarContainerVue>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { SideBarContainerVue } from '@hadss/web_adaptive_layout_vue3';
const showSideBar = ref(true);
const divider = reactive({
strokeWidth: '3px',
color: 'rgba(0, 0, 0, 0.3)',
startMargin: '2px',
endMargin: '2px',
});
const sideBarContainerType = ref('Embed');
const minContentWidth = ref(400);
const controlButton = reactive({
icons: {
shown: require('../../assets/icons/icon_001.png'),
hidden: require('../../assets/icons/icon_002.png'),
}
});
const changeEvent = (value: boolean) => {
console.log(`当前状态为${value}`);
}
</script>
五、分栏布局响应式组件适配多设备
分栏布局响应式组件,包含父组件NavigationContainer、导航栏组件NavigationBar和内容区组件NavigationContent三部分。在空间充足时,可以实现将窗口划分为两栏,用于展示多类内容,结合侧边栏组件可以实现三分栏布局。
组件导入方式
- Vue2
// xxx.vue
import { NavigationContainerVue, NavigationBarVue, NavigationContentVue } from '@hadss/web_adaptive_layout_vue2';
- Vue3
// xxx.vue
import { NavigationContainerVue, NavigationBarVue, NavigationContentVue } from '@hadss/web_adaptive_layout_vue3';
- React
// xxx.tsx
import { NavigationContainerReact, NavigationBarReact, NavigationContentReact } from '@hadss/web_adaptive_layout_react';
默认布局效果
分栏组件基于屏幕宽度,动态控制导航栏的显示模式,实现二分栏布局,如下图所示:
sm |
md |
lg |
|
|
|
NavigationContainer父组件API
1. Attribute
属性 |
说明 |
类型 |
是否必填 |
默认值 |
navBarWidth |
设置导航栏宽度。仅在Navigation组件分栏时生效。 |
String / Number |
否 |
默认值:240
单位:px |
navBarWidthRange |
设置导航栏最小和最大宽度(双栏模式下生效)。 |
String / Number[] |
否 |
默认值:最小默认值 240,最大默认值为组件宽度的40% ,且不大于 432,未正确设置的值按照默认值计算。 单位:px |
minContentWidth |
导航栏内容区最小宽度。 |
String / Number |
否 |
默认值:360 单位:px |
mode |
导航栏的显示模式。 |
NavigationMode |
否 |
默认值:NavigationMode.Auto 自适应:基于组件宽度自适应单栏和双栏。 |
navigationPageName |
导航栏页面的路由名称,属性参考对应框架的README介绍 |
String |
是 |
- |
navBarPosition |
设置导航栏位置。仅在Navigation组件分栏时生效。 |
NavBarPosition |
否 |
NavBarPosition.Start |
NavigationMode类型
名称 |
说明 |
Stack |
导航栏与内容区独立显示,相当于两个页面。 |
Split |
导航栏与内容区分两栏显示。以下navBarWidthRange的值用[minNavBarWidth,maxNavBarWidth]表示 1.当navBarWidth属性的值,在navBarWidthRange属性的值范围以外时,navBarWidth按如下规则显示:navBarWidth < minNavBarWidth时,navBarWidth修正为minNavBarWidth;navBarWidth > maxNavBarWidth,且组件宽度 - minContentWidth - 分割线宽度(1) > maxNavBarWidth时,navBarWidth修正为maxNavBarWidth;navBarWidth > maxNavBarWidth,且组件宽度 - minContentWidth - 分割线宽度(1) < minNavBarWidth时,navBarWidth修正为minNavBarWidth;navBarWidth > maxNavBarWidth,且组件宽度 - minContentWidth - 分割线宽度(1)在navBarWidthRange范围内,navBarWidth修正为组件宽度 - 分割线宽度(1) - minContentWidth。 2.当navBarWidth属性的值,在navBarWidthRange属性的值范围以内时,navBarWidth按如下规则显示:minNavBarWidth + minContentWidth + 分割线宽度(1) >= 组件宽度时,navBarWidth修正为minNavBarWidth;minNavBarWidth + minContentWidth + 分割线宽度(1) < 组件宽度,且navBarWidth + minContentWidth + 分割线宽度(1) >= 组件宽度时,navBarWidth修正为组件宽度 - 分割线宽度(1) - minContentWidth;minNavBarWidth + minContentWidth + 分割线宽度(1) < 组件宽度,且navBarWidth + minContentWidth + 分割线宽度(1) < 组件宽度时,navBarWidth为设置的值。 3.缩小组件尺寸时,先缩小内容区的尺寸至minContentWidth,然后再缩小导航栏的尺寸至minNavBarWidth。若继续缩小,先缩小内容区,内容区消失后再缩小导航栏。 4.设置导航栏为固定尺寸时,若持续缩小组件尺寸,导航栏最后压缩显示。 5.若只设置了navBarWidth属性,则导航栏宽度为navBarWidth,且分割线不可拖动。 |
Auto |
窗口宽度>=600px时,采用Split模式显示;窗口宽度<600px时,采用Stack模式显示,600px等于minNavBarWidth(240px) + minContentWidth (360px)。 |
NavBarPosition类型
名称 |
说明 |
Start |
双栏显示时,主列在主轴方向首部。 |
End |
双栏显示时,主列在主轴方向尾部。 |
2. Event
事件名 |
说明 |
参数 |
onNavBarStateChange |
导航栏显示状态切换时触发该回调。 |
isVisible为true时表示显示,为false时表示隐藏。 |
onNavigationModeChange |
当Navigation首次显示或者单双栏状态发生变化时触发该回调。 |
mode为NavigationMode.Split时,当前Navigation显示为双栏;mode为NavigationMode.Stack时,当前Navigation显示为单栏。 |
推荐使用方式(以Vue3为例)
1. 导入分栏组件
import { NavigationContainerVue, NavigationBarVue, NavigationContentVue } from '@hadss/web_adaptive_layout_vue3';
2. template中渲染组件
<template>
<NavigationContainerVue :navBarWidth="navBarWidth"
:navigationPageName="navigationPageName"
:onNavBarStateChange="onNavBarStateChange"
:onNavigationModeChange="onNavigationModeChange">
<NavigationBarVue>
// 导航栏页面
</NavigationBarVue>
// 基于路由表挂载内容区页面
<NavigationContentVue></NavigationContentVue>
</NavigationContainerVue>
</template>
3. 在路由表中配置内容区路由信息
Navigation为导航栏页面,children里的路由对应内容区页面。
// router/index.ts
const routes: Array<RouteRecordRaw> = [
{
path: '/navigation',
name: 'Navigation',
component: Navigation,
children: [
{
path: 'pageContent1',
component: PageContent1,
},
{
path: 'pageContent2',
component: PageContent2,
},
]
},
...
]
4. 设置组件属性 其中navigationPageName必须传递,值为导航栏页面的路由name值。
<script lang="ts" setup>
import { NavigationContainerVue, NavigationBarVue, NavigationContentVue } from '@hadss/web_adaptive_layout_vue3';
const navBarWidth = ref(200);
const navigationPageName = ref('Navigation');
const onNavBarStateChange = (isVisible: boolean) => {
// ...
}
const onNavigationModeChange = (mode: string) => {
// ...
}
</script>
场景案例
实现效果
sm |
md |
lg |
|
|
|
关键代码(以vue3为例)
代码示例如下:
1. 路由表配置
import MessageList from '../views/MessageList/MessageList.vue';
import DetailContainer from '../views/MessageList/DetailContainer.vue';
const routes: Array<RouteConfig> = [
...
{
path: '/messageList',
name: 'MessageList',
component: MessageList,
children: [
{
path: 'detailContainer',
name: 'DetailContainer',
component: DetailContainer,
},
],
},
];
2. 分栏组件页面示例代码
// web_adaptive_ui\samples\vue3_sample\multidevice_sample\src\views\MessageList\MessageList.vue
<template>
<div class="adaptive-layout">
<NavigationContainerVue
:mode="mode"
:navigationPageName="navigationPageName"
:navBarWidth="navBarWidth"
:navBarWidthRange="navBarWidthRange"
:minContentWidth="minContentWidth"
:onNavigationModeChange="onNavigationModeChange"
>
<NavigationBarVue>
// 导航栏内容
<div class="list-container">
<div class="container">
<ul class="content message-list">
<li
v-for="item in messageDataList"
:key="item.id"
class="message-item"
:class="{ 'item-active': activeItem.id === item.id }"
>
<div class="item">
// 消息列表
</div>
</li>
</ul>
</div>
</div>
</NavigationBarVue>
// 内容区挂载位置
<NavigationContentVue></NavigationContentVue>
</NavigationContainerVue>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { NavigationContainerVue, NavigationContentVue, NavigationBarVue } from '@hadss/web_adaptive_layout_vue3';
import { messageData } from '../../mocks/mockData';
const router = useRouter();
const route = useRoute();
const messageDataList = ref(messageData);
const mode = ref<string>('Auto');
const navBarWidth = ref<number>(470);
const navigationPageName = ref<string>('MessageList');
const navBarWidthRange = ref<number[]>([260, 700]);
const minContentWidth = ref<number>(340);
const activeItem = ref({
id: '11',
name: '张晓萌',
message: '好的,我明天过来',
time: '13:55',
avatar: require('../../assets/images/touxiang.svg'),
unread: null,
});
const unsubscribe = ref(null);
const onNavigationModeChange = (mode: string) => {
if (mode === 'Stack' && route.path !== '/messageList') {
router.push('/messageList');
} else if (mode === 'Split' && route.path !== '/messageList/detailContainer') {
router.push('/messageList/detailContainer');
}
};
</script>
3. 内容区页面示例代码
// web_adaptive_ui\samples\vue3_sample\multidevice_sample\src\views\MessageList\DetailContainer.vue
<template>
<div class="detail-container">
<div class="container">
// 聊天内容
<div class="title-wrap">
<div v-show="isXSorSm" className="go-back" @click="goHome">
<img className="title-img" src="../../assets/images/left.png" />
</div>
...
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch, computed } from "vue";
import { useRouter } from "vue-router";
import { MessageList } from "../../types/types";
const router = useRouter();
const goHome = () => {
router.push("/messageList");
};
</script>
六、示例代码
基于H5框架的多设备开发sample示例代码地址。
更多推荐
所有评论(0)