新增插件
parent
b51cb05497
commit
3f294b0b7a
|
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<text :style="[{ color: color, 'font-size': iconSize, 'font-family': 'iconfont' }]" class="icon-font" :class="'cr-' + type" @tap="_onClick">{{ iconfontCode }}</text>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<text :style="{ color: color, 'font-size': iconSize }" class="icon-font" :class="'icon-' + name + ' cr-' + type" @tap="_onClick"></text>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//#ifdef APP-NVUE
|
||||
import dataIconfont from '@/static/icon/iconfont.json';
|
||||
import iconUrl from '@/static/icon/iconfont.ttf';
|
||||
//#endif
|
||||
/**
|
||||
* Icons 图标
|
||||
* @description 用于展示 icons 图标
|
||||
* @property {Number} size 图标大小
|
||||
* @property {String} name 图标图案,参考示例
|
||||
* @property {String} color 图标颜色
|
||||
* @property {String} type 图标常规颜色 info / primary / error/ warning / success
|
||||
*/
|
||||
export default {
|
||||
name: 'u-icon',
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'info',
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 32,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
//#region 获取图标大小
|
||||
iconSize() {
|
||||
return this.getVal(this.size);
|
||||
},
|
||||
//#ifdef APP-NVUE
|
||||
// appnvue页面做的特殊处理
|
||||
iconfontCode() {
|
||||
const code = this.dataIconfont.glyphs.find(v => v.font_class === this.name);
|
||||
if (code != null) {
|
||||
return unescape(`%u${code.unicode}`);
|
||||
}
|
||||
return ''
|
||||
}
|
||||
//#endif
|
||||
//#endregion
|
||||
},
|
||||
//#ifdef APP-NVUE
|
||||
data() {
|
||||
return {
|
||||
dataIconfont: dataIconfont,
|
||||
iconUrl: iconUrl
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const domModule = weex.requireModule("dom");
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': 'iconfont',
|
||||
'src': `url('${this.iconUrl}')`
|
||||
})
|
||||
},
|
||||
//#endif
|
||||
methods: {
|
||||
//#region 获取图标大小
|
||||
getVal(val) {
|
||||
const reg = /^[0-9]*$/g;
|
||||
return typeof val === 'number' || reg.test(val) ? val.toString() + 'rpx' : val;
|
||||
},
|
||||
//#endregion
|
||||
//#region 点击事件处理
|
||||
_onClick() {
|
||||
this.$emit('click');
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* #ifndef APP-NVUE */
|
||||
@import url('@/static/icon/iconfont.css');
|
||||
/* #ifndef MP-WEIXIN */
|
||||
@font-face {
|
||||
font-family: "iconfont";
|
||||
src: url('@/static/icon/iconfont.ttf');
|
||||
}
|
||||
/* #endif */
|
||||
/* #endif */
|
||||
.icon-font {
|
||||
font-family: 'iconfont' !important;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,45 @@
|
|||
// #ifdef H5
|
||||
export default {
|
||||
name: 'Keypress',
|
||||
props: {
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const keyNames = {
|
||||
esc: ['Esc', 'Escape'],
|
||||
tab: 'Tab',
|
||||
enter: 'Enter',
|
||||
space: [' ', 'Spacebar'],
|
||||
up: ['Up', 'ArrowUp'],
|
||||
left: ['Left', 'ArrowLeft'],
|
||||
right: ['Right', 'ArrowRight'],
|
||||
down: ['Down', 'ArrowDown'],
|
||||
delete: ['Backspace', 'Delete', 'Del']
|
||||
}
|
||||
const listener = ($event) => {
|
||||
if (this.disable) {
|
||||
return
|
||||
}
|
||||
const keyName = Object.keys(keyNames).find(key => {
|
||||
const keyName = $event.key
|
||||
const value = keyNames[key]
|
||||
return value === keyName || (Array.isArray(value) && value.includes(keyName))
|
||||
})
|
||||
if (keyName) {
|
||||
// 避免和其他按键事件冲突
|
||||
setTimeout(() => {
|
||||
this.$emit(keyName, {})
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
document.addEventListener('keyup', listener)
|
||||
// this.$once('hook:beforeDestroy', () => {
|
||||
// document.removeEventListener('keyup', listener)
|
||||
// })
|
||||
},
|
||||
render: () => { }
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,698 @@
|
|||
<template>
|
||||
<view v-if="showPopup" class="uni-popup" :class="popupstyle + (isDesktop ? ' fixforpc-z-index' : '')">
|
||||
<view @touchstart="touchstart">
|
||||
<u-transition key="1" v-if="maskShow" name="mask" mode="fade" :customStyle="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
|
||||
<u-transition key="2" :mode="ani" name="content" :customStyle="transClass" :duration="duration" :show="showTrans">
|
||||
<view class="re" :style="'border-radius:' + round + 'px'">
|
||||
<view v-if="closeType == 'icon' && closeable" class="popup-close pa-14 box-border-box" :class="closeIconPos" :style="closeIconStyle" @tap="close">
|
||||
<u-icon :name="closeIcon" :type="closeIconType" :size="closeIconSize + 'rpx'"></u-icon>
|
||||
</view>
|
||||
<view v-if="closeType == 'text' && closeable" class="flex-row jc-sb align-c w abs top-0 pa-14 z-i">
|
||||
<text class="cr-info" @click="close">取消</text>
|
||||
<view class="re">
|
||||
<text v-if="isCustomBtn" class="inline-block" :style="customBtnStyle" @click="custom_change">{{ customBtnText }}</text>
|
||||
<text class="cr-primary" @click="comfirm">确定</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="title != ''" class="re">
|
||||
<view class="popup-close pa-14 box-border-box" :class="titleBorder ? 'br-b-e' : ''">
|
||||
<view v-if="title != ''" class="title">
|
||||
<text class="fw">{{ title }}222</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-popup__wrapper radius-lg" :style="{ backgroundColor: bg, maxHeight: height }" :class="[popupstyle]" @click="clear">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</u-transition>
|
||||
</view>
|
||||
<!-- #ifdef H5 -->
|
||||
<keypress v-if="maskShow" @esc="onTap" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef H5
|
||||
import keypress from './keypress.js';
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* PopUp 弹出层
|
||||
* @description 弹出层组件,为了解决遮罩弹层的问题
|
||||
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
|
||||
* @value top 顶部弹出
|
||||
* @value center 中间弹出
|
||||
* @value bottom 底部弹出
|
||||
* @value left 左侧弹出
|
||||
* @value right 右侧弹出
|
||||
* @value message 消息提示
|
||||
* @value dialog 对话框
|
||||
* @value share 底部分享示例
|
||||
* @property {Boolean} animation = [true|false] 是否开启动画
|
||||
* @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
|
||||
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
|
||||
* @property {String} backgroundColor 主窗口背景色
|
||||
* @property {String} maskBackgroundColor 蒙版颜色
|
||||
* @property {Boolean} safeArea 是否适配底部安全区
|
||||
* @property {Boolean} closeable Boolean | 是否显示关闭图标,默认true
|
||||
* @property {String} closeIcon String | 关闭iconfont,默认‘close’ ---还有close-o,或者其他自定义icon
|
||||
* @property {String} closeIconType String | 关闭颜色,默认‘6’ 参照组件u-icon
|
||||
* @property {String} closeIconPos = [top-left|top-right|top-center|bottom-left|bottom-right|bottom-center] String | 关闭图标位置
|
||||
* @value top-left 左上角
|
||||
* @value top-right 右上角
|
||||
* @value bottom-left 左下角
|
||||
* @value bottom-right 右下角
|
||||
* @property {Number} closeIconSize Number | 关闭图标大小,默认32(单位rpx)
|
||||
* @property {Boolean} closeType Boolean | 显示关闭图标还是文本(文本包含确定和取消),默认icon,------icon,text,
|
||||
* @property {Boolean} isCustomBtn Boolean | 自定义按钮
|
||||
* @property {String} customBtnText String | 自定义按钮文本
|
||||
* @property {Object} customBtnStyle Object | 自定义按钮样式
|
||||
* @property {String} title String | 弹窗标题
|
||||
* @property {String} titleBorder String | 弹窗底部边框
|
||||
* @property {String} height String | 弹窗内容高度
|
||||
* @property {String} round String | 弹窗圆角
|
||||
* @property {Boolean} isConfirmClose Boolean | 是否点击确认按钮时主动关闭弹窗
|
||||
* @event {Function} change 打开关闭弹窗触发,e={show: false}
|
||||
* @event {Function} maskClick 点击遮罩触发
|
||||
* @event {Function} callBack 确定按钮回调方法
|
||||
* @event {Function} custom_change 自定义按钮回调方法
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'uniPopup',
|
||||
components: {
|
||||
// #ifdef H5
|
||||
keypress,
|
||||
// #endif
|
||||
},
|
||||
emits: ['change', 'maskClick', 'callBack', 'callBackCustom'],
|
||||
props: {
|
||||
// 开启动画
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
||||
// message: 消息提示 ; dialog : 对话框
|
||||
type: {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
},
|
||||
// maskClick
|
||||
isMaskClick: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
// TODO 2 个版本后废弃属性 ,使用 isMaskClick
|
||||
maskClick: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: 'none',
|
||||
},
|
||||
safeArea: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
maskBackgroundColor: {
|
||||
type: String,
|
||||
default: 'rgba(0, 0, 0, 0.4)',
|
||||
},
|
||||
// 是否显示关闭图标
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// icon
|
||||
closeIcon: {
|
||||
type: String,
|
||||
default: 'close-line',
|
||||
},
|
||||
// icon颜色
|
||||
closeIconType: {
|
||||
type: String,
|
||||
default: '6',
|
||||
},
|
||||
// 关闭图标大小
|
||||
closeIconSize: {
|
||||
type: Number,
|
||||
default: 32,
|
||||
},
|
||||
// 关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角,top-center为外部上部中间位置, bottom-center为外部底部中间位置,
|
||||
closeIconPos: {
|
||||
type: String,
|
||||
validator: (value) => ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'top-center', 'bottom-center'].includes(value),
|
||||
},
|
||||
closeType: {
|
||||
type: String,
|
||||
default: 'icon',
|
||||
},
|
||||
isCustomBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
customBtnText: {
|
||||
type: String,
|
||||
default: '自定义',
|
||||
},
|
||||
customBtnStyle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
titleBorder: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: 'auto',
|
||||
},
|
||||
round: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
isConfirmClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
/**
|
||||
* 监听type类型
|
||||
*/
|
||||
type: {
|
||||
handler: function (type) {
|
||||
if (!this.config[type]) return;
|
||||
this[this.config[type]](true);
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
isDesktop: {
|
||||
handler: function (newVal) {
|
||||
if (!this.config[newVal]) return;
|
||||
this[this.config[this.type]](true);
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
/**
|
||||
* 监听遮罩是否可点击
|
||||
* @param {Object} val
|
||||
*/
|
||||
maskClick: {
|
||||
handler: function (val) {
|
||||
this.mkclick = val;
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
isMaskClick: {
|
||||
handler: function (val) {
|
||||
this.mkclick = val;
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
// H5 下禁止底部滚动
|
||||
showPopup(show) {
|
||||
// #ifdef H5
|
||||
// fix by mehaotian 处理 h5 滚动穿透的问题
|
||||
document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible';
|
||||
// #endif
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
duration: 300,
|
||||
ani: [],
|
||||
showPopup: false,
|
||||
showTrans: false,
|
||||
popupWidth: 0,
|
||||
popupHeight: 0,
|
||||
config: {
|
||||
top: 'top',
|
||||
bottom: 'bottom',
|
||||
center: 'center',
|
||||
left: 'left',
|
||||
right: 'right',
|
||||
message: 'top',
|
||||
dialog: 'center',
|
||||
share: 'bottom',
|
||||
},
|
||||
maskClass: {
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
top: '-1000%',
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
||||
},
|
||||
transClass: {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
maskShow: true,
|
||||
mkclick: true,
|
||||
popupstyle: this.isDesktop ? 'fixforpc-top' : 'top',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isDesktop() {
|
||||
return this.popupWidth >= 500 && this.popupHeight >= 500;
|
||||
},
|
||||
bg() {
|
||||
if (this.backgroundColor === '' || this.backgroundColor === 'none') {
|
||||
return '#fff';
|
||||
}
|
||||
return this.backgroundColor;
|
||||
},
|
||||
// transition固定样式
|
||||
transitionFixedStyle() {
|
||||
let style = {
|
||||
backgroundColor: this.bgColor,
|
||||
zIndex: this.zIndex,
|
||||
};
|
||||
switch (this.mode) {
|
||||
case 'top':
|
||||
style['borderBottomLeftRadius'] = this.round + 'px';
|
||||
style['borderBottomRightRadius'] = this.round + 'px';
|
||||
break;
|
||||
case 'bottom':
|
||||
style['borderTopLeftRadius'] = this.round + 'px';
|
||||
style['borderTopRightRadius'] = this.round + 'px';
|
||||
break;
|
||||
case 'left':
|
||||
style['borderTopRightRadius'] = this.round + 'px';
|
||||
style['borderBottomRightRadius'] = this.round + 'px';
|
||||
break;
|
||||
case 'right':
|
||||
style['borderTopLeftRadius'] = this.round + 'px';
|
||||
style['borderBottomLeftRadius'] = this.round + 'px';
|
||||
break;
|
||||
case 'center':
|
||||
style['borderRadius'] = this.round + 'px';
|
||||
break;
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
// 关闭Icon样式
|
||||
closeIconStyle() {
|
||||
let style = {
|
||||
zIndex: 999,
|
||||
};
|
||||
let posSize = 28;
|
||||
switch (this.closeIconPos) {
|
||||
case 'top-left':
|
||||
style['position'] = 'absolute';
|
||||
style['top'] = '0rpx';
|
||||
style['left'] = '0rpx';
|
||||
break;
|
||||
case 'top-right':
|
||||
style['position'] = 'absolute';
|
||||
style['top'] = '0rpx';
|
||||
style['right'] = '0rpx';
|
||||
break;
|
||||
case 'top-center':
|
||||
style['position'] = 'absolute';
|
||||
style['top'] = -(posSize * 4) + 'rpx';
|
||||
style['left'] = '0rpx';
|
||||
style['right'] = '0rpx';
|
||||
break;
|
||||
case 'bottom-left':
|
||||
style['position'] = 'absolute';
|
||||
style['bottom'] = '0rpx';
|
||||
style['left'] = '0rpx';
|
||||
break;
|
||||
case 'bottom-right':
|
||||
style['position'] = 'absolute';
|
||||
style['bottom'] = '0rpx';
|
||||
style['right'] = '0rpx';
|
||||
break;
|
||||
case 'bottom-center':
|
||||
style['position'] = 'absolute';
|
||||
style['bottom'] = -(posSize * 4) + 'rpx';
|
||||
style['left'] = '0rpx';
|
||||
style['right'] = '0rpx';
|
||||
break;
|
||||
default:
|
||||
if (this.title != '') {
|
||||
style['position'] = 'absolute';
|
||||
style['top'] = '0rpx';
|
||||
style['right'] = '0rpx';
|
||||
}
|
||||
break;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const fixSize = () => {
|
||||
const { windowWidth, windowHeight, windowTop, safeArea, screenHeight, safeAreaInsets } = uni.getSystemInfoSync();
|
||||
this.popupWidth = windowWidth;
|
||||
this.popupHeight = windowHeight + (windowTop || 0);
|
||||
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
|
||||
if (safeArea && this.safeArea) {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.safeAreaInsets = screenHeight - safeArea.bottom;
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
this.safeAreaInsets = safeAreaInsets.bottom;
|
||||
// #endif
|
||||
} else {
|
||||
this.safeAreaInsets = 0;
|
||||
}
|
||||
};
|
||||
fixSize();
|
||||
// #ifdef H5
|
||||
// window.addEventListener('resize', fixSize)
|
||||
// this.$once('hook:beforeDestroy', () => {
|
||||
// window.removeEventListener('resize', fixSize)
|
||||
// })
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
// 修复nvue 遮罩层位置问题
|
||||
const data = uni.getWindowInfo();
|
||||
this.maskClass.top = - data.windowHeight + 'px;';
|
||||
// #endif
|
||||
},
|
||||
// #ifndef VUE3
|
||||
// TODO vue2
|
||||
destroyed() {
|
||||
this.setH5Visible();
|
||||
},
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
// TODO vue3
|
||||
unmounted() {
|
||||
this.setH5Visible();
|
||||
},
|
||||
// #endif
|
||||
created() {
|
||||
// this.mkclick = this.isMaskClick || this.maskClick
|
||||
if (this.isMaskClick === null && this.maskClick === null) {
|
||||
this.mkclick = true;
|
||||
} else {
|
||||
this.mkclick = this.isMaskClick != null ? this.isMaskClick : this.maskClick;
|
||||
}
|
||||
if (this.animation) {
|
||||
this.duration = 300;
|
||||
} else {
|
||||
this.duration = 0;
|
||||
}
|
||||
// TODO 处理 message 组件生命周期异常的问题
|
||||
this.messageChild = null;
|
||||
// TODO 解决头条冒泡的问题
|
||||
this.clearPropagation = false;
|
||||
this.maskClass.backgroundColor = this.maskBackgroundColor;
|
||||
},
|
||||
methods: {
|
||||
setH5Visible() {
|
||||
// #ifdef H5
|
||||
// fix by mehaotian 处理 h5 滚动穿透的问题
|
||||
document.getElementsByTagName('body')[0].style.overflow = 'visible';
|
||||
// #endif
|
||||
},
|
||||
/**
|
||||
* 公用方法,不显示遮罩层
|
||||
*/
|
||||
closeMask() {
|
||||
this.maskShow = false;
|
||||
},
|
||||
/**
|
||||
* 公用方法,遮罩层禁止点击
|
||||
*/
|
||||
disableMask() {
|
||||
this.mkclick = false;
|
||||
},
|
||||
// TODO nvue 取消冒泡
|
||||
clear(e) {
|
||||
// #ifndef APP-NVUE
|
||||
e.stopPropagation();
|
||||
// #endif
|
||||
this.clearPropagation = true;
|
||||
},
|
||||
|
||||
open(direction) {
|
||||
// fix by mehaotian 处理快速打开关闭的情况
|
||||
if (this.showPopup) {
|
||||
clearTimeout(this.timer);
|
||||
this.showPopup = false;
|
||||
}
|
||||
let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share'];
|
||||
if (!(direction && innerType.indexOf(direction) != -1)) {
|
||||
direction = this.type;
|
||||
}
|
||||
if (!this.config[direction]) {
|
||||
return;
|
||||
}
|
||||
this[this.config[direction]]();
|
||||
this.$emit('change', {
|
||||
show: true,
|
||||
type: direction,
|
||||
});
|
||||
},
|
||||
close(type) {
|
||||
this.showTrans = false;
|
||||
this.$emit('change', {
|
||||
show: false,
|
||||
type: this.type,
|
||||
});
|
||||
clearTimeout(this.timer);
|
||||
// // 自定义关闭事件
|
||||
// this.customOpen && this.customClose()
|
||||
this.timer = setTimeout(() => {
|
||||
this.showPopup = false;
|
||||
}, 300);
|
||||
},
|
||||
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
|
||||
touchstart() {
|
||||
this.clearPropagation = false;
|
||||
},
|
||||
|
||||
onTap() {
|
||||
if (this.clearPropagation) {
|
||||
// fix by mehaotian 兼容 nvue
|
||||
this.clearPropagation = false;
|
||||
return;
|
||||
}
|
||||
this.$emit('maskClick');
|
||||
if (!this.mkclick) return;
|
||||
this.close();
|
||||
},
|
||||
/**
|
||||
* 顶部弹出样式处理
|
||||
*/
|
||||
top(type) {
|
||||
this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top';
|
||||
this.ani = ['slide-top'];
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: this.bg,
|
||||
};
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return;
|
||||
this.showPopup = true;
|
||||
this.showTrans = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.messageChild && this.type === 'message') {
|
||||
this.messageChild.timerClose();
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 底部弹出样式处理
|
||||
*/
|
||||
bottom(type) {
|
||||
this.popupstyle = 'bottom';
|
||||
this.ani = ['slide-bottom'];
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
paddingBottom: this.safeAreaInsets + 'px',
|
||||
backgroundColor: this.bg,
|
||||
};
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return;
|
||||
this.showPopup = true;
|
||||
this.showTrans = true;
|
||||
},
|
||||
/**
|
||||
* 中间弹出样式处理
|
||||
*/
|
||||
center(type) {
|
||||
this.popupstyle = 'center';
|
||||
this.ani = ['zoom-out', 'fade'];
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
/* #ifndef APP-NVUE */
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
/* #endif */
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
};
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return;
|
||||
this.showPopup = true;
|
||||
this.showTrans = true;
|
||||
},
|
||||
left(type) {
|
||||
this.popupstyle = 'left';
|
||||
this.ani = ['slide-left'];
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
backgroundColor: this.bg,
|
||||
/* #ifndef APP-NVUE */
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
/* #endif */
|
||||
};
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return;
|
||||
this.showPopup = true;
|
||||
this.showTrans = true;
|
||||
},
|
||||
right(type) {
|
||||
this.popupstyle = 'right';
|
||||
this.ani = ['slide-right'];
|
||||
this.transClass = {
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
backgroundColor: this.bg,
|
||||
/* #ifndef APP-NVUE */
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
/* #endif */
|
||||
};
|
||||
// TODO 兼容 type 属性 ,后续会废弃
|
||||
if (type) return;
|
||||
this.showPopup = true;
|
||||
this.showTrans = true;
|
||||
},
|
||||
comfirm() {
|
||||
if (this.isConfirmClose) {
|
||||
this.close();
|
||||
}
|
||||
this.$emit('callBack');
|
||||
},
|
||||
// 自定义按钮点击回调
|
||||
custom_change() {
|
||||
if (this.isConfirmClose) {
|
||||
this.close();
|
||||
}
|
||||
this.$emit('callBackCustom');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.uni-popup {
|
||||
position: fixed;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 99;
|
||||
|
||||
/* #endif */
|
||||
&.top,
|
||||
&.left,
|
||||
&.right {
|
||||
/* #ifdef H5 */
|
||||
top: var(--window-top);
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
&.top {
|
||||
.popup-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.center,
|
||||
&.bottom,
|
||||
&.left {
|
||||
.popup-close {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
&.right {
|
||||
.popup-close {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.uni-popup__wrapper {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
|
||||
/* iphonex 等安全区设置,底部安全区适配 */
|
||||
/* #ifndef APP-NVUE */
|
||||
// padding-bottom: constant(safe-area-inset-bottom);
|
||||
// padding-bottom: env(safe-area-inset-bottom);
|
||||
/* #endif */
|
||||
&.left,
|
||||
&.right {
|
||||
/* #ifdef H5 */
|
||||
padding-top: var(--window-top);
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
padding-top: 0;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
padding: 28rpx;
|
||||
&.top-center,
|
||||
&.bottom-center {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.fixforpc-z-index {
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 999;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fixforpc-top {
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
// const defaultOption = {
|
||||
// duration: 300,
|
||||
// timingFunction: 'linear',
|
||||
// delay: 0,
|
||||
// transformOrigin: '50% 50% 0'
|
||||
// }
|
||||
// #ifdef APP-NVUE
|
||||
const nvueAnimation = uni.requireNativePlugin('animation')
|
||||
// #endif
|
||||
class MPAnimation {
|
||||
constructor(options, _this) {
|
||||
this.options = options
|
||||
// #ifndef APP-NVUE
|
||||
// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
|
||||
this.animation = uni.createAnimation({
|
||||
...options
|
||||
})
|
||||
//#endif
|
||||
this.currentStepAnimates = {}
|
||||
this.next = 0
|
||||
this.$ = _this
|
||||
|
||||
}
|
||||
|
||||
_nvuePushAnimates (type, args) {
|
||||
let aniObj = this.currentStepAnimates[this.next]
|
||||
let styles = {}
|
||||
if (!aniObj) {
|
||||
styles = {
|
||||
styles: {},
|
||||
config: {}
|
||||
}
|
||||
} else {
|
||||
styles = aniObj
|
||||
}
|
||||
if (animateTypes1.includes(type)) {
|
||||
if (!styles.styles.transform) {
|
||||
styles.styles.transform = ''
|
||||
}
|
||||
let unit = ''
|
||||
if (type === 'rotate') {
|
||||
unit = 'deg'
|
||||
}
|
||||
styles.styles.transform += `${type}(${args + unit}) `
|
||||
} else {
|
||||
styles.styles[type] = `${args}`
|
||||
}
|
||||
this.currentStepAnimates[this.next] = styles
|
||||
}
|
||||
_animateRun (styles = {}, config = {}) {
|
||||
let ref = this.$.$refs['ani'].ref
|
||||
if (!ref) return
|
||||
return new Promise((resolve, reject) => {
|
||||
nvueAnimation.transition(ref, {
|
||||
styles,
|
||||
...config
|
||||
}, res => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_nvueNextAnimate (animates, step = 0, fn) {
|
||||
let obj = animates[step]
|
||||
if (obj) {
|
||||
let {
|
||||
styles,
|
||||
config
|
||||
} = obj
|
||||
this._animateRun(styles, config).then(() => {
|
||||
step += 1
|
||||
this._nvueNextAnimate(animates, step, fn)
|
||||
})
|
||||
} else {
|
||||
this.currentStepAnimates = {}
|
||||
typeof fn === 'function' && fn()
|
||||
this.isEnd = true
|
||||
}
|
||||
}
|
||||
|
||||
step (config = {}) {
|
||||
// #ifndef APP-NVUE
|
||||
this.animation.step(config)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
|
||||
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
|
||||
this.next++
|
||||
// #endif
|
||||
return this
|
||||
}
|
||||
|
||||
run (fn) {
|
||||
// #ifndef APP-NVUE
|
||||
this.$.animationData = this.animation.export()
|
||||
this.$.timer = setTimeout(() => {
|
||||
typeof fn === 'function' && fn()
|
||||
}, this.$.durationTime)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.isEnd = false
|
||||
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
|
||||
if (!ref) return
|
||||
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
|
||||
this.next = 0
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
|
||||
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
|
||||
'translateZ'
|
||||
]
|
||||
const animateTypes2 = ['opacity', 'backgroundColor']
|
||||
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
|
||||
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
|
||||
MPAnimation.prototype[type] = function (...args) {
|
||||
// #ifndef APP-NVUE
|
||||
this.animation[type](...args)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this._nvuePushAnimates(type, args)
|
||||
// #endif
|
||||
return this
|
||||
}
|
||||
})
|
||||
|
||||
export function createAnimation (option, _this) {
|
||||
if (!_this) return
|
||||
clearTimeout(_this.timer)
|
||||
return new MPAnimation(option, _this)
|
||||
}
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
<template>
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
<script>
|
||||
import { createAnimation } from './createAnimation';
|
||||
/**
|
||||
* transition 动画组件
|
||||
* @description
|
||||
* @tutorial
|
||||
* @property {Boolean} show 控制组件显示或关闭 (默认 false )
|
||||
* @property {Array | String} mode 内置过渡动画类型 (默认 'fade' )
|
||||
* @value fade 渐隐渐出过渡
|
||||
* @value slide-top 由上至下过渡
|
||||
* @value slide-bottom 由下至上过渡
|
||||
* @value slide-left 由左至右过渡
|
||||
* @value slide-right 由右至左过渡
|
||||
* @value zoom-in 由小到大过渡
|
||||
* @value zoom-out 由大到小过渡
|
||||
* @property {String | Number} duration 动画的执行时间,单位ms (默认 300 )
|
||||
* @property {Object} customStyle 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
|
||||
* @property {String} timingFunction 使用的动画过渡函数 (默认 'ease-out' )
|
||||
* @property {String} customClass 自定义类名
|
||||
* @event {Function} click 点击组件触发
|
||||
* @event {Function} change 过渡动画结束时触发
|
||||
* @example
|
||||
*/
|
||||
export default {
|
||||
name: 'u-transition',
|
||||
emits: ['click', 'change'],
|
||||
props: {
|
||||
// 是否展示组件
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 使用的动画模式
|
||||
mode: {
|
||||
type: [Array, String, null],
|
||||
default() {
|
||||
return 'fade';
|
||||
},
|
||||
},
|
||||
// 动画的执行时间,单位ms
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: 300,
|
||||
},
|
||||
// 使用的动画过渡函数
|
||||
timingFunction: {
|
||||
type: String,
|
||||
default: 'ease-out',
|
||||
},
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
customClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// nvue模式下 是否直接显示,在uv-list等cell下面使用就需要设置
|
||||
cellChild: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
transform: '',
|
||||
opacity: 1,
|
||||
animationData: {},
|
||||
durationTime: 300,
|
||||
config: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.open();
|
||||
} else {
|
||||
// 避免上来就执行 close,导致动画错乱
|
||||
if (this.isShow) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// 初始化动画条件
|
||||
transformStyles() {
|
||||
const style = {
|
||||
transform: this.transform,
|
||||
opacity: this.opacity,
|
||||
...this.addStyle(this.customStyle),
|
||||
'transition-duration': `${this.duration / 1000}s`,
|
||||
};
|
||||
return this.addStyle(style, 'string');
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// 动画默认配置
|
||||
this.config = {
|
||||
duration: this.duration,
|
||||
timingFunction: this.timingFunction,
|
||||
transformOrigin: '50% 50%',
|
||||
delay: 0,
|
||||
};
|
||||
this.durationTime = this.duration;
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @description 样式转换
|
||||
* 对象转字符串,或者字符串转对象
|
||||
* @param {object | string} customStyle 需要转换的目标
|
||||
* @param {String} target 转换的目的,object-转为对象,string-转为字符串
|
||||
* @returns {object|string}
|
||||
*/
|
||||
addStyle (customStyle, target = 'object') {
|
||||
// 字符串转字符串,对象转对象情形,直接返回
|
||||
if (this.empty(customStyle) || typeof (customStyle) === 'object' && target === 'object' || target === 'string' &&
|
||||
typeof (customStyle) === 'string') {
|
||||
return customStyle
|
||||
}
|
||||
// 字符串转对象
|
||||
if (target === 'object') {
|
||||
// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
|
||||
customStyle = this.trim(customStyle)
|
||||
// 根据";"将字符串转为数组形式
|
||||
const styleArray = customStyle.split(';')
|
||||
const style = {}
|
||||
// 历遍数组,拼接成对象
|
||||
for (let i = 0; i < styleArray.length; i++) {
|
||||
// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
|
||||
if (styleArray[i]) {
|
||||
const item = styleArray[i].split(':')
|
||||
style[this.trim(item[0])] = this.trim(item[1])
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
// 这里为对象转字符串形式
|
||||
let string = ''
|
||||
for (const i in customStyle) {
|
||||
// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
|
||||
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
string += `${key}:${customStyle[i]};`
|
||||
}
|
||||
// 去除两端空格
|
||||
return this.trim(string)
|
||||
},
|
||||
/**
|
||||
* 判断是否为空
|
||||
*/
|
||||
empty (value) {
|
||||
switch (typeof value) {
|
||||
case 'undefined':
|
||||
return true
|
||||
case 'string':
|
||||
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
|
||||
break
|
||||
case 'boolean':
|
||||
if (!value) return true
|
||||
break
|
||||
case 'number':
|
||||
if (value === 0 || isNaN(value)) return true
|
||||
break
|
||||
case 'object':
|
||||
if (value === null || value.length === 0) return true
|
||||
for (const i in value) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
/**
|
||||
* @description 去除空格
|
||||
* @param {String} str 需要去除空格的字符串
|
||||
* @param {String} pos both(左右)|left|right|all 默认both
|
||||
*/
|
||||
trim (str, pos = 'both') {
|
||||
str = String(str)
|
||||
if (pos == 'both') {
|
||||
return str.replace(/^\s+|\s+$/g, '')
|
||||
}
|
||||
if (pos == 'left') {
|
||||
return str.replace(/^\s*/, '')
|
||||
}
|
||||
if (pos == 'right') {
|
||||
return str.replace(/(\s*$)/g, '')
|
||||
}
|
||||
if (pos == 'all') {
|
||||
return str.replace(/\s+/g, '')
|
||||
}
|
||||
return str
|
||||
},
|
||||
/**
|
||||
* ref 触发 初始化动画
|
||||
*/
|
||||
init(obj = {}) {
|
||||
if (obj.duration) {
|
||||
this.durationTime = obj.duration;
|
||||
}
|
||||
this.animation = createAnimation(Object.assign(this.config, obj), this);
|
||||
},
|
||||
/**
|
||||
* 点击组件触发回调
|
||||
*/
|
||||
onClick() {
|
||||
this.$emit('click', {
|
||||
detail: this.isShow,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* ref 触发 动画分组
|
||||
* @param {Object} obj
|
||||
*/
|
||||
step(obj, config = {}) {
|
||||
if (!this.animation) return;
|
||||
for (let i in obj) {
|
||||
try {
|
||||
if (typeof obj[i] === 'object') {
|
||||
this.animation[i](...obj[i]);
|
||||
} else {
|
||||
this.animation[i](obj[i]);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
this.animation.step(config);
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* ref 触发 执行动画
|
||||
*/
|
||||
run(fn) {
|
||||
if (!this.animation) return;
|
||||
this.animation.run(fn);
|
||||
},
|
||||
// 开始过度动画
|
||||
open() {
|
||||
clearTimeout(this.timer);
|
||||
this.transform = '';
|
||||
this.isShow = true;
|
||||
let { opacity, transform } = this.styleInit(false);
|
||||
if (typeof opacity != 'undefined') {
|
||||
this.opacity = opacity;
|
||||
}
|
||||
this.transform = transform;
|
||||
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
|
||||
this.$nextTick(() => {
|
||||
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
|
||||
this.timer = setTimeout(() => {
|
||||
this.animation = createAnimation(this.config, this);
|
||||
this.tranfromInit(false).step();
|
||||
// #ifdef APP-NVUE
|
||||
if (this.cellChild) {
|
||||
this.opacity = 1;
|
||||
} else {
|
||||
this.animation.run();
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
this.animation.run();
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
// #ifdef H5
|
||||
this.opacity = 1;
|
||||
// #endif
|
||||
// #endif
|
||||
this.$emit('change', {
|
||||
detail: this.isShow,
|
||||
});
|
||||
// #ifdef H5
|
||||
// #ifdef VUE3
|
||||
this.transform = '';
|
||||
// #endif
|
||||
// #endif
|
||||
}, 20);
|
||||
});
|
||||
},
|
||||
// 关闭过渡动画
|
||||
close(type) {
|
||||
if (!this.animation) return;
|
||||
this.tranfromInit(true)
|
||||
.step()
|
||||
.run(() => {
|
||||
this.isShow = false;
|
||||
this.animationData = null;
|
||||
this.animation = null;
|
||||
let { opacity, transform } = this.styleInit(false);
|
||||
this.opacity = opacity || 1;
|
||||
this.transform = transform;
|
||||
this.$emit('change', {
|
||||
detail: this.isShow,
|
||||
});
|
||||
});
|
||||
},
|
||||
// 处理动画开始前的默认样式
|
||||
styleInit(type) {
|
||||
let styles = {
|
||||
transform: '',
|
||||
};
|
||||
let buildStyle = (type, mode) => {
|
||||
if (mode === 'fade') {
|
||||
styles.opacity = this.animationType(type)[mode];
|
||||
} else {
|
||||
styles.transform += this.animationType(type)[mode] + ' ';
|
||||
}
|
||||
};
|
||||
if (typeof this.mode === 'string') {
|
||||
buildStyle(type, this.mode);
|
||||
} else {
|
||||
this.mode.forEach((mode) => {
|
||||
buildStyle(type, mode);
|
||||
});
|
||||
}
|
||||
return styles;
|
||||
},
|
||||
// 处理内置组合动画
|
||||
tranfromInit(type) {
|
||||
let buildTranfrom = (type, mode) => {
|
||||
let aniNum = null;
|
||||
if (mode === 'fade') {
|
||||
aniNum = type ? 0 : 1;
|
||||
} else {
|
||||
aniNum = type ? '-100%' : '0';
|
||||
if (mode === 'zoom-in') {
|
||||
aniNum = type ? 0.8 : 1;
|
||||
}
|
||||
if (mode === 'zoom-out') {
|
||||
aniNum = type ? 1.2 : 1;
|
||||
}
|
||||
if (mode === 'slide-right') {
|
||||
aniNum = type ? '100%' : '0';
|
||||
}
|
||||
if (mode === 'slide-bottom') {
|
||||
aniNum = type ? '100%' : '0';
|
||||
}
|
||||
}
|
||||
this.animation[this.animationMode()[mode]](aniNum);
|
||||
};
|
||||
if (typeof this.mode === 'string') {
|
||||
buildTranfrom(type, this.mode);
|
||||
} else {
|
||||
this.mode.forEach((mode) => {
|
||||
buildTranfrom(type, mode);
|
||||
});
|
||||
}
|
||||
return this.animation;
|
||||
},
|
||||
animationType(type) {
|
||||
return {
|
||||
fade: type ? 1 : 0,
|
||||
'slide-top': `translateY(${type ? '0' : '-100%'})`,
|
||||
'slide-right': `translateX(${type ? '0' : '100%'})`,
|
||||
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
|
||||
'slide-left': `translateX(${type ? '0' : '-100%'})`,
|
||||
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
|
||||
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`,
|
||||
};
|
||||
},
|
||||
// 内置动画类型与实际动画对应字典
|
||||
animationMode() {
|
||||
return {
|
||||
fade: 'opacity',
|
||||
'slide-top': 'translateY',
|
||||
'slide-right': 'translateX',
|
||||
'slide-bottom': 'translateY',
|
||||
'slide-left': 'translateX',
|
||||
'zoom-in': 'scale',
|
||||
'zoom-out': 'scale',
|
||||
};
|
||||
},
|
||||
// 驼峰转中横线
|
||||
toLine(name) {
|
||||
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<view :class="theme_view + ' bg-0 flex-row'" :style="'width:' + windowWidth + 'px;height:' + windowHeight + 'px;'">
|
||||
<view class="flex-1">
|
||||
<live-video src="http://live-pull-all.shopxo.vip/68f764013572f9240ca7ce6c/shopxo122.m3u8"></live-video>
|
||||
</view>
|
||||
<view class="live-content" :style="'width:' + windowWidth + 'px;height:' + windowHeight + 'px;'">
|
||||
<live-content></live-content>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import liveVideo from './video.vue';
|
||||
import liveContent from './components/live-content.vue';
|
||||
const app = getApp();
|
||||
export default {
|
||||
components: {
|
||||
liveVideo,
|
||||
liveContent
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
windowWidth: 0,
|
||||
windowHeight: 0,
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(params) {
|
||||
// 调用公共事件方法
|
||||
app.globalData.page_event_onload_handle(params);
|
||||
// 设置参数
|
||||
this.params = app.globalData.launch_params_handle(params);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 调用公共事件方法
|
||||
app.globalData.page_event_onshow_handle();
|
||||
|
||||
// 分享菜单处理
|
||||
app.globalData.page_share_handle();
|
||||
const data = uni.getWindowInfo();
|
||||
this.windowWidth = data.windowWidth;
|
||||
this.windowHeight = data.windowHeight;
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.live-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
|||
## 1.2.1(2023-07-25)
|
||||
更新节流函数不执行导致DOM不会逐渐消失问题
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"id": "like-button",
|
||||
"displayName": "点赞冒泡效果 like-button",
|
||||
"version": "1.2.1",
|
||||
"description": "uni-app 点赞冒泡效果,兼容nvue。",
|
||||
"keywords": [
|
||||
"like-button",
|
||||
"点赞",
|
||||
"冒泡"
|
||||
],
|
||||
"repository": "https://github.com/lnice/like-button",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# like-button
|
||||
|
||||
#### 使用方法:
|
||||
在 **script** 中引用组件
|
||||
```javascript
|
||||
import likeButton from '@/components/like-button/like-button.vue'
|
||||
export default {
|
||||
components: {likeButton}
|
||||
}
|
||||
```
|
||||
在 **template** 中引用组件
|
||||
```javascript
|
||||
<like-button></like-button>
|
||||
```
|
||||
#### 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ------------ | ------------ | ------------ | ------------ |
|
||||
| src | String | /static/logo.png | 点击按钮图片 |
|
||||
| showImgs | Array | ['//xxx', '/static/logo.png', ] | 冒泡图片 |
|
||||
| duration | Number | 5000 | 动画效果时间 |
|
||||
| range | Number | 50 | 冒泡图片x轴摇摆幅度 |
|
||||
| high | Number | 360 | 冒泡图片y轴飘出高度 |
|
||||
| width | Number | 52 | 点赞图标宽度 |
|
||||
| height | Number | 52 | 点赞图标高度 |
|
||||
| imgWidth | Number | 52 | 冒泡图标宽度 |
|
||||
| imgHeight | Number | 52 | 冒泡图标高度 |
|
||||
| throttle | Number | 200 | 点击按钮 节流 |
|
||||
| site | Array,Object | [30, 160] | 冒泡图片相对窗口x y坐标 |
|
||||
| large | Number,Boolean | false | 缩放冒泡,为true默认放大2 |
|
||||
| alone | Boolean | true | 1.0.9-新增, DOM元素逐渐消失 |
|
||||
#### 事件说明
|
||||
| 事件名称 | 说明 | 返回值 |
|
||||
| ------------ | ------------ | ------------ |
|
||||
| handleClick | 点击按钮触发事件 | 冒泡元素id |
|
||||
| finished | 动画执行完成回调 | - |
|
||||
|
||||
PS:使用定时器触发可用 this.$refs.likeButton.handleClick({timeStamp: Date.now()})。
|
||||
|
||||
---
|
||||
### End
|
||||
Loading…
Reference in New Issue