411 lines
10 KiB
Vue
411 lines
10 KiB
Vue
<template>
|
|
<view class="p_wrapper" style="z-index: 19999" v-if="isShow">
|
|
<view
|
|
ref="mask"
|
|
catchtouchmove="true"
|
|
bubble="true"
|
|
class="mask"
|
|
style="z-index: 20000"
|
|
:style="maskStyle"
|
|
@touchmove.stop.prevent="moveHandle"
|
|
@click="close()"
|
|
:class="[notNvueAni ? 'not_animation_show_mask' : 'not_animation_hide_mask']"
|
|
></view>
|
|
<view
|
|
ref="popup"
|
|
id="popup"
|
|
class="content"
|
|
:class="{
|
|
not_def_show: type == 'zoom',
|
|
not_def_show_f: type != 'zoom',
|
|
not_nvue_animation_show: notNvueAni && type == 'zoom',
|
|
not_nvue_animation_hide: !notNvueAni && type == 'zoom',
|
|
not_nvue_animation_show_f: notNvueAni && type != 'zoom',
|
|
not_nvue_animation_hide_f: !notNvueAni && type != 'zoom',
|
|
}"
|
|
style="z-index: 20001"
|
|
:style="contentStyle"
|
|
>
|
|
<slot></slot>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
// #ifdef APP-PLUS-NVUE
|
|
const animation = weex.requireModule('animation')
|
|
// #endif
|
|
export default {
|
|
name: 'benben-position-popup',
|
|
props: {
|
|
// 是否显示蒙层
|
|
maskHide: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
// 弹窗模式 有 zoom fade
|
|
type: {
|
|
type: String,
|
|
default: 'zoom',
|
|
},
|
|
// 内容区 宽度 单位rpx
|
|
width: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// 内容区 高度 单位 rpx
|
|
height: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// 弹窗定位 顶部距离 可ref 调用时传入 ref没有传入这个就是默认定位 单位px
|
|
top: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// 弹窗定位 左侧距离 可ref 调用时传入 ref没有传入这个就是默认定位 单位px
|
|
left: {
|
|
type: Number,
|
|
default: 0,
|
|
},
|
|
// 是否有 原生状态栏
|
|
hasNav: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
// 自定义弹出方向
|
|
popupOrigin: {
|
|
type: String,
|
|
default: 'left top',
|
|
},
|
|
// 是否 开启自适应
|
|
adaption: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
origin: 'left top', // 动画的定位点
|
|
x: 0, // 弹窗弹出位置 定位点
|
|
y: 0, // 弹窗弹出位置 定位点
|
|
maxX: 0, // 最大 x 值 用于判断弹窗定位点
|
|
maxY: 0, // 最大 y 值 用于判断弹窗定位点
|
|
winH: 0, // 屏幕 总高度
|
|
winW: 0, // 屏幕 总宽度
|
|
windowTop: 0, // h5 下的顶部距离
|
|
notNvueAni: false, // 控制非nvue动画
|
|
hideTarget: 'scale(0,0)', // 隐藏目标
|
|
isShow: false, // 是否显示
|
|
statusBarHeight: 0, // 手机状态栏高度
|
|
}
|
|
},
|
|
methods: {
|
|
// 一些状态下 阻止 遮罩下滚动
|
|
moveHandle() {
|
|
return
|
|
},
|
|
// 打开弹窗
|
|
async open(e, isMe) {
|
|
console.log('benben-position-popup', e.changedTouches)
|
|
this.comMax()
|
|
let x, y
|
|
if (isMe && e) {
|
|
// 自己传入 x y 定位
|
|
x = e.x
|
|
y = e.y
|
|
} else if (e) {
|
|
// 自动传入长按事件 对象
|
|
// app nvue 的情况 拿取 长按位置
|
|
// #ifdef APP-NVUE
|
|
if (Array.isArray(e.changedTouches) && e.changedTouches.length > 0) {
|
|
// nvue 安卓下 获取pageX pageY 会有问题 所以 统一使用 screenX screenY
|
|
x = e.changedTouches[0].screenX
|
|
y = e.changedTouches[0].screenY - this.statusBarHeight - (this.hasNav ? 40 : 0)
|
|
} else {
|
|
const { position } = e
|
|
x = position.x
|
|
y = position.y
|
|
}
|
|
// #endif
|
|
// app 非nvue 的情况 拿取 长按位置
|
|
// #ifdef APP-VUE
|
|
if (Array.isArray(e.changedTouches) && e.changedTouches.length > 0) {
|
|
x = e.changedTouches[0].clientX
|
|
y = e.changedTouches[0].clientY
|
|
console.log('x', x)
|
|
console.log('y', y)
|
|
} else {
|
|
const { position } = e
|
|
x = position.x
|
|
y = position.y
|
|
}
|
|
// #endif
|
|
// 小程序的 情况 拿取 长按位置
|
|
// #ifdef MP
|
|
|
|
x = e.touches[0].clientX
|
|
y = e.touches[0].clientY
|
|
// #endif
|
|
// h5的 情况 拿取 长按位置
|
|
// #ifdef H5
|
|
if (Array.isArray(e.changedTouches) && e.changedTouches.length > 0) {
|
|
x = e.changedTouches[0].clientX
|
|
y = e.changedTouches[0].clientY + this.windowTop
|
|
}
|
|
// #endif
|
|
}
|
|
// 固定定位 props 传入
|
|
if (!e) {
|
|
x = this.left
|
|
y = this.top
|
|
}
|
|
|
|
const originArr = this.popupOrigin.split(' ')
|
|
let originX = originArr[0]
|
|
let originY = originArr[1]
|
|
if (this.adaption) {
|
|
if (x > this.maxX && y > this.maxY) {
|
|
// this.origin = 'right bottom';
|
|
originX = 'right'
|
|
originY = 'bottom'
|
|
} else if (x < uni.upx2px(this.width) && y < uni.upx2px(this.height)) {
|
|
originX = 'left'
|
|
originY = 'top'
|
|
} else if (x > this.maxX) {
|
|
originX = 'right'
|
|
} else if (y > this.maxY) {
|
|
originY = 'bottom'
|
|
} else if (x < uni.upx2px(this.width)) {
|
|
originX = 'left'
|
|
} else if (y < uni.upx2px(this.height)) {
|
|
originY = 'top'
|
|
}
|
|
this.origin = originX + ' ' + originY
|
|
}
|
|
// 检测到 极限位置 开边弹出方向
|
|
if (this.adaption) {
|
|
if (originY === 'top') {
|
|
this.y = y
|
|
} else {
|
|
this.y = y - uni.upx2px(this.height) > 0 ? y - uni.upx2px(this.height) : 0
|
|
}
|
|
if (originX === 'left') {
|
|
this.x = x
|
|
} else {
|
|
this.x = x - uni.upx2px(this.width) > 0 ? x - uni.upx2px(this.width) : 0
|
|
}
|
|
} else {
|
|
this.x = x
|
|
this.y = y
|
|
}
|
|
this.isShow = true
|
|
// #ifndef APP-NVUE
|
|
setTimeout(() => {
|
|
this.notNvueAni = true
|
|
}, 10)
|
|
// #endif
|
|
this.hideTarget = 'scale(0,0)'
|
|
setTimeout(() => {
|
|
this.animationShow()
|
|
}, 30)
|
|
},
|
|
close() {
|
|
// #ifdef APP-NVUE
|
|
this.animationHide()
|
|
// #endif
|
|
// #ifndef APP-NVUE
|
|
this.notNvueAni = false
|
|
setTimeout(() => {
|
|
this.isShow = false
|
|
}, 300)
|
|
// #endif
|
|
},
|
|
// 检测极限位置
|
|
comMax() {
|
|
let sizeArr = [this.width, this.height]
|
|
let width = uni.upx2px(Number(sizeArr[0]))
|
|
let height = uni.upx2px(Number(sizeArr[1]))
|
|
this.maxX = this.winW - width
|
|
this.maxY = this.winH - height
|
|
},
|
|
// nvue 打开 动画
|
|
animationShow() {
|
|
// #ifdef APP-PLUS-NVUE
|
|
let tranget = ''
|
|
if (this.type === 'zoom') {
|
|
tranget = 'scale(1,1)'
|
|
}
|
|
animation.transition(this.$refs.popup, {
|
|
styles: {
|
|
transformOrigin: this.origin,
|
|
transform: tranget,
|
|
opacity: 1,
|
|
},
|
|
duration: 200, //ms
|
|
timingFunction: 'ease',
|
|
})
|
|
|
|
if (this.$refs.mask) {
|
|
animation.transition(this.$refs.mask, {
|
|
styles: {
|
|
opacity: 1,
|
|
},
|
|
duration: 200, //ms
|
|
timingFunction: 'ease',
|
|
})
|
|
}
|
|
// #endif
|
|
},
|
|
// nvue 关闭 动画
|
|
animationHide() {
|
|
// #ifdef APP-PLUS-NVUE
|
|
this.$nextTick(() => {
|
|
animation.transition(
|
|
this.$refs.popup,
|
|
{
|
|
styles: {
|
|
transform: this.type === 'zoom' ? this.hideTarget : '',
|
|
transformOrigin: this.origin,
|
|
opacity: 0,
|
|
},
|
|
duration: 200, //ms
|
|
timingFunction: 'ease',
|
|
},
|
|
() => {
|
|
this.isShow = false
|
|
}
|
|
)
|
|
|
|
if (this.$refs.mask) {
|
|
animation.transition(this.$refs.mask, {
|
|
styles: {
|
|
opacity: 0,
|
|
},
|
|
duration: 200, //ms
|
|
timingFunction: 'ease',
|
|
})
|
|
}
|
|
})
|
|
// #endif
|
|
},
|
|
// 获取系统 信息
|
|
getSystemSize() {
|
|
this.$nextTick(() => {
|
|
const winSize = uni.getSystemInfoSync()
|
|
const { windowHeight, windowWidth, statusBarHeight } = winSize
|
|
this.statusBarHeight = statusBarHeight
|
|
this.winH = windowHeight
|
|
this.winW = windowWidth
|
|
// #ifdef H5
|
|
const { windowTop } = winSize
|
|
this.windowTop = windowTop
|
|
// #endif
|
|
})
|
|
},
|
|
},
|
|
// 组件初始化
|
|
created() {
|
|
this.getSystemSize()
|
|
},
|
|
computed: {
|
|
// 蒙层样式
|
|
maskStyle() {
|
|
return this.maskHide ? 'background-color: rgba(0, 0, 0, .4);' : ''
|
|
},
|
|
// 主弹窗样式
|
|
contentStyle() {
|
|
let width, height, x, y
|
|
switch (this.type) {
|
|
case 'zoom': // 当default时 样式
|
|
width = this.width
|
|
height = this.height
|
|
x = this.x
|
|
y = this.y
|
|
let origin = this.origin
|
|
return `left: ${x}px; top: ${y}px; width: ${width}rpx; height: ${height}rpx; transform-origin: ${origin};`
|
|
default:
|
|
width = this.width
|
|
height = this.height
|
|
x = this.x
|
|
y = this.y
|
|
return `left: ${x}px; top: ${y}px; width: ${width}rpx; height: ${height}rpx;`
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.p_wrapper {
|
|
/* position: fixed; */
|
|
position: fixed;
|
|
/* #ifndef APP-NVUE */
|
|
z-index: 10000;
|
|
/* #endif */
|
|
}
|
|
|
|
.mask {
|
|
/* #ifndef APP-NVUE */
|
|
z-index: 10000;
|
|
/* #endif */
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
}
|
|
|
|
.content {
|
|
/* #ifndef APP-NVUE */
|
|
z-index: 10001;
|
|
/* #endif */
|
|
position: fixed;
|
|
border-radius: 10rpx;
|
|
}
|
|
|
|
.not_def_show {
|
|
transform: scale(0, 0);
|
|
opacity: 0;
|
|
}
|
|
|
|
.not_def_show_f {
|
|
opacity: 0;
|
|
}
|
|
|
|
.not_animation_show_mask {
|
|
transition: opacity 200;
|
|
opacity: 1;
|
|
}
|
|
|
|
.not_animation_hide_mask {
|
|
transition: opacity 200;
|
|
opacity: 0;
|
|
}
|
|
|
|
/* #ifndef APP-NVUE */
|
|
.not_nvue_animation_show {
|
|
transition: all 200ms;
|
|
transform: scale(1, 1);
|
|
opacity: 1;
|
|
}
|
|
|
|
.not_nvue_animation_hide {
|
|
transition: all 200ms;
|
|
transform: scale(0, 0);
|
|
opacity: 0;
|
|
}
|
|
|
|
.not_nvue_animation_show_f {
|
|
transition: all 400ms;
|
|
opacity: 1;
|
|
}
|
|
|
|
.not_nvue_animation_hide_f {
|
|
transition: all 400ms;
|
|
opacity: 0;
|
|
}
|
|
|
|
/* #endif */
|
|
</style>
|