lc-fab-touch的贴边隐藏原理
悬浮按钮的贴边原理简单来说就是用屏幕的宽度或高度减去球的一半的宽度或高度,从而实现隐藏效果。
也就如上图所示,悬浮按钮和屏幕的宽度和高度表示。
上图指向的就是对于悬浮按钮的初始位置计算
// that.safeArea.minLeft 代表靠左
// that.safeArea.maxLeft 代表靠右
// that.safeArea.minTop 代表靠下
// that.safeArea.maxTop 代表靠上
这是箭头指向位置,即是靠边隐藏的实现代码。想要取消就不能将用悬浮按钮的宽和高除以2去计算,即直接减去整个按钮的宽或高,从而使按钮完全展露出来。
解决办法:
// 计算悬浮球初始位置
initDragBall(){
console.log('initDragBall');
let that = this;
try {
uni.createSelectorQuery().in(this).select('#drag-ball').fields({
size: true
}, dragBallSize => {
// dragBallSize就是球的大小,
// 球的宽
that.width = dragBallSize.width;
// 球的高
that.height = dragBallSize.height;
uni.getSystemInfo({
success: function(res) {
that.safeArea.windowWidth = res.windowWidth;
that.safeArea.windowHeight = res.windowHeight;
if (that.orientation === 'landscape') {
// dragBallSize.height / 2就是贴边(也就是显示一半)
// that.safeArea.minTop = -parseInt(dragBallSize.height / 2);
// that.safeArea.maxTop = res.windowHeight - parseInt(dragBallSize.height / 2);
// 这里是为了初始位置不再自动贴边(就是显示半个)
that.safeArea.minTop = -parseInt(dragBallSize.height);
that.safeArea.maxTop = res.windowHeight - parseInt(dragBallSize.height);
that.safeArea.minLeft = parseInt(0.06 * res.windowWidth);
that.safeArea.maxLeft = parseInt(0.94 * res.windowWidth) - dragBallSize.width;
that.left = that.safeArea.minLeft;
that.top = that.safeArea.minTop;
// that.left = that.safeArea.maxLeft;
// that.top = that.safeArea.maxTop;
} else {
that.safeArea.minTop = parseInt(0.06 * res.windowHeight);
that.safeArea.maxTop = parseInt(0.94 * res.windowHeight) - dragBallSize.height;
// dragBallSize.height / 2就是贴边(也就是显示一半)
// that.safeArea.minLeft = -parseInt(dragBallSize.width / 2);
// that.safeArea.maxLeft = res.windowWidth - parseInt(dragBallSize.width / 2);
// 这里是为了初始位置不再自动贴边(就是显示半个)
that.safeArea.minLeft = -parseInt(dragBallSize.width);
that.safeArea.maxLeft = res.windowWidth - parseInt(dragBallSize.width );
// 右下角
that.left = that.safeArea.maxLeft;
that.top = that.safeArea.minTop;
if(that.vertical2 == 'bottom') {
that.top = that.safeArea.maxTop / 1.2;
}
// 左上角
// that.left = that.safeArea.minLeft;
// that.top = that.safeArea.maxTop;
// that.safeArea.minLeft 代表靠左
// that.safeArea.maxLeft 代表靠右
// that.safeArea.minTop 代表靠下
// that.safeArea.maxTop 代表靠上
}
that.showDragBall();
}
});
}).exec();
} catch (e) {
//TODO handle the exception
console.log(e);
}
},
下面是我将其他隐藏修改:
<template>
<view class="uni-cursor-point ">
<view v-if="(popMenu && (leftBottom||rightBottom||leftTop||rightTop) && 0 < content.length) && showMenu"
:class="{
'lc-fab-touch--leftBottom': leftBottom,
'lc-fab-touch--rightBottom': rightBottom,
'lc-fab-touch--leftTop': leftTop,
'lc-fab-touch--rightTop': rightTop
}"
class="lc-fab-touch " :style="{top : contTop2+'px',left :contLeft2}" style="height: 55px;border: 0px solid green;">
<view :class="{
'lc-fab-touch__content--left': horizontal2 === 'left',
'lc-fab-touch__content--right': horizontal2 === 'right',
'lc-fab-touch__content--flexDirection': direction === 'vertical',
'lc-fab-touch__content--flexDirectionStart': flexDirectionStart,
'lc-fab-touch__content--flexDirectionEnd': flexDirectionEnd,
'lc-fab-touch__content--other-platform': !isAndroidNvue
}"
:style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor}" class="lc-fab-touch__content"
elevation="5" style="border: 0px solid red;">
<view v-if="flexDirectionStart || horizontalLeft" class="lc-fab-touch__item lc-fab-touch__item--first" />
<view v-for="(item, index) in content" :key="index" :class="{ 'lc-fab-touch__item--active': isShow }" class="lc-fab-touch__item"
@click="_onItemClick(index, item)">
<image :src="item.active ? item.selectedIconPath : item.iconPath" class="lc-fab-touch__item-image" mode="widthFix" />
<text class="lc-fab-touch__item-text" :style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text>
</view>
<view v-if="flexDirectionEnd || horizontalRight" class="lc-fab-touch__item lc-fab-touch__item--first" />
</view>
</view>
<view
:class="[{
'lc-fab-touch__circle--leftBottom': leftBottom,
'lc-fab-touch__circle--rightBottom': rightBottom,
'lc-fab-touch__circle--leftTop': leftTop,
'lc-fab-touch__circle--rightTop': rightTop,
'lc-fab-touch__content--other-platform': !isAndroidNvue,
'active':!dragMoveSatus
}]"
class="lc-fab-touch__circle lc-fab-touch__plus drag-ball-box" :style="{ 'background-color': styles.buttonColor,left:left+'px',top :top+'px'} "
@click="_onClick" id="drag-ball" @touchstart="dragStart" @touchmove="dragMove" @touchend="dragEnd">
<view class="fab-circle-v" :class="{'lc-fab-touch__plus--active': isShow && content.length > 0}"></view>
<view class="fab-circle-h" :class="{'lc-fab-touch__plus--active': isShow && content.length > 0}"></view>
</view>
</view>
</template>
<script>
let platform = 'other'
// #ifdef APP-NVUE
platform = uni.getSystemInfoSync().platform
// #endif
/**
* Fab 悬浮按钮
* @description 点击可展开一个图形按钮菜单
* @tutorial https://ext.dcloud.net.cn/plugin?id=144
* @property {Object} pattern 可选样式配置项
* @property {Object} horizontal = [left | right] 水平对齐方式
* @value left 左对齐
* @value right 右对齐
* @property {Object} vertical = [bottom | top] 垂直对齐方式
* @value bottom 下对齐
* @value top 上对齐
* @property {Object} direction = [horizontal | vertical] 展开菜单显示方式
* @value horizontal 水平显示
* @value vertical 垂直显示
* @property {Array} content 展开菜单内容配置项
* @property {Boolean} popMenu 是否使用弹出菜单
* @event {Function} trigger 展开菜单点击事件,返回点击信息
* @event {Function} fabClick 悬浮按钮点击事件
*/
export default {
name: 'UniFab',
props: {
pattern: {
type: Object,
default () {
return {}
}
},
horizontal: {
type: String,
default: 'right'
},
vertical: {
type: String,
default: 'bottom'
},
direction: {
type: String,
default: 'horizontal'
},
content: {
type: Array,
default () {
return []
}
},
show: {
type: Boolean,
default: false
},
popMenu: {
type: Boolean,
default: true
}
},
data() {
return {
fabShow: false,
isShow: false,
isAndroidNvue: platform === 'android',
styles: {
color: '#3c3e49',
selectedColor: '#007AFF',
backgroundColor: '#fff',
buttonColor: '#007AFF'
},
showMenu: false,
horizontal2: 'left',
vertical2: 'bottom',
orientation: 'portrait',
safeArea: {
minTop: 0,
maxTop: 0,
minLeft: 0,
maxLeft: 0,
windowWidth: 0,
windowHeight: 0
},
dragMoveSatus: true,
dragEndInfo: {
type: null,
coordinate: null
},
width: 0,
height: 0,
left: 0,
top: 0
}
},
computed: {
contLeft2() {
if(this.horizontal2 == 'right') {
// console.log('left'+this.left)
if(this.left == this.safeArea.maxLeft || this.left >= this.safeArea.maxLeft-20 ) {
return this.left+30 + 'px';
} else {
if(this.direction == 'vertical') { // 垂直
return this.left + 'px';
} else {
return this.left - (this.content.length) * 55-10 + 'px';
}
return 'unset';
}
} else {
return this.left - 10+'px';
}
},
contTop2() {
if(this.direction == 'vertical') {
if(this.vertical2 == 'top') {
return this.top + ((this.content.length) * 30);
}
return this.top - ((this.content.length) * 30);
}
return this.top;
},
contentWidth(e) {
return (this.content.length + 1) * 55 + 10 + 'px'
},
contentWidthMin() {
return 55 + 'px'
},
// 动态计算宽度
boxWidth() {
return this.getPosition(3, 'horizontal')
},
// 动态计算高度
boxHeight() {
return this.getPosition(3, 'vertical')
},
// 计算左下位置
leftBottom() {
return this.getPosition(0, 'left', 'bottom')
},
// 计算右下位置
rightBottom() {
return this.getPosition(0, 'right', 'bottom')
},
// 计算左上位置
leftTop() {
return this.getPosition(0, 'left', 'top')
},
rightTop() {
return this.getPosition(0, 'right', 'top')
},
flexDirectionStart() {
return this.getPosition(1, 'vertical', 'top')
},
flexDirectionEnd() {
return this.getPosition(1, 'vertical', 'bottom')
},
horizontalLeft() {
return this.getPosition(2, 'horizontal', 'left')
},
horizontalRight() {
return this.getPosition(2, 'horizontal', 'right')
}
},
watch: {
pattern(newValue, oldValue) {
//console.log(JSON.stringify(newValue))
this.styles = Object.assign({}, this.styles, newValue)
}
},
created() {
this.isShow = this.show;
if (this.top === 0) {
this.fabShow = true
}
// 初始化样式
this.styles = Object.assign({}, this.styles, this.pattern);
this.horizontal2 = this.horizontal;
this.vertical2 = this.vertical;
},
mounted: function() {
this.initDragBall();
},
methods: {
_onClick() {
this.$emit('fabClick')
if (!this.popMenu) {
return
}
if(this.showMenu) {
this.showMenu = false;
} else {
this.showMenu = true;
}
this.isShow = !this.isShow;
// linc 20210714 当展开菜单时不自动隐藏
if(!this.showMenu) {
// 隐藏
setTimeout(function(that) {
if(!that.dragMoveSatus && !that.showMenu){
that[that.dragEndInfo.type] = that.dragEndInfo.coordinate;
}
}, 1500, this);
}
if(this.left >= this.safeArea.maxLeft-40) {
this.horizontal2 ='right';
// this.horizontal ='right';
}
if(this.left <= this.safeArea.minLeft+30) {
this.horizontal2 ='left';
// this.horizontal ='left';
}
},
open() {
this.isShow = true;
},
close() {
this.isShow = false;
},
/**
* 按钮点击事件
*/
_onItemClick(index, item) {
this._onClick();
if(item.type == 'top') {
uni.pageScrollTo({
scrollTop: 0,
duration: 300
});
} else {
this.$emit('trigger', {
index,
item
})
}
},
/**
* 获取 位置信息
*/
getPosition(types, paramA, paramB) {
if (types === 0) {
return this.horizontal2 === paramA && this.vertical2 === paramB
} else if (types === 1) {
return this.direction === paramA && this.vertical2 === paramB
} else if (types === 2) {
return this.direction === paramA && this.horizontal2 === paramB
} else {
return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin
}
},
// 计算悬浮球初始位置
initDragBall(){
console.log('initDragBall');
let that = this;
try {
uni.createSelectorQuery().in(this).select('#drag-ball').fields({
size: true
}, dragBallSize => {
// dragBallSize就是球的大小,
// 球的宽
that.width = dragBallSize.width;
// 球的高
that.height = dragBallSize.height;
uni.getSystemInfo({
success: function(res) {
that.safeArea.windowWidth = res.windowWidth;
that.safeArea.windowHeight = res.windowHeight;
if (that.orientation === 'landscape') {
// dragBallSize.height / 2就是贴边(也就是显示一半)
// that.safeArea.minTop = -parseInt(dragBallSize.height / 2);
// that.safeArea.maxTop = res.windowHeight - parseInt(dragBallSize.height / 2);
// 这里是为了初始位置不再自动贴边(就是显示半个)
that.safeArea.minTop = -parseInt(dragBallSize.height);
that.safeArea.maxTop = res.windowHeight - parseInt(dragBallSize.height);
that.safeArea.minLeft = parseInt(0.06 * res.windowWidth);
that.safeArea.maxLeft = parseInt(0.94 * res.windowWidth) - dragBallSize.width;
that.left = that.safeArea.minLeft;
that.top = that.safeArea.minTop;
// that.left = that.safeArea.maxLeft;
// that.top = that.safeArea.maxTop;
} else {
that.safeArea.minTop = parseInt(0.06 * res.windowHeight);
that.safeArea.maxTop = parseInt(0.94 * res.windowHeight) - dragBallSize.height;
// dragBallSize.height / 2就是贴边(也就是显示一半)
// that.safeArea.minLeft = -parseInt(dragBallSize.width / 2);
// that.safeArea.maxLeft = res.windowWidth - parseInt(dragBallSize.width / 2);
// 这里是为了初始位置不再自动贴边(就是显示半个)
that.safeArea.minLeft = -parseInt(dragBallSize.width);
that.safeArea.maxLeft = res.windowWidth - parseInt(dragBallSize.width );
// 右下角
that.left = that.safeArea.maxLeft;
that.top = that.safeArea.minTop;
if(that.vertical2 == 'bottom') {
that.top = that.safeArea.maxTop / 1.2;
}
// 左上角
// that.left = that.safeArea.minLeft;
// that.top = that.safeArea.maxTop;
// that.safeArea.minLeft 代表靠左
// that.safeArea.maxLeft 代表靠右
// that.safeArea.minTop 代表靠下
// that.safeArea.maxTop 代表靠上
}
that.showDragBall();
}
});
}).exec();
} catch (e) {
//TODO handle the exception
console.log(e);
}
},
showDragBall() {
setTimeout(function(that) {
that.dragMoveSatus = false;
}, 1000, this);
},
// 手指触摸悬浮球动作开始
dragStart(event) {
if (this.orientation === 'landscape') {
if (this.top === this.safeArea.minTop) {
this.top += parseInt(this.height / 2); // 上面
} else if (this.top === this.safeArea.maxTop) {
this.top -= parseInt(this.height / 2); // 下面
}
} else {
if (this.left === this.safeArea.minLeft) {
this.left += parseInt(this.width / 2); // 左面
} else if (this.left === this.safeArea.maxLeft) {
this.left -= parseInt(this.width / 2); // 右面
}
}
},
// 手指触摸后移动
dragMove(event) {
if (!this.dragMoveSatus) {
this.dragMoveSatus = true;
}
// 定位触摸焦点及移动位置
this.top = event.touches[0].clientY - parseInt(this.height /2);
this.left = event.touches[0].clientX - parseInt(this.width /2);
},
// 手指触摸悬浮球动作结束,贴边并延迟隐藏
dragEnd(event) {
console.log('event',event);
this.dragMoveSatus = false;
if (this.orientation === 'landscape') {
let halfWindowHeight = parseInt(this.safeArea.windowHeight / 2),
// 贴边隐藏
// halfDragBallHeight = parseInt(this.height /2);
// 只贴边不隐藏
halfDragBallHeight = parseInt(this.height);
this.dragEndInfo.type = 'top';
if (this.top < halfWindowHeight) {
this.dragEndInfo.coordinate = this.safeArea.minTop;
this.top = this.safeArea.minTop + halfDragBallHeight;
this.vertical2 = 'top'; // 根据拖拽后高度自动适配展示模式
} else {
this.dragEndInfo.coordinate = this.safeArea.maxTop;
this.top = this.safeArea.maxTop - halfDragBallHeight;
this.vertical2 = 'bottom'; // 根据拖拽后高度自动适配展示模式
}
} else {
let halfWindowWidth = parseInt(this.safeArea.windowWidth / 2),
// 贴边隐藏
// halfDragBallWidth = parseInt(this.width / 2);
// 只贴边不隐藏
halfDragBallWidth = parseInt(this.width);
this.dragEndInfo.type = 'left';
if (this.left < halfWindowWidth) {
this.dragEndInfo.coordinate = this.safeArea.minLeft;
this.left = this.safeArea.minLeft + halfDragBallWidth;
this.horizontal2 = 'left'; // 根据拖拽后高度自动适配展示模式
} else {
this.dragEndInfo.coordinate = this.safeArea.maxLeft;
// 靠右一个球的距离
// this.left = this.safeArea.maxLeft - halfDragBallWidth;
// 跟右边没有距离(贴边)
this.left = this.safeArea.maxLeft;
this.horizontal2 = 'right'; // 根据拖拽后高度自动适配展示模式
}
let halfWindowHeight = parseInt(this.safeArea.windowHeight);
if (this.top < halfWindowHeight) {
this.vertical2 = 'top';
} else {
this.vertical2 = 'bottom';
}
}
// 位置容错计算
if (this.top < this.safeArea.minTop) {
this.top = this.safeArea.minTop;
} else if (this.top > this.safeArea.maxTop) {
this.top = this.safeArea.maxTop;
}
if (this.left < this.safeArea.minLeft) {
this.left = this.safeArea.minLeft;
console.log(this.left + 'right');
} else if (this.left > this.safeArea.maxLeft) {
this.left = this.safeArea.maxLeft;
console.log(this.left + 'left');
}
// console.log(this.vertical2 + this.horizontal2);
}
}
}
</script>
<style lang="scss" scoped>
.lc-fab-touch {
position: fixed;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
z-index: 10;
}
.uni-cursor-point {
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.lc-fab-touch--active {
opacity: 1;
}
.lc-fab-touch--leftBottom {
left: 5px;
bottom: 20px;
/* #ifdef H5 */
left: calc(5px + var(--window-left));
bottom: calc(20px + var(--window-bottom));
/* #endif */
padding: 10px;
}
.lc-fab-touch--leftTop {
left: 5px;
top: 30px;
/* #ifdef H5 */
left: calc(5px + var(--window-left));
top: calc(30px + var(--window-top));
/* #endif */
padding: 10px;
}
.lc-fab-touch--rightBottom {
right: 0px;
bottom: 20px;
/* #ifdef H5 */
// right: calc(5px + var(--window-right));
bottom: calc(20px + var(--window-bottom));
/* #endif */
padding: 10px 0;
}
.lc-fab-touch--rightTop {
// right: 0px;
top: 30px;
/* #ifdef H5 */
// right: calc(5px + var(--window-right));
top: calc(30px + var(--window-top));
/* #endif */
padding: 0;
padding-top: 10px;
}
.lc-fab-touch__circle {
position: fixed;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
background-color: #3c3e49;
border-radius: 55px;
z-index: 11;
}
.lc-fab-touch__circle--leftBottom {
left: 15px;
bottom: 30px;
/* #ifdef H5 */
left: calc(15px + var(--window-left));
bottom: calc(30px + var(--window-bottom));
/* #endif */
}
.lc-fab-touch__circle--leftTop {
left: 15px;
top: 40px;
/* #ifdef H5 */
left: calc(15px + var(--window-left));
top: calc(40px + var(--window-top));
/* #endif */
}
.lc-fab-touch__circle--rightBottom {
right: 15px;
bottom: 30px;
/* #ifdef H5 */
right: calc(15px + var(--window-right));
bottom: calc(30px + var(--window-bottom));
/* #endif */
}
.lc-fab-touch__circle--rightTop {
right: 15px;
top: 40px;
/* #ifdef H5 */
right: calc(15px + var(--window-right));
top: calc(40px + var(--window-top));
/* #endif */
}
.lc-fab-touch__circle--left {
left: 0;
}
.lc-fab-touch__circle--right {
right: 0;
}
.lc-fab-touch__circle--top {
top: 0;
}
.lc-fab-touch__circle--bottom {
bottom: 0;
}
.lc-fab-touch__plus {
font-weight: bold;
}
.fab-circle-v {
position: absolute;
width: 3px;
height: 31px;
left: 26px;
top: 12px;
background-color: white;
transform: rotate(0deg);
transition: transform 0.3s;
}
.fab-circle-h {
position: absolute;
width: 31px;
height: 3px;
left: 12px;
top: 26px;
background-color: white;
transform: rotate(0deg);
transition: transform 0.3s;
}
.lc-fab-touch__plus--active {
transform: rotate(135deg);
}
.lc-fab-touch__content {
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: flex;
/* #endif */
flex-direction: row;
border-radius: 55px;
overflow: hidden;
transition-property: width, height;
transition-duration: 0.2s;
width: 55px;
border-color: #DDDDDD;
border-width: 1rpx;
border-style: solid;
}
.lc-fab-touch__content--other-platform {
border-width: 0px;
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.2);
}
.lc-fab-touch__content--left {
justify-content: flex-start;
}
.lc-fab-touch__content--right {
justify-content: flex-end;
}
.lc-fab-touch__content--flexDirection {
flex-direction: column;
justify-content: flex-end;
}
.lc-fab-touch__content--flexDirectionStart {
flex-direction: column;
justify-content: flex-start;
}
.lc-fab-touch__content--flexDirectionEnd {
flex-direction: column;
justify-content: flex-end;
}
.lc-fab-touch__item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
opacity: 0;
transition: opacity 0.2s;
}
.lc-fab-touch__item--active {
opacity: 1;
}
.lc-fab-touch__item-image {
width: 25px;
height: 25px;
margin-bottom: 3px;
}
.lc-fab-touch__item-text {
color: #FFFFFF;
font-size: 12px;
}
.lc-fab-touch__item--first {
width: 55px;
}
.drag-ball-box {
position: fixed;
overflow: hidden;
border-radius: 50%;
z-index: 109;
&.portrait {
width: 55px;
height: 55px;
}
&.landscape {
width: 40rpx;
height: 40rpx;
}
&.active {
transition: .3s;
}
.item-group {
width: 100%;
height: 100%;
.item-image {
width: 100%;
height: 100%;
}
}
}
</style>