《SafeCapsaulContainer》
介绍
SafeCapsaulContainer
是一个用于自动处理小程序顶部状态栏区域的容器组件。
- 1、它会自动调整 标题栏 内容区域的安全顶部距离,使其与小程序右上角胶囊一致
- 2、它会自动设置 标题栏 的上下对其位置,使其与 小程序右上角胶囊 保持 上下居中
- 3、并提供了固定顶部 、 修改背景颜色 设置左右边距 的功能。
- 4、当使用
fixed
模式时,组件会自动添加一个占位元素,以防止页面内容被遮挡 - 5、启用了条件编译,自动适配
支付宝小程序的奇葩返回键安全距离
功能特性
- 自动适配顶部状态栏高度
- 内容区域与胶囊按钮高度对齐
- 支持固定顶部模式
- 可自定义背景色
- 支持左右内边距调整
- 自动处理内容垂直居中
快速使用示例
代码:
<template>
<SafeCapsaulContainer :fixed="true">
<!-- 这里的标题栏内容直接使用 wot组件 -->
<wd-navbar
title="我的报告"
left-arrow
@click-left="handleClickLeft"
custom-class="custom-navbar"
></wd-navbar>
</SafeCapsaulContainer>
<!-- ......其他内容省略....... -->
</template>
效果展示:
Attributes属性
Props 可选的传入数值
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
fixed | 是否固定在顶部 | Boolean | false |
bgColor | 背景颜色 | String | #ffffff(纯白) |
showBgColor | 是否显示背景色 | Boolean | true |
showPlaceholder | 是否显示占位元素(固定顶部时可能需要) | Boolean | true |
paddingLeft | 左侧内边距(默认单位:px,用户只需传入纯数字即可) | Number | 0 |
paddingRight | 右侧内边距默认单位:px,用户只需传入纯数字即可)) | Number | 0 |
默认插槽
名称 | 说明 |
---|---|
default | 默认插槽,用于放置内容 |
注意事项
- 当使用
fixed
模式时,组件会自动添加一个占位元素,以防止页面内容被遮挡 - 内容区域会自动垂直居中对齐
- z-index 默认设置为 998,若有项目有用到遮罩层,可能需要根据实际场景调整
- 组件会自动获取胶囊按钮的位置信息来调整布局
样式定制
组件提供了基础的样式定制能力:
- 通过
bgColor
属性可以修改背景色 - 通过
paddingLeft
和paddingRight
可以调整内容区域的水平间距 - 内容区域默认垂直居中,使用了
position子绝父相
和transform
实现
示例场景
1. 固定顶部导航栏
<template>
<safe-capsaul-container
:fixed="true"
:bgColor="#f8f8f8"
:paddingLeft="16"
:paddingRight="16"
>
<view class="nav-content">
<text>导航栏标题</text>
</view>
</safe-capsaul-container>
</template>
2. 普通内容区域
<template>
<safe-capsaul-container
:paddingLeft="12"
:paddingRight="12"
>
<view class="content">
<text>普通内容</text>
</view>
</safe-capsaul-container>
</template>
建议的使用方式
有些项目,一会要fixed,一会不用fixed,建议按下面思路使用
- 外部起一个
container容器
,占满全屏且不设padding
- 内含 《
SafeCapsaulContainer
》 与 《content的view盒子
》 SafeCapsaulContainer
不论是否fixed
,都将自动安全的吃掉安全顶部距离content
高度被其内容自动撑开,且由content去
配置padding
内边距挤占子内容,这样不影响安全顶部组件
<template>
<!-- 页面内容容器(必需) -->
<view class="container">
<!-- 安全顶部容器(必需,且仅仅闭合包裹导航栏) -->
<SafeCapsaulContainer :fixed="true">
<!-- 导航栏(可选) -->
<wd-navbar
title="页面标题"
left-text="返回"
left-arrow
@click-left="handleClickLeft"
/>
</SafeCapsaulContainer>
<!-- 页面内容 -->
<view class="content">
<!--在这里编写页面主体内容-->
</view>
</view>
</template>
<script setup>
// 安全顶部容器组件(必需)
import { SafeCapsaulContainer } from "@/components/SafeCapsaulContainer"
// 页面导航(可选,这只是二次封装过的uniapp页面跳转函数)
import { useNavigate, useSwitchTab } from "@/hooks/useNavigate"
// 简单的返回上一级页面,使用uni.navigateBack
const handleClickLeft = () => {
uni.navigateBack()
}
// 复杂的路由跳转,普通子页面使用封装好的: useNavigate
const jumpXxxPage = () => {
useNavigate("/pages/Xxx/index",{...params})
}
// 复杂的路由跳转,tabbar页面使用封装好的: useSwitchTab
const jumpXxxTabbar = () => {
useSwitchTab("/pages/Xxx/index",{...params})
}
// ......省略其他代码
</script>
<style lang="scss" scoped>
// 页面容器(必需)
.container {
width: 100%;
min-height: 100vh;
box-sizing: border-box; // 若页面涉及padding,统一在padding后续上:box-sizing: border-box;
// 建议的flex布局(可选)
display: flex;
flex-direction: column;
align-items: center;
}
// 页面内容
.content {
width: 100%;
padding: 12px; // (可选,建议用padding去挤占子内容)
box-sizing: border-box;
}
// ....省略其他代码
</style>
更新日志
1.0.0
- 首次发布
- 支持基础的顶部安全区域适配
- 支持固定顶部模式
- 提供基础的样式定制能力
2.0.0
- 适配支付宝小程序的左侧返回按键
Bug 反馈与解决
经过实践,当使用wot 的导航栏组件时,会出现组件高度过长而遮挡下方的主体内容
原因:
- 这是由于前期考虑不足而导致的
填充盒子高度
默认为顶部距离+胶囊高度
解决办法:
- 1、可以在content的主体内容区域,增加
padding-top: 8px
解决 - 2、也可以手搓一个导航栏盒子,一般来说这样高度不会太长
组件源代码
可以复制原代码,在components文件创建一个SafeCapsaulContainer.vue组件
例如这样 import { SafeCapsaulContainer } from "@/components/SafeCapsaulContainer"
<!--
description: 自动撑开顶部状态栏安全区域,并且是与胶囊保持一样的高度
description: 条件编译,针对支付宝小程序左侧的返回按键适配,自动计算左侧返回按键的安全距离
-->
<template>
<view class="safe-top-container" :style="fixedLayoutStyle" :class="{ 'fixed-wrapper': fixed }">
<view class="outer-box" :style="{ width: '100%', height: `${capsualHeight}px`, position: 'relative', zIndex: 998 }">
<view class="inner-box" :style="diyInnerBoxStyle">
<slot></slot>
</view>
</view>
</view>
<!-- 配一个不脱离文档流的假盒子,使得页面主要元素不会被遮住 -->
<view v-if="fixed" :style="{ height: `${capsualTop + capsualHeight}px` }"></view>
</template>
<script setup>
const props = defineProps({
// 固定在顶部
fixed: {
type: Boolean,
default: false,
},
// 背景色
bgColor: {
type: String,
default: '#ffffff',
},
// 是否显示背景色
showBgColor: {
type: Boolean,
default: true,
},
// 显示占位(如果开启固定到顶部可能会用到
showPlaceholder: {
type: Boolean,
default: true,
},
// 左侧内边距
paddingLeft: {
type: Number,
default: 0,
},
// 右侧内边距
paddingRight: {
type: Number,
default: 0,
},
})
// 旧版·顶部安全距离
// const safeTop = ref(0)
// 右上角的胶囊信息
const capsualTop = ref(0)
const capsualWidth = ref(0)
const capsualHeight = ref(0)
const capsualLeft = ref(0)
const capsualRight = ref(0)
// 针对支付宝小程序左侧的返回按键
const alipayLeftBackBtnDistance = ref(0) // 初始化为0
// 固定布局样式
const fixedLayoutStyle = computed(() => ({
width: "100%",
paddingTop: `${capsualTop.value}px`, // 来自胶囊的顶部距离
// paddingLeft: `${props.paddingLeft}px`, // 传入的左边距 ----- 不要写这里,不然底部的线条会变短
// paddingRight: `${props.paddingRight}px`, // 传入的右边距
backgroundColor: props.showBgColor ? props.bgColor : "transparent",
zIndex: 998,
}))
const diyInnerBoxStyle = computed(() => ({
// height: `${capsualHeight.value}px`,
width: "100%",
paddingLeft: `${props.paddingLeft + alipayLeftBackBtnDistance.value}px`, // 传入的左边距+支付宝左侧返回按键的距离(默认0)
paddingRight: `${props.paddingRight}px`, // 传入的右边距
boxSizing: "border-box",
// 这样子实现上下居中
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
zIndex: 998,
}))
onMounted(async () => {
// const { statusBarHeight } = uni.getSystemInfoSync() // 旧版·获取状态栏的高度
// safeTop.value = statusBarHeight // 旧版·状态栏的高度
const capsaulInfo = uni.getMenuButtonBoundingClientRect() // 获取胶囊的信息 {width,height,top,left,right 单位:px}
capsualTop.value = capsaulInfo.top
capsualWidth.value = capsaulInfo.width
capsualHeight.value = capsaulInfo.height
capsualLeft.value = capsaulInfo.left
capsualRight.value = capsaulInfo.right
// console.log("-------------------------《SafeTopContainer》信息:-------------------------")
// console.log("胶囊信息", capsaulInfo)
// console.log("整体计算样式", fixedLayoutStyle.value)
// console.log("插槽计算样式", diyInnerBoxStyle.value)
// #ifdef MP-ALIPAY
const leftBackBtnInfo = await my.getLeftButtonsBoundingClientRect()
alipayLeftBackBtnDistance.value = leftBackBtnInfo.backButtonIcon.left + leftBackBtnInfo.backButtonIcon.width
console.log("支付宝左侧返回按键信息", leftBackBtnInfo)
console.log("支付宝左侧返回按键距离", alipayLeftBackBtnDistance.value)
// #endif
})
</script>
<style scoped lang="scss">
.safe-top-container {
width: 100%;
z-index: 998;
}
.fixed-wrapper {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 998; // 有些wot组件的遮罩层可能是999,数值太大了的话会导致这个还是白色的
}
</style>