Skip to content

Instantly share code, notes, and snippets.

@CaptainLiao
Last active July 12, 2022 03:38
Show Gist options
  • Save CaptainLiao/204e93fdfacc0ca60b3f386191216785 to your computer and use it in GitHub Desktop.
Save CaptainLiao/204e93fdfacc0ca60b3f386191216785 to your computer and use it in GitHub Desktop.
灵活的弹窗
<!-- -->
<template>
<div v-if="show" :class="[state]">
<div class="hl-overlay" @click="tapOverlay" @touchmove.stop="noop"></div>
<div :class="['hl-popup fx-col', hlPopupClasses]" :style="style">
<div class="hl-popup-hd">
<slot name="title">{{title}}</slot>
<img v-if="closeable"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAElBMVEVHcEw0N0o0OEouLkY0N0kwMFAuyLj8AAAABnRSTlMAT4ALcBBngfhBAAAAZUlEQVQoz2NgoA4wgDGYIRSrI0xAJABMmSgJQPiMSs5QWhEiIASTgTLgEjAWXAGUiVAAZSMpAHOQFYCVoCgAKUFRAFKCqgBTAF0LhqHo1mI4DN3pGJ5D9z5GAGEEIUYgY0QDxQAAIDoMW2GxKeQAAAAASUVORK5CYII="
class="hl-popup-close-icon"
@click="tapClose" />
</div>
<div class="hl-popup-bd" @scroll="onScroll">
<slot></slot>
</div>
<slot name="foot"></slot>
</div>
</div>
</template>
<script>
const AnimationDuration = 300;
const raf = setTimeout;
export default {
props: {
show: {
type: Boolean,
default: false
},
closeable: {
type: Boolean,
default: true
},
overlayCloseable: {
type: Boolean,
default: true
}, // 点击蒙层关闭
round: {
type: Boolean,
default: true
},
zIndex: {
type: Number,
default: 100,
},
position: {
type: String,
default: 'center', // top, bottom, center
},
lockScroll: {
type: Boolean,
default: true,
},
style: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
},
data() {
return {
state: ''
}
},
computed: {
hlPopupClasses() {
const t = 'hl-popup--'
const res = [`${t}${this.position}`]
if (this.round) res.push(`${t}round`)
if (this.position === 'top') res.push(`${t}safe__top`)
if (this.position === 'bottom') res.push(`${t}safe__bottom`)
return res.join(' ')
}
},
watch: {
show: {
immediate: true,
handler(val) {
if (val) {
raf(() => this.state = 'ani-show ', 16)
document.body.classList.add('hl-overflow-hidden');
} else {
this.state = ''
document.body.classList.remove('hl-overflow-hidden');
}
}
},
},
methods: {
tapClose() {
this.state = ''
raf(() => this.$emit('close'), AnimationDuration)
},
tapOverlay() {
this.overlayCloseable && this.tapClose()
},
onScroll: throttle(function(e) {
this.$emit('scroll', e)
}),
noop() {}
}
}
function throttle(func, wait = 50, immediate) {
let timeout
return function() {
const context = this;
const args = arguments;
if (immediate) {
func.apply(context, args);
immediate = false
}
if (timeout) return
timeout = setTimeout(function() {
timeout = null;
func.apply(context, args)
}, wait);
}
}
</script>
<style lang='scss' scoped>
@import "@/scss/_vars.scss";
$round-border-radius: 8*$px;
.hl-overflow-hidden {
overflow: hidden!important;
}
.fx-col {
display: flex;
flex-direction: column;
}
.ani-show {
.hl-overlay {
opacity: 1;
}
.hl-popup--bottom,
.hl-popup--top {
transform: translate3d(0, 0, 0);
}
.hl-popup--center {
transform: translate3d(-50%, -50%, 0) scale(1);
}
}
.hl-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.4);
z-index: 9;
opacity: 0;
transition: opacity 0.3s ease;
}
.hl-popup {
position: fixed;
box-sizing: border-box;
max-height: 100%;
overflow-y: auto;
transition: transform 0.3s ease;
-webkit-overflow-scrolling: touch;
background-color: #fff;
z-index: 1001;
.i-close-wrap {
position: absolute;
right: 0;
top: 0;
padding: 12*$px;
z-index: 1002;
.i-close {
width: 20*$px;
height: 20*$px;
}
}
&--center {
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0) scale(0);
&.hl-popup--round {
border-radius: 8*$px;
}
}
&--top {
top: 0;
left: 0;
width: 100%;
transform: translate3d(0, -100%, 0);
&.hl-popup--round {
border-radius: 0 0 $round-border-radius $round-border-radius;
}
}
&--bottom {
bottom: 0;
left: 0;
width: 100%;
transform: translate3d(0, 100%, 0);
&.hl-popup--round {
border-radius: $round-border-radius $round-border-radius 0 0;
}
}
&--safe__bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
&--safe__top {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
}
}
.hl-popup-hd {
min-height: 48*$px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 16*$px;
font-weight: 400;
color: #262626;
line-height: 20*$px;
}
.hl-popup-close-icon {
position: absolute;
width: 16*$px;
height: 16*$px;
right: 16*$px;
top: 16*$px;
}
.hl-popup-bd {
flex: 1;
height: 100%;
box-sizing: border-box;
overflow-y: auto;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment