777 lines
21 KiB
Plaintext
777 lines
21 KiB
Plaintext
<script lang="ts" setup>
|
|
|
|
import { xTabbarCanvasDrawProps } from "./interface.uts"
|
|
import { SlotsType, PropType, computed, nextTick } from "vue"
|
|
import { getUid } from "../../core/util/xCoreUtil.uts"
|
|
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
|
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
|
import { xConfig, xTabbarConfig } from "../../config/xConfig.uts"
|
|
import { TABBAR_ITEM_INFO, NAVIGATE_TYPE, TABBAR_ITEM } from "../../interface.uts"
|
|
import xTabbarCanvas from "./xTabbarCanvas.uts"
|
|
|
|
/**
|
|
* @name 底部导航 xTabbar
|
|
* @description 可定义凸起按钮。通过全局状态设置选中项,放于任何页面可自动选中。组件的镂空因平台而有兼容差异,微信sdk不支持相关api,因为在微信端请务必is-canvas-render设置为false
|
|
* @page /pages/index/tabbar
|
|
* @category 导航组件
|
|
* @constant 平台兼容
|
|
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
|
*/
|
|
defineOptions({ name: "xTabbar" })
|
|
|
|
const proxy = getCurrentInstance()?.proxy;
|
|
const canvasIds = ref<UniCanvasElement | null>(null)
|
|
const canvasIdsEle = ref<UniElement | null>(null)
|
|
|
|
|
|
const height = ref(60)
|
|
const safeHeight = ref(0)
|
|
const tuqiHeight = ref(0)
|
|
const tuqiWeight = ref(0)
|
|
const tid = ref(0)
|
|
const keyid = ref(123)
|
|
const _list = ref<TABBAR_ITEM[]>([])
|
|
const xcanvas = ref<xTabbarCanvas | null>(null)
|
|
const resizeId = ref(0)
|
|
const tidresizeId = ref(3211)
|
|
const yanchiId = ref(11)
|
|
const domWidth = ref(0)
|
|
const domHeight = ref(0)
|
|
const domOffsetWidth = ref(0)
|
|
const domOffsetHeight = ref(0)
|
|
const isShowInpage = ref(false)
|
|
const contentIsChange = ref(false)
|
|
const canvasUid = ref('canvasid'+Math.random().toString(10).substring(2,15))
|
|
const isFirst = ref(true)
|
|
const slots = defineSlots<{
|
|
item : {
|
|
isactive : boolean,
|
|
children : TABBAR_ITEM,
|
|
activeindex : number,
|
|
selfindex : number
|
|
}
|
|
}>()
|
|
|
|
const emits = defineEmits<{
|
|
/**
|
|
* 项目被点击时触发。
|
|
* @param {number} index - 当前选中的索引
|
|
*/
|
|
(e : 'click', index : number) : void,
|
|
/**
|
|
* 项目被双击时触发。
|
|
* @param {number} index - 当前选中的索引
|
|
*/
|
|
(e : 'doubleClick', index : number) : void,
|
|
/**
|
|
* 切换项目时触发。
|
|
* @param {number} index - 当前选中的索引
|
|
*/
|
|
(e : 'change', index : number) : void,
|
|
/**
|
|
* 同步组件高给外部使用,请使用v-model:autoTabbarHeight
|
|
* 组件高度 = 安全栏高度 + 导航栏高度,外部最好computed使用,因为是异步的
|
|
* @param {number} height - 当前组件高度
|
|
*/
|
|
(e : 'update:autoTabbarHeight', height : number) : void,
|
|
}>()
|
|
|
|
const props = defineProps({
|
|
/**
|
|
* 未选中时的颜色
|
|
*/
|
|
color: {
|
|
type: String,
|
|
default: "#b9b9b9",
|
|
},
|
|
/**
|
|
* 选中时的颜色,空值取全局主题
|
|
*/
|
|
selectedColor: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
/**
|
|
* 背景,如果你为空,会读取全局的亮色tabbar背景
|
|
*/
|
|
bgColor: {
|
|
type: String,
|
|
default: "white",
|
|
},
|
|
/**
|
|
* 暗黑时的背景,如果为空,取全局的底部导航背景色。
|
|
*/
|
|
darkBgColor: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
/**
|
|
* 显示顶部边线,暗黑时取全局的borderDarkColor
|
|
*/
|
|
showTopBorder: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
/**
|
|
* 边线颜色
|
|
*/
|
|
borderColor: {
|
|
type: String,
|
|
default: "#f0f0f0",
|
|
},
|
|
/**
|
|
* 文字大小
|
|
*/
|
|
fontSize: {
|
|
type: String,
|
|
default: "11px",
|
|
},
|
|
/**
|
|
* 图标大小
|
|
*/
|
|
iconSize: {
|
|
type: String,
|
|
default: "28px",
|
|
},
|
|
/**
|
|
* 导航的整体高度,请使用v-model:autoTabbarHeight="x"
|
|
* 来获取当前的高度。外部要去变更值。这个只是对外输出,
|
|
* 给您 外部放在底部占位用,省得你们要一屏时计算高。外部最好computed使用,因为是异步的
|
|
*/
|
|
autoTabbarHeight: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
/**
|
|
* 需要向外凸起的项目索引。
|
|
* -1表示不凸起
|
|
*/
|
|
outIndex: {
|
|
type: Number,
|
|
default: 2
|
|
},
|
|
/**
|
|
* 凸起的背景色
|
|
*/
|
|
outBgColor: {
|
|
type: String,
|
|
default: "primary"
|
|
},
|
|
/**
|
|
* 凸起的图标颜色
|
|
*/
|
|
outIconColor: {
|
|
type: String,
|
|
default: "white"
|
|
},
|
|
/**
|
|
* 是否开启凸起背景镂空,截止sdk 4.31 ios有bug官方已经知晓在修复
|
|
*/
|
|
isOutSpace: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
/**
|
|
* 是否反向凸起就是不镂空,但在凸起的底层会被绘制背景。false是镂空,true表示反向包住。
|
|
* 截止sdk 4.31 ios有bug官方已经知晓在修复
|
|
*/
|
|
outReserve: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
/**
|
|
* 是否悬浮在底部,不可动态修改
|
|
* fixed悬浮,relative静态布局。
|
|
*/
|
|
position: {
|
|
type: String,
|
|
default: 'fixed'
|
|
},
|
|
/**
|
|
* 渐变背景,如果提供,上面的背景和暗黑背景将失效。
|
|
* 仅支持:to bottom,to right,to left,to top
|
|
* 例:['to right','#ff667f','#ff5416']
|
|
*/
|
|
linearGradient: {
|
|
type: Array as PropType<string[]>,
|
|
default: () : string[] => [] as string[]
|
|
},
|
|
/**
|
|
* 如果你提供了本地的list数据,那么全局的list将不会被采用,你需要自己管理激活引,
|
|
* 跨页面时需要你自己设置当前页面的索引,因为变量索引是无法跨页面的。
|
|
*/
|
|
list: {
|
|
type: Array as PropType<TABBAR_ITEM_INFO[]>,
|
|
default: () : TABBAR_ITEM_INFO[] => [] as TABBAR_ITEM_INFO[]
|
|
},
|
|
/**
|
|
* 当前激活的索引,如果你提供了本地索引,那全局的索引将失效。
|
|
* 跨页面时需要你自己设置当前页面的索引,因为变量索引是无法跨页面的。
|
|
* 仅当月list不为空时有效。
|
|
*/
|
|
activeIndex: {
|
|
type: Number,
|
|
default: -1
|
|
},
|
|
/**
|
|
* 层级
|
|
*/
|
|
zIndex: {
|
|
type: Number,
|
|
default: 20
|
|
},
|
|
/**
|
|
* 首次渲染等待时长
|
|
* 如果你在首页或者第一页使用时,如果渲染异常,请通过调节这个值来使用其正常
|
|
* 主要是针对安卓ios。对web不起效
|
|
* 如果是次页,时间不需要太长。时间长短时你页面渲染时长而定。
|
|
* 页面元素过多,排版较长时可能需要设置超过350ms,正常的次页100-150ms即可。
|
|
*/
|
|
firstRenderTimeout:{
|
|
type:Number,
|
|
default:300
|
|
},
|
|
/**
|
|
* 是否开启canvas渲染引擎,开启后,可以得到
|
|
* 异形的镂空效果。比view渲染的更精致好看。开启后前面的firstRenderTimeout要注意阅读
|
|
* 两种渲染版本请自行选择使用。微信端不支持canvas渲染,有相关接口微信sdk没有对齐平台,因此一定要设置为false
|
|
*/
|
|
isCanvasRender:{
|
|
type:Boolean,
|
|
default:true
|
|
}
|
|
})
|
|
|
|
|
|
const _outBgColor = computed(() : string => {
|
|
return getDefaultColor(props.outBgColor)
|
|
})
|
|
const _outIconColor = computed(() : string => {
|
|
return getDefaultColor(props.outIconColor)
|
|
})
|
|
|
|
const _outIndex = computed(() : number => {
|
|
if (props.outIndex == -1) return -1
|
|
if (props.outIndex > _list.value.length - 1) return -1
|
|
return props.outIndex;
|
|
})
|
|
const _nowOutItem = computed(() : TABBAR_ITEM => {
|
|
|
|
return _list.value[_outIndex.value]
|
|
})
|
|
const _fontSize = computed(() : string => {
|
|
return checkIsCssUnit(props.fontSize, xConfig.unit)
|
|
})
|
|
const _iconSize = computed(() : string => {
|
|
return checkIsCssUnit(props.iconSize, xConfig.unit)
|
|
})
|
|
|
|
const _selectedColor = computed(() : string => {
|
|
return props.selectedColor == "" ? getDefaultColor(xConfig.color) : getDefaultColor(props.selectedColor)
|
|
})
|
|
const _color = computed(() : string => {
|
|
return getDefaultColor(props.color)
|
|
})
|
|
|
|
const _bgColor = computed(() : string => {
|
|
if (xConfig.dark == 'dark') {
|
|
if (props.darkBgColor != '') {
|
|
return getDefaultColor(props.darkBgColor)
|
|
} else {
|
|
return getDefaultColor(xConfig.tabarBackgroundColorDark)
|
|
}
|
|
} else if (props.bgColor == '') {
|
|
return getDefaultColor(xConfig.tabarBackgroundColorLight)
|
|
}
|
|
return getDefaultColor(props.bgColor)
|
|
})
|
|
const _borderColor = computed(() : string => {
|
|
if (xConfig.dark == 'dark') {
|
|
return getDefaultColor(xConfig.borderDarkColor)
|
|
}
|
|
|
|
return getDefaultColor(props.borderColor)
|
|
})
|
|
const _showTopBorder = computed(() : boolean => {
|
|
return props.showTopBorder
|
|
})
|
|
const _active = computed(() : number => {
|
|
return xTabbarConfig.tabbarActiveIndex
|
|
})
|
|
|
|
|
|
const _linearGradientByar = computed(() : string[] => {
|
|
if (props.linearGradient.length < 3) return [] as string[];
|
|
let startcolor = getDefaultColor(props.linearGradient[1])
|
|
let endcolor = getDefaultColor(props.linearGradient[2])
|
|
let str = [props.linearGradient[0], startcolor, endcolor] as string[]
|
|
return str;
|
|
})
|
|
|
|
const _linearGradient = computed(() : string => {
|
|
if (props.linearGradient.length < 3) return '';
|
|
let startcolor = getDefaultColor(props.linearGradient[1])
|
|
let endcolor = getDefaultColor(props.linearGradient[2])
|
|
let str = [props.linearGradient[0], startcolor, endcolor] as string[]
|
|
return `linear-gradient(${str.join(',')})`
|
|
})
|
|
const _bgColorNoCanvas = computed(() : Map<string, string> => {
|
|
let maps = new Map<string, string>();
|
|
if (_linearGradient.value != '') {
|
|
maps.set('background-image', _linearGradient.value)
|
|
} else {
|
|
maps.set('background-color', _bgColor.value)
|
|
}
|
|
return maps
|
|
})
|
|
const _styleMap = computed(() : Map<string, string> => {
|
|
let maps = new Map<string, string>();
|
|
if (_linearGradient.value != '') {
|
|
maps.set('background-image', _linearGradient.value)
|
|
} else {
|
|
maps.set('background-color', _bgColor.value)
|
|
}
|
|
if (_showTopBorder.value) {
|
|
maps.set('border-top', `1px solid ${_borderColor.value}`)
|
|
}
|
|
return maps
|
|
})
|
|
|
|
const _listWatch = computed(() : TABBAR_ITEM_INFO[] => {
|
|
|
|
return xTabbarConfig.list
|
|
})
|
|
const _isDark = computed(() : boolean => {
|
|
|
|
return xConfig.dark == 'dark'
|
|
})
|
|
const _tabbarheigh = computed(() : string => {
|
|
|
|
let safetop = _outIndex.value > -1 ? 25 : 0
|
|
let total = safeHeight.value + height.value + safetop
|
|
return total.toString() + 'px';
|
|
})
|
|
const getNodeInfo = () => {
|
|
uni.createSelectorQuery().in(proxy)
|
|
.select(".xTabbar")
|
|
.boundingClientRect().exec((ret) => {
|
|
let nodeinfo = ret[0] as NodeInfo
|
|
/**
|
|
* 同步组件高给外部使用,请使用v-model:autoTabbarHeight
|
|
* 组件高度 = 安全栏高度 + 导航栏高度,外部最好computed使用,因为是异步的
|
|
*/
|
|
emits("update:autoTabbarHeight", nodeinfo.height!)
|
|
})
|
|
|
|
|
|
}
|
|
|
|
const getQuqi = () => {
|
|
getNodeInfo()
|
|
uni.createSelectorQuery().in(proxy)
|
|
.selectAll(".xTabbarItem")
|
|
.boundingClientRect().exec((ret) => {
|
|
let nodeinfo = ret[0] as NodeInfo[]
|
|
if(nodeinfo.length==0) return;
|
|
let nowx = nodeinfo[Math.max(_outIndex.value, 0)];
|
|
tuqiHeight.value = nowx.height!;
|
|
tuqiWeight.value = nowx.width!;
|
|
getNodeInfo()
|
|
})
|
|
}
|
|
|
|
const designCanvas = () => {
|
|
let img = null as any|null;
|
|
function drawerCanvas(ele:UniCanvasElement,ctx:CanvasRenderingContext2D){
|
|
|
|
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
|
|
|
|
ele.width = ele.offsetWidth * dpr;
|
|
ele.height = ele.offsetHeight * dpr;
|
|
ctx.scale(dpr, dpr);
|
|
|
|
xcanvas.value = new xTabbarCanvas(ele,ctx, safeHeight.value, ele.width, ele.height , ele.offsetWidth, ele.offsetHeight,img )
|
|
|
|
function drawer() {
|
|
xcanvas.value!.draw({
|
|
active:_outIndex.value,
|
|
bgColor:_bgColor.value,
|
|
img:'',
|
|
bgLigth:_linearGradientByar.value,
|
|
listCount:_list.value.length,
|
|
bgType:_linearGradientByar.value.length>0?2:1,
|
|
tuqiType:props.isOutSpace&&props.outReserve?2:1
|
|
})
|
|
}
|
|
drawer();
|
|
// #ifdef APP-ANDROID
|
|
xcanvas.value!.addRestoredListener(() => {
|
|
drawer();
|
|
})
|
|
// #endif
|
|
// 下面是兼容Ios不可复杂运算符.
|
|
let safetop = _outIndex.value > -1 ? 25 : 0;
|
|
let total = safeHeight.value + height.value + safetop
|
|
xTabbarConfig.tabaarHeight = total
|
|
}
|
|
|
|
|
|
uni.createCanvasContextAsync({
|
|
id: canvasUid.value,
|
|
component: proxy,
|
|
success: (context : CanvasContext) => {
|
|
const canvasContext = context.getContext('2d')!;
|
|
const canvas = canvasContext.canvas;
|
|
// #ifdef MP-WEIXIN
|
|
img = context.createImage()
|
|
// #endif
|
|
try{
|
|
drawerCanvas(canvas,canvasContext)
|
|
}catch(e){
|
|
|
|
console.log(e)
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
const resizeinit = () => {
|
|
let sys = uni.getWindowInfo();
|
|
safeHeight.value = sys.safeAreaInsets.bottom
|
|
|
|
if(props.isCanvasRender){
|
|
resizeId.value += 1;
|
|
clearTimeout(tidresizeId.value)
|
|
// #ifdef APP-IOS
|
|
tidresizeId.value = setTimeout(function () {
|
|
designCanvas()
|
|
getQuqi()
|
|
}, (isFirst.value?props.firstRenderTimeout:80));
|
|
// #endif
|
|
// #ifdef APP-ANDROID||MP-WEIXIN
|
|
|
|
tidresizeId.value = setTimeout(function () {
|
|
designCanvas()
|
|
getQuqi()
|
|
}, (isFirst.value?props.firstRenderTimeout:100));
|
|
// #endif
|
|
// #ifdef WEB
|
|
nextTick(() => {
|
|
designCanvas()
|
|
getQuqi()
|
|
})
|
|
// #endif
|
|
}
|
|
|
|
|
|
|
|
isFirst.value = false
|
|
}
|
|
const getList = () => {
|
|
let list = [] as TABBAR_ITEM[]
|
|
|
|
let localList = xTabbarConfig.list
|
|
if (props.list.length > 0) {
|
|
localList = props.list
|
|
}
|
|
localList.forEach((el : TABBAR_ITEM_INFO) => {
|
|
list.push({
|
|
title: el.title == null ? "" : el.title! as string,
|
|
icon: el.icon == null ? "" : el.icon as string,
|
|
selectedIcon: el.selectedIcon == null ? "" : el.selectedIcon as string,
|
|
color: el.color == null ? _color.value : getDefaultColor(el.color! as string),
|
|
selectedColor: el.selectedColor == null ? _selectedColor.value : getDefaultColor(el.selectedColor! as string),
|
|
openType: el.openType == null ? "" : el.openType! as NAVIGATE_TYPE,
|
|
url: el.url == null ? "" : el.url! as string,
|
|
disabled: el.disabled == null ? false : el.disabled! as boolean,
|
|
dotType: el.dotType == null ? "" : el.dotType! as string,
|
|
dotLabel: el.dotLabel == null ? "" : el.dotLabel! as string,
|
|
} as TABBAR_ITEM)
|
|
})
|
|
_list.value = [...list] as TABBAR_ITEM[]
|
|
nextTick(() => {
|
|
getQuqi()
|
|
})
|
|
}
|
|
const itemDbClick = (index:number) =>{
|
|
console.log('dbclick',1)
|
|
}
|
|
let dbClickId = 23
|
|
let dbClickTime = 0
|
|
const ontouchstart = (index:number) =>{
|
|
}
|
|
const ontouchend = (index:number) =>{
|
|
let time = Date.now()
|
|
let diff = time - dbClickTime;
|
|
if(diff>100&&diff<250){
|
|
emits('doubleClick',index)
|
|
}
|
|
dbClickTime = time;
|
|
}
|
|
const itemClick = (evt:UniPointerEvent,index : number) => {
|
|
|
|
// #ifdef WEB
|
|
// @ts-ignore
|
|
if(evt.type == 'click'&&evt.pointerType == 'mouse'){
|
|
let time = Date.now()
|
|
let diff = time - dbClickTime;
|
|
if(diff>100&&diff<250){
|
|
emits('doubleClick',index)
|
|
}
|
|
dbClickTime = time;
|
|
}
|
|
|
|
// #endif
|
|
/**
|
|
* 项目点击时触发.
|
|
*/
|
|
emits('click',index)
|
|
if(xTabbarConfig.tabbarActiveIndex == index) return
|
|
|
|
xTabbarConfig.tabbarActiveIndex = index;
|
|
/**
|
|
* 切换项目时触发。
|
|
* @param index 当前选中的索引
|
|
*/
|
|
emits('change', index);
|
|
let item = _list.value[index]
|
|
if (item.url != null && item.url != '' && !item.disabled) {
|
|
switch (item.openType) {
|
|
case 'navigateBack':
|
|
uni.navigateBack({})
|
|
break;
|
|
case 'navigate':
|
|
uni.navigateTo({
|
|
url: item.url
|
|
})
|
|
break;
|
|
case 'reLaunch':
|
|
uni.reLaunch({
|
|
url: item.url
|
|
})
|
|
break;
|
|
case 'redirect':
|
|
uni.redirectTo({
|
|
url: item.url
|
|
})
|
|
break;
|
|
case 'switchTab':
|
|
uni.switchTab({
|
|
url: item.url
|
|
})
|
|
break;
|
|
default: {
|
|
|
|
uni.navigateTo({
|
|
url: item.url
|
|
} as NavigateToOptions)
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const _activeIndex = computed(() : number => {
|
|
nextTick(() => {
|
|
getQuqi()
|
|
})
|
|
return props.list.length == 0 ? xTabbarConfig.tabbarActiveIndex : props.activeIndex;
|
|
})
|
|
|
|
watch(() : number => props.outIndex, () => {
|
|
contentIsChange.value = true;
|
|
if (!isShowInpage.value) return;
|
|
resizeinit()
|
|
})
|
|
watch(_isDark, () => {
|
|
contentIsChange.value = true;
|
|
if (!isShowInpage.value) return;
|
|
resizeinit()
|
|
})
|
|
watch(_listWatch, () => {
|
|
if (props.list.length > 0) return;
|
|
getList()
|
|
}, { deep: true })
|
|
watch(() : TABBAR_ITEM_INFO[] => props.list, () => {
|
|
// if (props.list.length > 0) return;
|
|
getList()
|
|
}, { deep: true })
|
|
|
|
onBeforeMount(() => {
|
|
let sys = uni.getWindowInfo();
|
|
safeHeight.value = sys.safeAreaInsets.bottom
|
|
})
|
|
|
|
onMounted(() => {
|
|
getList()
|
|
resizeinit()
|
|
uni.$on('onResize', resizeinit)
|
|
uni.$on('onReady', resizeinit)
|
|
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
clearTimeout(tid.value)
|
|
clearTimeout(tidresizeId.value)
|
|
clearTimeout(yanchiId.value)
|
|
uni.$off('onResize', resizeinit)
|
|
uni.$on('onReady', resizeinit)
|
|
xcanvas.value?.destory()
|
|
xcanvas.value = null;
|
|
})
|
|
|
|
onPageShow(() => {
|
|
isShowInpage.value = true;
|
|
if(contentIsChange.value){
|
|
resizeinit()
|
|
contentIsChange.value = false;
|
|
}
|
|
})
|
|
|
|
onPageHide(() => {
|
|
isShowInpage.value = false;
|
|
})
|
|
</script>
|
|
<template>
|
|
<view class="xTabbar" :style="{position: position,zIndex:zIndex}">
|
|
<!-- 凸起占位 -->
|
|
<view v-if="_outIndex!=-1" class="xTabbarTuqi"
|
|
:style="{top:outReserve?'-18px':'-20px',height:(100)+'px',zIndex:(zIndex+1).toString(),width:(100/_list.length)+'%',left:(100/_list.length*_outIndex)+'%'}">
|
|
<!--
|
|
@slot 凸起图标插槽。
|
|
@prop {boolean} active - 当前是否选中
|
|
@prop {number} size - 凸起图标大小,单位px
|
|
-->
|
|
<slot name="out" :active="_outIndex==_activeIndex" :size="tuqiHeight">
|
|
<view
|
|
|
|
@touchstart="ontouchstart(outIndex)"
|
|
@touchend="ontouchend(outIndex)"
|
|
@click="itemClick($event as UniPointerEvent,outIndex)" class="xTabbarTuqiItem"
|
|
:style="{height:tuqiHeight+'px',width:tuqiHeight+'px',backgroundColor:_outBgColor}">
|
|
<x-icon :color="_outIconColor" :font-size="iconSize"
|
|
:name="_activeIndex==_outIndex?_nowOutItem.selectedIcon:_nowOutItem.icon"></x-icon>
|
|
</view>
|
|
</slot>
|
|
</view>
|
|
|
|
<canvas v-if="props.isCanvasRender" :id="canvasUid" ref="canvasIds" :key="resizeId" :style="{width:'100%',height:_tabbarheigh,pointerEvents: 'none'}"></canvas>
|
|
<view v-if="!props.isCanvasRender" :key="resizeId" :style="{width:'100%',height:_tabbarheigh,pointerEvents: 'none'}">
|
|
<view v-if="_outIndex>-1" style="height:25px"></view>
|
|
<view ref="canvasIdsEle" v-if="_outIndex>-1" :style="[{height:(safeHeight+height)+'px'},_styleMap]"></view>
|
|
<view ref="canvasIdsEle" v-if="_outIndex==-1" :style="[{height:(height)+'px'},_styleMap]"></view>
|
|
</view>
|
|
|
|
<view class="xTabbarBox" :style="{position:'absolute','background-color': 'transparent',zIndex:zIndex}">
|
|
<view class="xTabbarWrap">
|
|
<view
|
|
@touchstart="ontouchstart(index)"
|
|
@touchend="ontouchend(index)"
|
|
@click="itemClick($event as UniPointerEvent,index)"
|
|
class="xTabbarItem" v-for="(item,index) in _list" :key="index"
|
|
:style="{height:height+'px'}">
|
|
<!--
|
|
@slot 子项目插槽,以便完全自定义化样式。
|
|
@prop {number} activeindex - 当前激活的项
|
|
@prop {boolean} isactive - 当前是否被激活
|
|
@prop {number} selfindex - 索引
|
|
-->
|
|
<slot name="item" :isactive="_activeIndex==index" :selfindex="index" :children="item"
|
|
:activeindex="_activeIndex">
|
|
<x-badge :dot="item.dotType=='dot'" :label="item.dotLabel" :offset="[0,5]">
|
|
<view style="height: 30px;padding: 0px 4px;">
|
|
<x-icon v-if="_outIndex!=index"
|
|
:dark-color="_activeIndex==index?item.selectedColor:item.color"
|
|
:color="_activeIndex==index?item.selectedColor:item.color" :font-size="iconSize"
|
|
:name="_activeIndex==index?item.selectedIcon:item.icon"></x-icon>
|
|
</view>
|
|
<text
|
|
:style="{fontSize:_fontSize,color:_activeIndex==index?item.selectedColor:item.color,paddingTop:'4px'}">{{item.title}}</text>
|
|
</x-badge>
|
|
</slot>
|
|
</view>
|
|
|
|
</view>
|
|
<!-- _bgColorNoCanvas -->
|
|
<view v-if="props.isCanvasRender" :style="[{height:safeHeight+'px'}]"></view>
|
|
<view v-if="!props.isCanvasRender" :style="[{height:safeHeight+'px'},_bgColorNoCanvas]"></view>
|
|
</view>
|
|
</view>
|
|
|
|
</template>
|
|
<style scoped>
|
|
.xTabbarBox {
|
|
left: 0;
|
|
bottom: 0;
|
|
width: 100%;
|
|
/* z-index: 20; */
|
|
}
|
|
|
|
.xTabbarTuqi {
|
|
position: absolute;
|
|
width: 100%;
|
|
pointer-events: none;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
/* z-index: 21; */
|
|
/* #ifdef WEB */
|
|
cursor: pointer;
|
|
/* #endif */
|
|
}
|
|
|
|
.xTabbarTuqiItem {
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.xTabbarPlace {
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
.xTabbarTuqiItem {
|
|
border-radius: 300px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.xTabbar {
|
|
/* position: fixed; */
|
|
/* z-index: 20; */
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
background-color: transparent;
|
|
|
|
}
|
|
|
|
.xTabbarWrap {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-around;
|
|
align-items: flex-start;
|
|
|
|
}
|
|
|
|
.xTabbarItem {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex: 1;
|
|
/* #ifdef WEB */
|
|
cursor: pointer;
|
|
/* #endif */
|
|
}
|
|
</style> |