mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-07 17:48:34 +08:00
feat: ✨ 使用Transition重构Popup为center类型的Popup添加zoom-in动画 (#699)
* feat: ✨ 使用Transition重构Popup为center类型的Popup添加zoom-in动画 ✅ Closes: #687 * fix: 🐛 修复 Transition 动画类名重复的问题
This commit is contained in:
parent
5e55da4839
commit
0dd34d0649
@ -8,7 +8,21 @@
|
|||||||
`v-model` 为绑定值,表示是否展示弹出层。
|
`v-model` 为绑定值,表示是否展示弹出层。
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<wd-popup v-model="show" custom-style="padding: 30px 40px;" @close="handleClose">内容</wd-popup>
|
<wd-popup v-model="show" custom-style="border-radius:32rpx;" @close="handleClose">
|
||||||
|
<text class="custom-txt">弹弹弹</text>
|
||||||
|
</wd-popup>
|
||||||
|
```
|
||||||
|
```css
|
||||||
|
.custom-txt {
|
||||||
|
color: black;
|
||||||
|
width: 400rpx;
|
||||||
|
height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 40rpx;
|
||||||
|
border-radius: 32rpx;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 弹出位置
|
## 弹出位置
|
||||||
@ -89,6 +103,7 @@ h5 滚动穿透不需要处理,组件已默认开启 `lock-scroll`。
|
|||||||
| hide-when-close | 是否当关闭时将弹出层隐藏(display: none) | boolean | - | true | - |
|
| hide-when-close | 是否当关闭时将弹出层隐藏(display: none) | boolean | - | true | - |
|
||||||
| lazy-render | 弹层内容懒渲染,触发展示时才渲染内容 | boolean | - | true | - |
|
| lazy-render | 弹层内容懒渲染,触发展示时才渲染内容 | boolean | - | true | - |
|
||||||
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | false | - |
|
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离(iphone X 类型的机型) | boolean | - | false | - |
|
||||||
|
| transition | 动画类型,参见 wd-transition 组件的name | string | fade / fade-up / fade-down / fade-left / fade-right / slide-up / slide-down / slide-left / slide-right / zoom-in | - | - |
|
||||||
| lockScroll | 是否锁定背景滚动 | boolean | - | true | 0.1.30 |
|
| lockScroll | 是否锁定背景滚动 | boolean | - | true | 0.1.30 |
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|||||||
13
src/App.vue
13
src/App.vue
@ -1,3 +1,12 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: weisheng
|
||||||
|
* @Date: 2024-10-12 13:07:08
|
||||||
|
* @LastEditTime: 2024-11-08 13:14:48
|
||||||
|
* @LastEditors: weisheng
|
||||||
|
* @Description:
|
||||||
|
* @FilePath: \wot-design-uni\src\App.vue
|
||||||
|
* 记得注释
|
||||||
|
-->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onLaunch, onShow, onHide, onThemeChange } from '@dcloudio/uni-app'
|
import { onLaunch, onShow, onHide, onThemeChange } from '@dcloudio/uni-app'
|
||||||
import { useDark } from './store'
|
import { useDark } from './store'
|
||||||
@ -7,7 +16,7 @@ onThemeChange((option) => {
|
|||||||
darkMode.setDark(option.theme === 'dark')
|
darkMode.setDark(option.theme === 'dark')
|
||||||
})
|
})
|
||||||
|
|
||||||
onLaunch((ctx) => {
|
onLaunch(() => {
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
darkMode.setDark(systemInfo.theme === 'dark')
|
darkMode.setDark(systemInfo.theme === 'dark')
|
||||||
|
|
||||||
@ -18,8 +27,6 @@ onLaunch((ctx) => {
|
|||||||
// 处理收到的消息
|
// 处理收到的消息
|
||||||
if (typeof event.data === 'boolean') {
|
if (typeof event.data === 'boolean') {
|
||||||
darkMode.setDark(event.data)
|
darkMode.setDark(event.data)
|
||||||
} else {
|
|
||||||
darkMode.setDark(false)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// #endif
|
// #endif
|
||||||
|
|||||||
@ -45,7 +45,9 @@
|
|||||||
</wd-cell-group>
|
</wd-cell-group>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
<wd-popup v-model="show1" custom-style="padding: 30px 40px;" @close="handleClose1"><text class="custom-txt">内容</text></wd-popup>
|
<wd-popup v-model="show1" @close="handleClose1" custom-style="border-radius:32rpx;">
|
||||||
|
<text class="custom-txt">弹弹弹</text>
|
||||||
|
</wd-popup>
|
||||||
<wd-popup v-model="show2" position="top" custom-style="height: 200px;" @close="handleClose2"></wd-popup>
|
<wd-popup v-model="show2" position="top" custom-style="height: 200px;" @close="handleClose2"></wd-popup>
|
||||||
<wd-popup v-model="show3" position="right" custom-style="width: 200px;" @close="handleClose3"></wd-popup>
|
<wd-popup v-model="show3" position="right" custom-style="width: 200px;" @close="handleClose3"></wd-popup>
|
||||||
<wd-popup v-model="show4" position="bottom" custom-style="height: 200px;" @close="handleClose4"></wd-popup>
|
<wd-popup v-model="show4" position="bottom" custom-style="height: 200px;" @close="handleClose4"></wd-popup>
|
||||||
@ -168,5 +170,12 @@ function handleClose10() {
|
|||||||
|
|
||||||
.custom-txt {
|
.custom-txt {
|
||||||
color: black;
|
color: black;
|
||||||
|
width: 400rpx;
|
||||||
|
height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 40rpx;
|
||||||
|
border-radius: 32rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
</demo-block>
|
</demo-block>
|
||||||
<demo-block title="Zoom 动画">
|
<demo-block title="Zoom 动画">
|
||||||
<wd-button @click="zoomIn">zoom-in</wd-button>
|
<wd-button @click="zoomIn">zoom-in</wd-button>
|
||||||
|
<wd-button @click="zoomOut">zoom-out</wd-button>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
<demo-block title="自定义动画">
|
<demo-block title="自定义动画">
|
||||||
<wd-button @click="custom">custom</wd-button>
|
<wd-button @click="custom">custom</wd-button>
|
||||||
@ -25,7 +26,6 @@
|
|||||||
|
|
||||||
<wd-transition
|
<wd-transition
|
||||||
:show="customShow"
|
:show="customShow"
|
||||||
name=""
|
|
||||||
:duration="{ enter: 700, leave: 1000 }"
|
:duration="{ enter: 700, leave: 1000 }"
|
||||||
enter-class="custom-enter"
|
enter-class="custom-enter"
|
||||||
enter-active-class="custom-enter-active"
|
enter-active-class="custom-enter-active"
|
||||||
@ -39,10 +39,11 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { TransitionName } from '@/uni_modules/wot-design-uni/components/wd-transition/types'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const show = ref<boolean>(false)
|
const show = ref<boolean>(false)
|
||||||
const name = ref<any>('')
|
const name = ref<TransitionName>()
|
||||||
const customShow = ref<boolean>(false)
|
const customShow = ref<boolean>(false)
|
||||||
function fade() {
|
function fade() {
|
||||||
transition('fade')
|
transition('fade')
|
||||||
@ -74,13 +75,16 @@ function slideRight() {
|
|||||||
function zoomIn() {
|
function zoomIn() {
|
||||||
transition('zoom-in')
|
transition('zoom-in')
|
||||||
}
|
}
|
||||||
|
function zoomOut() {
|
||||||
|
transition('zoom-out')
|
||||||
|
}
|
||||||
function custom() {
|
function custom() {
|
||||||
customShow.value = true
|
customShow.value = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
customShow.value = false
|
customShow.value = false
|
||||||
}, 1200)
|
}, 1200)
|
||||||
}
|
}
|
||||||
function transition(transition: string) {
|
function transition(transition: TransitionName) {
|
||||||
name.value = transition
|
name.value = transition
|
||||||
show.value = true
|
show.value = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@ -103,6 +103,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 定义状态(m) */
|
||||||
|
@mixin mdeep($modifier...) {
|
||||||
|
$selectors: "";
|
||||||
|
|
||||||
|
@each $item in $modifier {
|
||||||
|
$selectors: #{$selectors + & + $modifierSeparator + $item + ","};
|
||||||
|
}
|
||||||
|
|
||||||
|
@at-root {
|
||||||
|
:deep() {
|
||||||
|
#{$selectors} {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 对于需要需要嵌套在 m 底下的 e,调用这个混合宏,一般在切换整个组件的状态,如切换颜色的时候 */
|
/* 对于需要需要嵌套在 m 底下的 e,调用这个混合宏,一般在切换整个组件的状态,如切换颜色的时候 */
|
||||||
@mixin me($element...) {
|
@mixin me($element...) {
|
||||||
$selector: &;
|
$selector: &;
|
||||||
|
|||||||
@ -1,23 +1,33 @@
|
|||||||
@import './../common/abstracts/_mixin.scss';
|
@import './../common/abstracts/_mixin.scss';
|
||||||
@import './../common/abstracts/variable.scss';
|
@import './../common/abstracts/variable.scss';
|
||||||
@import '../wd-overlay/index.scss';
|
|
||||||
|
|
||||||
.wot-theme-dark {
|
.wot-theme-dark {
|
||||||
@include b(popup) {
|
@include b(popup-wrapper) {
|
||||||
background: $-dark-background2;
|
:deep() {
|
||||||
|
.wd-popup {
|
||||||
|
background: $-dark-background2;
|
||||||
|
}
|
||||||
|
|
||||||
@include e(close) {
|
.wd-popup__close {
|
||||||
color: $-dark-color;
|
color: $-dark-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include b(popup) {
|
@include b(popup-wrapper) {
|
||||||
position: fixed;
|
:deep() {
|
||||||
max-height: 100%;
|
.wd-popup {
|
||||||
overflow-y: auto;
|
position: fixed;
|
||||||
background: #fff;
|
max-height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@include b(popup) {
|
||||||
@include edeep(close) {
|
@include edeep(close) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
@ -27,86 +37,48 @@
|
|||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@include m(center) {
|
@include mdeep(center) {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate3d(-50%, -50%, 0);
|
transform: translate3d(-50%, -50%, 0);
|
||||||
|
transform-origin: 0% 0%;
|
||||||
|
|
||||||
|
&.wd-zoom-in-enter,
|
||||||
|
&.wd-zoom-in-leave-to {
|
||||||
|
transform: scale(0.8) translate3d(-50%, -50%, 0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include when(deep) {
|
||||||
|
|
||||||
|
&.wd-zoom-in-enter,
|
||||||
|
&.wd-zoom-in-leave-to {
|
||||||
|
transform: scale(0.1) translate3d(-50%, -50%, 0) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include m(left) {
|
@include mdeep(left) {
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include m(right) {
|
@include mdeep(right) {
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include m(top) {
|
@include mdeep(top) {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include m(bottom) {
|
@include mdeep(bottom) {
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.wd-center-enter-active,
|
|
||||||
.wd-center-leave-active {
|
|
||||||
transition-property: opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-center-enter,
|
|
||||||
.wd-center-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-top-enter-active,
|
|
||||||
.wd-top-leave-active,
|
|
||||||
.wd-bottom-enter-active,
|
|
||||||
.wd-bottom-leave-active,
|
|
||||||
.wd-left-enter-active,
|
|
||||||
.wd-left-leave-active,
|
|
||||||
.wd-right-enter-active,
|
|
||||||
.wd-right-enter-active {
|
|
||||||
transition-property: transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-top-enter,
|
|
||||||
.wd-top-leave-to {
|
|
||||||
transform: translate3d(0, -100%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-bottom-enter,
|
|
||||||
.wd-bottom-leave-to {
|
|
||||||
transform: translate3d(0, 100%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-left-enter,
|
|
||||||
.wd-left-leave-to {
|
|
||||||
transform: translate3d(-100%, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-right-enter,
|
|
||||||
.wd-right-leave-to {
|
|
||||||
transform: translate3d(100%, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-zoom-in-enter-active,
|
|
||||||
.wd-zoom-in-leave-active {
|
|
||||||
transition-property: opacity, transform;
|
|
||||||
transform-origin: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-zoom-in-enter,
|
|
||||||
.wd-zoom-in-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translate3d(-50%, -50%, 0) scale(0.7);
|
|
||||||
}
|
}
|
||||||
@ -1,33 +1,49 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: weisheng
|
* @Author: weisheng
|
||||||
* @Date: 2024-03-18 11:22:03
|
* @Date: 2024-03-18 11:22:03
|
||||||
* @LastEditTime: 2024-03-18 15:29:43
|
* @LastEditTime: 2024-11-08 12:55:58
|
||||||
* @LastEditors: weisheng
|
* @LastEditors: weisheng
|
||||||
* @Description:
|
* @Description:
|
||||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-popup\types.ts
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-popup\types.ts
|
||||||
* 记得注释
|
* 记得注释
|
||||||
*/
|
*/
|
||||||
|
import type { PropType } from 'vue'
|
||||||
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
||||||
|
import type { TransitionName } from '../wd-transition/types'
|
||||||
|
|
||||||
export type PopupType = 'center' | 'top' | 'right' | 'bottom' | 'left'
|
export type PopupType = 'center' | 'top' | 'right' | 'bottom' | 'left'
|
||||||
|
|
||||||
export const popupProps = {
|
export const popupProps = {
|
||||||
...baseProps,
|
...baseProps,
|
||||||
transition: String,
|
/**
|
||||||
|
* 动画类型,参见 wd-transition 组件的name
|
||||||
|
* 类型:string
|
||||||
|
* 可选值:fade / fade-up / fade-down / fade-left / fade-right / slide-up / slide-down / slide-left / slide-right / zoom-in
|
||||||
|
*/
|
||||||
|
transition: String as PropType<TransitionName>,
|
||||||
/**
|
/**
|
||||||
* 关闭按钮
|
* 关闭按钮
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:false
|
||||||
*/
|
*/
|
||||||
closable: makeBooleanProp(false),
|
closable: makeBooleanProp(false),
|
||||||
/**
|
/**
|
||||||
* 弹出框的位置
|
* 弹出框的位置
|
||||||
|
* 类型:string
|
||||||
|
* 默认值:center
|
||||||
|
* 可选值:center / top / right / bottom / left
|
||||||
*/
|
*/
|
||||||
position: makeStringProp<PopupType>('center'),
|
position: makeStringProp<PopupType>('center'),
|
||||||
/**
|
/**
|
||||||
* 点击遮罩是否关闭
|
* 点击遮罩是否关闭
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:true
|
||||||
*/
|
*/
|
||||||
closeOnClickModal: makeBooleanProp(true),
|
closeOnClickModal: makeBooleanProp(true),
|
||||||
/**
|
/**
|
||||||
* 动画持续时间
|
* 动画持续时间
|
||||||
|
* 类型:number | boolean
|
||||||
|
* 默认值:300
|
||||||
*/
|
*/
|
||||||
duration: {
|
duration: {
|
||||||
type: [Number, Boolean],
|
type: [Number, Boolean],
|
||||||
@ -35,22 +51,32 @@ export const popupProps = {
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 是否显示遮罩
|
* 是否显示遮罩
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:true
|
||||||
*/
|
*/
|
||||||
modal: makeBooleanProp(true),
|
modal: makeBooleanProp(true),
|
||||||
/**
|
/**
|
||||||
* 设置层级
|
* 设置层级
|
||||||
|
* 类型:number
|
||||||
|
* 默认值:10
|
||||||
*/
|
*/
|
||||||
zIndex: makeNumberProp(10),
|
zIndex: makeNumberProp(10),
|
||||||
/**
|
/**
|
||||||
* 是否当关闭时将弹出层隐藏(display: none)
|
* 是否当关闭时将弹出层隐藏(display: none)
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:true
|
||||||
*/
|
*/
|
||||||
hideWhenClose: makeBooleanProp(true),
|
hideWhenClose: makeBooleanProp(true),
|
||||||
/**
|
/**
|
||||||
* 遮罩样式
|
* 遮罩样式
|
||||||
|
* 类型:string
|
||||||
|
* 默认值:''
|
||||||
*/
|
*/
|
||||||
modalStyle: makeStringProp(''),
|
modalStyle: makeStringProp(''),
|
||||||
/**
|
/**
|
||||||
* 弹出面板是否设置底部安全距离(iphone X 类型的机型)
|
* 弹出面板是否设置底部安全距离(iphone X 类型的机型)
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:false
|
||||||
*/
|
*/
|
||||||
safeAreaInsetBottom: makeBooleanProp(false),
|
safeAreaInsetBottom: makeBooleanProp(false),
|
||||||
/**
|
/**
|
||||||
@ -59,10 +85,14 @@ export const popupProps = {
|
|||||||
modelValue: makeBooleanProp(false),
|
modelValue: makeBooleanProp(false),
|
||||||
/**
|
/**
|
||||||
* 弹层内容懒渲染,触发展示时才渲染内容
|
* 弹层内容懒渲染,触发展示时才渲染内容
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:true
|
||||||
*/
|
*/
|
||||||
lazyRender: makeBooleanProp(true),
|
lazyRender: makeBooleanProp(true),
|
||||||
/**
|
/**
|
||||||
* 是否锁定滚动
|
* 是否锁定滚动
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:true
|
||||||
*/
|
*/
|
||||||
lockScroll: makeBooleanProp(true)
|
lockScroll: makeBooleanProp(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<wd-overlay
|
<view class="wd-popup-wrapper">
|
||||||
v-if="modal"
|
<wd-overlay
|
||||||
:show="modelValue"
|
v-if="modal"
|
||||||
:z-index="zIndex"
|
:show="modelValue"
|
||||||
:lock-scroll="lockScroll"
|
:z-index="zIndex"
|
||||||
:duration="duration"
|
:lock-scroll="lockScroll"
|
||||||
:custom-style="modalStyle"
|
:duration="duration"
|
||||||
@click="handleClickModal"
|
:custom-style="modalStyle"
|
||||||
@touchmove="noop"
|
@click="handleClickModal"
|
||||||
/>
|
@touchmove="noop"
|
||||||
<view v-if="!lazyRender || inited" :class="rootClass" :style="style" @transitionend="onTransitionEnd">
|
/>
|
||||||
<slot />
|
<wd-transition
|
||||||
<wd-icon v-if="closable" custom-class="wd-popup__close" name="add" @click="close" />
|
:lazy-render="lazyRender"
|
||||||
|
:custom-class="rootClass"
|
||||||
|
:custom-style="style"
|
||||||
|
:duration="duration"
|
||||||
|
:show="modelValue"
|
||||||
|
:name="transitionName"
|
||||||
|
:destroy="hideWhenClose"
|
||||||
|
@before-enter="emit('before-enter')"
|
||||||
|
@enter="emit('enter')"
|
||||||
|
@after-enter="emit('after-enter')"
|
||||||
|
@before-leave="emit('before-leave')"
|
||||||
|
@leave="emit('leave')"
|
||||||
|
@after-leave="emit('after-leave')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<wd-icon v-if="closable" custom-class="wd-popup__close" name="add" @click="close" />
|
||||||
|
</wd-transition>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -29,9 +45,9 @@ export default {
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||||
import wdOverlay from '../wd-overlay/wd-overlay.vue'
|
import wdOverlay from '../wd-overlay/wd-overlay.vue'
|
||||||
import { computed, onBeforeMount, ref, watch } from 'vue'
|
import { computed, onBeforeMount, ref } from 'vue'
|
||||||
import { isObj, requestAnimationFrame } from '../common/util'
|
|
||||||
import { popupProps } from './types'
|
import { popupProps } from './types'
|
||||||
|
import type { TransitionName } from '../wd-transition/types'
|
||||||
|
|
||||||
const props = defineProps(popupProps)
|
const props = defineProps(popupProps)
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@ -46,53 +62,42 @@ const emit = defineEmits([
|
|||||||
'close'
|
'close'
|
||||||
])
|
])
|
||||||
|
|
||||||
const getClassNames = (name?: string) => {
|
/**
|
||||||
if (!name) {
|
* 弹出位置
|
||||||
return {
|
*/
|
||||||
enter: 'enter-class enter-active-class',
|
const transitionName = computed<TransitionName | TransitionName[]>(() => {
|
||||||
'enter-to': 'enter-to-class enter-active-class',
|
if (props.transition) {
|
||||||
leave: 'leave-class leave-active-class',
|
return props.transition
|
||||||
'leave-to': 'leave-to-class leave-active-class'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (props.position === 'center') {
|
||||||
return {
|
return ['zoom-in', 'fade']
|
||||||
enter: `wd-${name}-enter wd-${name}-enter-active`,
|
|
||||||
'enter-to': `wd-${name}-enter-to wd-${name}-enter-active`,
|
|
||||||
leave: `wd-${name}-leave wd-${name}-leave-active`,
|
|
||||||
'leave-to': `wd-${name}-leave-to wd-${name}-leave-active`
|
|
||||||
}
|
}
|
||||||
}
|
if (props.position === 'left') {
|
||||||
|
return 'slide-left'
|
||||||
// 初始化是否完成
|
}
|
||||||
const inited = ref<boolean>(false)
|
if (props.position === 'right') {
|
||||||
// 是否显示
|
return 'slide-right'
|
||||||
const display = ref<boolean>(false)
|
}
|
||||||
// 当前动画状态
|
if (props.position === 'bottom') {
|
||||||
const status = ref<string>('')
|
return 'slide-up'
|
||||||
// 动画是否结束
|
}
|
||||||
const transitionEnded = ref<boolean>(false)
|
if (props.position === 'top') {
|
||||||
// 动画持续时间
|
return 'slide-down'
|
||||||
const currentDuration = ref<number>(300)
|
}
|
||||||
// 类名
|
return 'slide-up'
|
||||||
const classes = ref<string>('')
|
})
|
||||||
|
|
||||||
const safeBottom = ref<number>(0)
|
const safeBottom = ref<number>(0)
|
||||||
|
|
||||||
const name = ref<string>('') // 动画名
|
|
||||||
|
|
||||||
const style = computed(() => {
|
const style = computed(() => {
|
||||||
return `z-index: ${props.zIndex}; padding-bottom: ${safeBottom.value}px; -webkit-transition-duration: ${
|
return `z-index:${props.zIndex}; padding-bottom: ${safeBottom.value}px;${props.customStyle}`
|
||||||
currentDuration.value
|
|
||||||
}ms; transition-duration: ${currentDuration.value}ms; ${display.value || !props.hideWhenClose ? '' : 'display: none;'} ${props.customStyle}`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const rootClass = computed(() => {
|
const rootClass = computed(() => {
|
||||||
return `wd-popup wd-popup--${props.position} ${props.customClass || ''} ${classes.value || ''}`
|
return `wd-popup wd-popup--${props.position} ${!props.transition && props.position === 'center' ? 'is-deep' : ''} ${props.customClass || ''}`
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
observerTransition()
|
|
||||||
if (props.safeAreaInsetBottom) {
|
if (props.safeAreaInsetBottom) {
|
||||||
const { safeArea, screenHeight, safeAreaInsets } = uni.getSystemInfoSync()
|
const { safeArea, screenHeight, safeAreaInsets } = uni.getSystemInfoSync()
|
||||||
|
|
||||||
@ -107,95 +112,8 @@ onBeforeMount(() => {
|
|||||||
safeBottom.value = 0
|
safeBottom.value = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (props.modelValue) {
|
|
||||||
enter()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(newVal) => {
|
|
||||||
observermodelValue(newVal)
|
|
||||||
},
|
|
||||||
{ deep: true, immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
[() => props.position, () => props.transition],
|
|
||||||
() => {
|
|
||||||
observerTransition()
|
|
||||||
},
|
|
||||||
{ deep: true, immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
function observermodelValue(value: boolean) {
|
|
||||||
value ? enter() : leave()
|
|
||||||
}
|
|
||||||
|
|
||||||
function enter() {
|
|
||||||
const classNames = getClassNames(props.transition || props.position)
|
|
||||||
const duration = props.transition === 'none' ? 0 : isObj(props.duration) ? (props.duration as any).enter : props.duration
|
|
||||||
status.value = 'enter'
|
|
||||||
emit('before-enter')
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
emit('enter')
|
|
||||||
classes.value = classNames.enter
|
|
||||||
currentDuration.value = duration
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
inited.value = true
|
|
||||||
display.value = true
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
transitionEnded.value = false
|
|
||||||
classes.value = classNames['enter-to']
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function leave() {
|
|
||||||
if (!display.value) return
|
|
||||||
const classNames = getClassNames(props.transition || props.position)
|
|
||||||
const duration = props.transition === 'none' ? 0 : isObj(props.duration) ? (props.duration as any).leave : props.duration
|
|
||||||
status.value = 'leave'
|
|
||||||
emit('before-leave')
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
emit('leave')
|
|
||||||
classes.value = classNames.leave
|
|
||||||
currentDuration.value = duration
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
transitionEnded.value = false
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
onTransitionEnd()
|
|
||||||
clearTimeout(timer)
|
|
||||||
}, currentDuration.value)
|
|
||||||
classes.value = classNames['leave-to']
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTransitionEnd() {
|
|
||||||
if (transitionEnded.value) return
|
|
||||||
|
|
||||||
transitionEnded.value = true
|
|
||||||
if (status.value === 'leave') {
|
|
||||||
// 离开后触发
|
|
||||||
emit('after-leave')
|
|
||||||
} else if (status.value === 'enter') {
|
|
||||||
// 进入后触发
|
|
||||||
emit('after-enter')
|
|
||||||
}
|
|
||||||
if (!props.modelValue && display.value) {
|
|
||||||
display.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function observerTransition() {
|
|
||||||
const { transition, position } = props
|
|
||||||
name.value = transition || position
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClickModal() {
|
function handleClickModal() {
|
||||||
emit('click-modal')
|
emit('click-modal')
|
||||||
if (props.closeOnClickModal) {
|
if (props.closeOnClickModal) {
|
||||||
|
|||||||
@ -2,25 +2,14 @@
|
|||||||
transition-timing-function: ease;
|
transition-timing-function: ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-fade-enter-active,
|
|
||||||
.wd-fade-leave-active {
|
|
||||||
transition-property: opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-fade-enter,
|
.wd-fade-enter,
|
||||||
.wd-fade-leave-to {
|
.wd-fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-fade-up-enter-active,
|
.wd-fade-enter-active,
|
||||||
.wd-fade-up-leave-active,
|
.wd-fade-leave-active {
|
||||||
.wd-fade-down-enter-active,
|
transition-property: opacity;
|
||||||
.wd-fade-down-leave-active,
|
|
||||||
.wd-fade-left-enter-active,
|
|
||||||
.wd-fade-left-leave-active,
|
|
||||||
.wd-fade-right-enter-active,
|
|
||||||
.wd-fade-right-enter-active {
|
|
||||||
transition-property: opacity, transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-fade-up-enter,
|
.wd-fade-up-enter,
|
||||||
@ -47,17 +36,6 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-slide-up-enter-active,
|
|
||||||
.wd-slide-up-leave-active,
|
|
||||||
.wd-slide-down-enter-active,
|
|
||||||
.wd-slide-down-leave-active,
|
|
||||||
.wd-slide-left-enter-active,
|
|
||||||
.wd-slide-left-leave-active,
|
|
||||||
.wd-slide-right-enter-active,
|
|
||||||
.wd-slide-right-enter-active {
|
|
||||||
transition-property: transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-slide-up-enter,
|
.wd-slide-up-enter,
|
||||||
.wd-slide-up-leave-to {
|
.wd-slide-up-leave-to {
|
||||||
transform: translate3d(0, 100%, 0);
|
transform: translate3d(0, 100%, 0);
|
||||||
@ -78,14 +56,40 @@
|
|||||||
transform: translate3d(100%, 0, 0);
|
transform: translate3d(100%, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-zoom-in-enter-active,
|
|
||||||
.wd-zoom-in-leave-active {
|
|
||||||
transition-property: opacity, transform;
|
|
||||||
transform-origin: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-zoom-in-enter,
|
.wd-zoom-in-enter,
|
||||||
.wd-zoom-in-leave-to {
|
.wd-zoom-in-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.7);
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wd-zoom-out-enter,
|
||||||
|
.wd-zoom-out-leave-to {
|
||||||
|
transform: scale(1.2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wd-zoom-in-enter-active,
|
||||||
|
.wd-zoom-in-leave-active,
|
||||||
|
.wd-zoom-out-enter-active,
|
||||||
|
.wd-zoom-out-leave-active,
|
||||||
|
.wd-fade-up-enter-active,
|
||||||
|
.wd-fade-up-leave-active,
|
||||||
|
.wd-fade-down-enter-active,
|
||||||
|
.wd-fade-down-leave-active,
|
||||||
|
.wd-fade-left-enter-active,
|
||||||
|
.wd-fade-left-leave-active,
|
||||||
|
.wd-fade-right-enter-active,
|
||||||
|
.wd-fade-right-leave-active {
|
||||||
|
transition-property: opacity, transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wd-slide-up-enter-active,
|
||||||
|
.wd-slide-up-leave-active,
|
||||||
|
.wd-slide-down-enter-active,
|
||||||
|
.wd-slide-down-leave-active,
|
||||||
|
.wd-slide-left-enter-active,
|
||||||
|
.wd-slide-left-leave-active,
|
||||||
|
.wd-slide-right-enter-active,
|
||||||
|
.wd-slide-right-leave-active {
|
||||||
|
transition-property: transform;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* @Author: weisheng
|
||||||
|
* @Date: 2024-09-01 15:42:04
|
||||||
|
* @LastEditTime: 2024-11-06 23:50:08
|
||||||
|
* @LastEditors: weisheng
|
||||||
|
* @Description:
|
||||||
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-transition\types.ts
|
||||||
|
* 记得注释
|
||||||
|
*/
|
||||||
import type { ExtractPropTypes, PropType } from 'vue'
|
import type { ExtractPropTypes, PropType } from 'vue'
|
||||||
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
import { baseProps, makeBooleanProp, makeStringProp } from '../common/props'
|
||||||
|
|
||||||
export type TransitionName =
|
export type TransitionName =
|
||||||
| 'fade'
|
| 'fade'
|
||||||
@ -33,22 +42,25 @@ export const transitionProps = {
|
|||||||
type: [Object, Number, Boolean] as PropType<Record<string, number> | number | boolean>,
|
type: [Object, Number, Boolean] as PropType<Record<string, number> | number | boolean>,
|
||||||
default: 300
|
default: 300
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 弹层内容懒渲染,触发展示时才渲染内容
|
||||||
|
* 类型:boolean
|
||||||
|
* 默认值:false
|
||||||
|
*/
|
||||||
|
lazyRender: makeBooleanProp(false),
|
||||||
/**
|
/**
|
||||||
* 动画类型
|
* 动画类型
|
||||||
* 类型:string
|
* 类型:string
|
||||||
* 可选值:fade / fade-up / fade-down / fade-left / fade-right / slide-up / slide-down / slide-left / slide-right / zoom-in
|
* 可选值:fade / fade-up / fade-down / fade-left / fade-right / slide-up / slide-down / slide-left / slide-right / zoom-in
|
||||||
* 默认值:'fade'
|
* 默认值:'fade'
|
||||||
*/
|
*/
|
||||||
name: makeStringProp<TransitionName | ''>('fade'),
|
name: [String, Array] as PropType<TransitionName | TransitionName[]>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否延迟渲染子组件
|
* 是否在动画结束时销毁子节点(display: none)
|
||||||
* 类型:boolean
|
* 类型:boolean
|
||||||
* 默认值:true
|
* 默认值:false
|
||||||
*/
|
*/
|
||||||
lazyRender: makeBooleanProp(true),
|
destroy: makeBooleanProp(true),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进入过渡的开始状态
|
* 进入过渡的开始状态
|
||||||
* 类型:string
|
* 类型:string
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="inited" :class="rootClass" :style="style" @transitionend="onTransitionEnd" @click="handleClick">
|
<view v-if="!lazyRender || inited" :class="rootClass" :style="style" @transitionend="onTransitionEnd" @click="handleClick">
|
||||||
<slot />
|
<slot />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -18,24 +18,33 @@ export default {
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onBeforeMount, ref, watch } from 'vue'
|
import { computed, onBeforeMount, ref, watch } from 'vue'
|
||||||
import { isObj, isPromise, requestAnimationFrame } from '../common/util'
|
import { isObj, isPromise, requestAnimationFrame } from '../common/util'
|
||||||
import { transitionProps } from './types'
|
import { transitionProps, type TransitionName } from './types'
|
||||||
import { AbortablePromise } from '../common/AbortablePromise'
|
import { AbortablePromise } from '../common/AbortablePromise'
|
||||||
|
|
||||||
const getClassNames = (name?: string) => {
|
const getClassNames = (name?: TransitionName | TransitionName[]) => {
|
||||||
if (!name) {
|
let enter: string = `${props.enterClass} ${props.enterActiveClass}`
|
||||||
return {
|
let enterTo: string = `${props.enterToClass} ${props.enterActiveClass}`
|
||||||
enter: `${props.enterClass} ${props.enterActiveClass}`,
|
let leave: string = `${props.leaveClass} ${props.leaveActiveClass}`
|
||||||
'enter-to': `${props.enterToClass} ${props.enterActiveClass}`,
|
let leaveTo: string = `${props.leaveToClass} ${props.leaveActiveClass}`
|
||||||
leave: `${props.leaveClass} ${props.leaveActiveClass}`,
|
|
||||||
'leave-to': `${props.leaveToClass} ${props.leaveActiveClass}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (Array.isArray(name)) {
|
||||||
|
for (let index = 0; index < name.length; index++) {
|
||||||
|
enter = `wd-${name[index]}-enter wd-${name[index]}-enter-active ${enter}`
|
||||||
|
enterTo = `wd-${name[index]}-enter-to wd-${name[index]}-enter-active ${enterTo}`
|
||||||
|
leave = `wd-${name[index]}-leave wd-${name[index]}-leave-active ${leave}`
|
||||||
|
leaveTo = `wd-${name[index]}-leave-to wd-${name[index]}-leave-active ${leaveTo}`
|
||||||
|
}
|
||||||
|
} else if (name) {
|
||||||
|
enter = `wd-${name}-enter wd-${name}-enter-active ${enter}`
|
||||||
|
enterTo = `wd-${name}-enter-to wd-${name}-enter-active ${enterTo}`
|
||||||
|
leave = `wd-${name}-leave wd-${name}-leave-active ${leave}`
|
||||||
|
leaveTo = `wd-${name}-leave-to wd-${name}-leave-active ${leaveTo}`
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
enter: `wd-${name}-enter wd-${name}-enter-active`,
|
enter: enter,
|
||||||
'enter-to': `wd-${name}-enter-to wd-${name}-enter-active`,
|
'enter-to': enterTo,
|
||||||
leave: `wd-${name}-leave wd-${name}-leave-active`,
|
leave: leave,
|
||||||
'leave-to': `wd-${name}-leave-to wd-${name}-leave-active`
|
'leave-to': leaveTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +74,7 @@ const leaveLifeCyclePromises = ref<AbortablePromise<unknown> | null>(null)
|
|||||||
|
|
||||||
const style = computed(() => {
|
const style = computed(() => {
|
||||||
return `-webkit-transition-duration:${currentDuration.value}ms;transition-duration:${currentDuration.value}ms;${
|
return `-webkit-transition-duration:${currentDuration.value}ms;transition-duration:${currentDuration.value}ms;${
|
||||||
display.value ? '' : 'display: none;'
|
display.value || !props.destroy ? '' : 'display: none;'
|
||||||
}${props.customStyle}`
|
}${props.customStyle}`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user