提交 27bef0cc 编写于 作者: W wanganxp

Merge branch 'dev' of https://gitcode.net/dcloud/hello-uni-app-x into dev

## 1.0.5(2024-01-12)
- 优化 删除组件内无用日志输出
## 1.0.4(2024-01-10) ## 1.0.4(2024-01-10)
- 优化 兼容 uvue h5 项目 - 优化 兼容 uvue h5 项目
## 1.0.3(2023-12-22) ## 1.0.3(2023-12-22)
......
<template> <template>
<!-- #ifdef APP --> <!-- #ifdef APP -->
<view :ref="elId" class="block" :style="{width:size+'px',height:size+'px'}"></view> <view :ref="elId" class="block" :style="{width:size+'px',height:size+'px'}"></view>
<!-- #endif --> <!-- #endif -->
<!-- #ifdef WEB --> <!-- #ifdef WEB -->
<svg :width="size" :height="size" viewBox="25 25 50 50" :style="{width:size+'px',height:size+'px'}" class="uni-load__img uni-load__img--android-H5"> <svg :width="size" :height="size" viewBox="25 25 50 50" :style="{width:size+'px',height:size+'px'}" class="uni-load__img uni-load__img--android-H5">
<circle cx="50" cy="50" r="20" fill="none" :style="{color:color}" :stroke-width="iconsSize"></circle> <circle cx="50" cy="50" r="20" fill="none" :style="{color:color}" :stroke-width="iconsSize"></circle>
</svg> </svg>
<!-- #endif --> <!-- #endif -->
</template> </template>
<script> <script>
import { easeInOutCubic } from './util' import { easeInOutCubic } from './util'
let elId = 0 let elId = 0
export default { export default {
name: "circle", name: "circle",
props: { props: {
speed: { speed: {
type: Number, type: Number,
default: 16, default: 16,
}, },
size: { size: {
type: Number, type: Number,
default: 20, default: 20,
}, },
color: { color: {
type: String, type: String,
default: '#666', default: '#666',
} }
}, },
data() { data() {
// 防止多调用,随机元素id // 防止多调用,随机元素id
elId += 1 elId += 1
const elID = `Uni_Load_Circle_${elId}` const elID = `Uni_Load_Circle_${elId}`
return { return {
elId: elID, elId: elID,
timer: 0, timer: 0,
}; };
}, },
computed: { computed: {
iconsSize() : number { iconsSize() : number {
console.log(this.size / 10); return (this.size / 10) +1
return (this.size / 10) +1 }
} },
}, mounted() {
mounted() { // #ifdef APP
// #ifdef APP this.init()
this.init() // #endif
// #endif },
}, unmounted() {
unmounted() { // 组件卸载时,需要卸载定时器,优化性能,防止页面卡死
// 组件卸载时,需要卸载定时器,优化性能,防止页面卡死 clearInterval(this.timer)
clearInterval(this.timer) },
}, methods: {
methods: { /**
/** * 初始化圆环
* 初始化圆环 */
*/ init() {
init() { const refs = this.$refs[this.elId] as UniElement
const refs = this.$refs[this.elId] as UniElement let ctx = refs.getDrawableContext()!
let ctx = refs.getDrawableContext()! this.build_circular(ctx)
this.build_circular(ctx) },
},
/**
/** * 构建圆环动画
* 构建圆环动画 */
*/ build_circular(ctx : DrawableContext) {
build_circular(ctx : DrawableContext) { let startAngle = 0;
let startAngle = 0; let rotate = 0;
let rotate = 0; const ARC_LENGTH = 359;
const ARC_LENGTH = 359; const center = this.size / 2; // 圆心
const center = this.size / 2; // 圆心 const lineWidth = this.size / 10; // 圆环宽度
const lineWidth = this.size / 10; // 圆环宽度 const duration = 1200; // 动画持续时间
const duration = 1200; // 动画持续时间 const interval = this.speed; // 定时器间隔(大约 60 帧每秒)
const interval = this.speed; // 定时器间隔(大约 60 帧每秒)
// 使圆环过度更自然,不必运动到底
// 使圆环过度更自然,不必运动到底 const ARC_MAX = 358
const ARC_MAX = 358 let startTime = 0;
let startTime = 0; let foreward_end = 0 // 正传
let foreward_end = 0 // 正传 let reversal_end = ARC_MAX // 反转
let reversal_end = ARC_MAX // 反转 function pogress_time() : number {
function pogress_time() : number { const currentTime = Date.now();
const currentTime = Date.now(); // 运动时间计算
// 运动时间计算 const elapsedTime = currentTime - startTime;
const elapsedTime = currentTime - startTime; const progress = elapsedTime / duration;
const progress = elapsedTime / duration; // 动画缓动
// 动画缓动 const easedProgress = easeInOutCubic(progress);
const easedProgress = easeInOutCubic(progress); return easedProgress
return easedProgress }
} const draw = () => {
const draw = () => {
ctx.reset();
ctx.reset(); ctx.beginPath();
ctx.beginPath();
if (reversal_end == ARC_MAX) {
if (reversal_end == ARC_MAX) { foreward_end = Math.min(pogress_time() * ARC_LENGTH, ARC_LENGTH); // 限制 end 的最大值为 ARC_LENGTH
foreward_end = Math.min(pogress_time() * ARC_LENGTH, ARC_LENGTH); // 限制 end 的最大值为 ARC_LENGTH if (foreward_end >= ARC_MAX) {
if (foreward_end >= ARC_MAX) { reversal_end = 0
reversal_end = 0 foreward_end = ARC_MAX
foreward_end = ARC_MAX startTime = Date.now();
startTime = Date.now(); }
} }
}
if (foreward_end == ARC_MAX) {
if (foreward_end == ARC_MAX) { reversal_end = Math.min(pogress_time() * ARC_LENGTH, ARC_LENGTH);
reversal_end = Math.min(pogress_time() * ARC_LENGTH, ARC_LENGTH); if (reversal_end >= ARC_MAX) {
if (reversal_end >= ARC_MAX) { reversal_end = ARC_MAX
reversal_end = ARC_MAX foreward_end = 0
foreward_end = 0 startTime = Date.now();
startTime = Date.now(); }
} }
}
ctx.arc(
ctx.arc( center,
center, center,
center, center - lineWidth,
center - lineWidth, startAngle + rotate + (reversal_end * Math.PI / 180),
startAngle + rotate + (reversal_end * Math.PI / 180), startAngle + rotate + (foreward_end * Math.PI / 180)
startAngle + rotate + (foreward_end * Math.PI / 180) );
); ctx.lineWidth = lineWidth;
ctx.lineWidth = lineWidth; const fillColor = (this.color !== '' ? this.color : '#666').toString();
const fillColor = (this.color !== '' ? this.color : '#666').toString(); ctx.strokeStyle = fillColor;
ctx.strokeStyle = fillColor; ctx.stroke();
ctx.stroke(); ctx.update();
ctx.update(); rotate += 0.05; // 旋转速度
rotate += 0.05; // 旋转速度 }
}
this.timer = setInterval(() => draw(), interval);
this.timer = setInterval(() => draw(), interval); }
}
}
} }
} </script>
</script>
<style scoped>
<style scoped> .block {
.block { width: 50px;
width: 50px; height: 50px;
height: 50px; }
}
/* #ifdef WEB */
/* #ifdef WEB */ .uni-load__img {
.uni-load__img { width: 24px;
width: 24px; height: 24px;
height: 24px; }
}
.uni-load__img--android-H5 {
.uni-load__img--android-H5 { animation: loading-android-H5-rotate 2s linear infinite;
animation: loading-android-H5-rotate 2s linear infinite; transform-origin: center center;
transform-origin: center center; }
}
.uni-load__img--android-H5 circle {
.uni-load__img--android-H5 circle { display: inline-block;
display: inline-block; animation: loading-android-H5-dash 1.5s ease-in-out infinite;
animation: loading-android-H5-dash 1.5s ease-in-out infinite; stroke: currentColor;
stroke: currentColor; stroke-linecap: round;
stroke-linecap: round; }
}
@keyframes loading-android-H5-rotate {
@keyframes loading-android-H5-rotate { 0% {
0% { transform: rotate(0deg);
transform: rotate(0deg); }
}
100% {
100% { transform: rotate(360deg);
transform: rotate(360deg); }
} }
}
@keyframes loading-android-H5-dash {
@keyframes loading-android-H5-dash { 0% {
0% { stroke-dasharray: 1, 200;
stroke-dasharray: 1, 200; /* stroke-dashoffset: 0; */
/* stroke-dashoffset: 0; */ }
}
50% {
50% { stroke-dasharray: 90, 150;
stroke-dasharray: 90, 150; stroke-dashoffset: -20;
stroke-dashoffset: -20; }
}
100% {
100% { stroke-dasharray: 90, 150;
stroke-dasharray: 90, 150; stroke-dashoffset: -120;
stroke-dashoffset: -120; }
} }
}
/* #endif */
/* #endif */
</style> </style>
<template> <template>
<uni-icons :id="elId" class='load-ani' :style="aniStyle" @transitionend="onEnd" :type="iconType" :size="size" :color="color"></uni-icons> <uni-icons :id="elId" class='load-ani' :style="aniStyle" @transitionend="onEnd" :type="iconType" :size="size" :color="color"></uni-icons>
</template> </template>
<script> <script>
export default { export default {
name:'icon', name:'icon',
props: { props: {
iconType: { iconType: {
type: String, type: String,
default: 'loop' default: 'loop'
}, },
size: { size: {
type: Number, type: Number,
default: 0 default: 0
}, },
color: { color: {
type: String, type: String,
default: '#333' default: '#333'
} }
}, },
data() { data() {
const elId = `Uni_${(Math.random() * 10e5).toInt().toString(36)}` const elId = `Uni_${(Math.random() * 10e5).toInt().toString(36)}`
return { return {
elId: elId, elId: elId,
element: null as UniElement | null, element: null as UniElement | null,
times: 0, times: 0,
aniStyle: '', aniStyle: '',
deg: 3600000 deg: 3600000
} }
}, },
created() { created() {
this.times = 0 this.times = 0
// 需要延迟一些时间,否则动画不生效 // 需要延迟一些时间,否则动画不生效
setTimeout(() => { setTimeout(() => {
this.aniStyle = 'transform:rotate(1deg);' this.aniStyle = 'transform:rotate(1deg);'
}, 300) }, 300)
}, },
mounted() { mounted() {
this.element = uni.getElementById(this.elId as string) this.element = uni.getElementById(this.elId as string)
}, },
methods: { methods: {
onEnd() { onEnd() {
// 因为循环角度是不断增加,在增加10次以后需要重置,防止无限增加下去 // 因为循环角度是不断增加,在增加10次以后需要重置,防止无限增加下去
if (this.times == 10) { if (this.times == 10) {
this.element!.style.setProperty('transform', 'rotate(0deg)') this.element!.style.setProperty('transform', 'rotate(0deg)')
this.element!.style.setProperty('transition-duration', '1') this.element!.style.setProperty('transition-duration', '1')
this.times = 0 this.times = 0
return return
} }
this.times = this.times + 1 this.times = this.times + 1
const rotate = this.times * 360 const rotate = this.times * 360
this.element!.style.setProperty('transform', 'rotate(' + rotate + 'deg)') this.element!.style.setProperty('transform', 'rotate(' + rotate + 'deg)')
this.element!.style.setProperty('transition-duration', '1000') this.element!.style.setProperty('transition-duration', '1000')
} }
} }
} }
</script> </script>
<style> <style>
.load-ani { .load-ani {
transition-property: transform; transition-property: transform;
transition-duration: 0.1s; transition-duration: 0.1s;
transition-timing-function: linear; transition-timing-function: linear;
transform: rotate(0deg); transform: rotate(0deg);
} }
</style> </style>
<template> <template>
<!-- 如果没有插槽,则使用 load-inline 样式 --> <!-- 如果没有插槽,则使用 load-inline 样式 -->
<view class="uni-loading-main" :class="{'load-inline':$slots['default'] == null}"> <view class="uni-loading-main" :class="{'load-inline':$slots['default'] == null}">
<template v-if="loading"> <template v-if="loading">
<slot></slot> <slot></slot>
<template v-if="$slots['default'] == null"> <template v-if="$slots['default'] == null">
<Circle :speed="16" :size="loadWidth" :color="color"></Circle> <Circle :speed="16" :size="loadWidth" :color="color"></Circle>
<text v-if="text" class="inline-text" :style=" { color: color }">{{text}}</text> <text v-if="text" class="inline-text" :style=" { color: color }">{{text}}</text>
</template> </template>
<template v-else> <template v-else>
<view class="uni-loading-mask" :style="{backgroundColor:background}"> <view class="uni-loading-mask" :style="{backgroundColor:background}">
<Circle :speed="16" :size="loadWidth" :color="color"></Circle> <Circle :speed="16" :size="loadWidth" :color="color"></Circle>
<text v-if="text" class="block-text" :style=" { color: color }">{{text}}</text> <text v-if="text" class="block-text" :style=" { color: color }">{{text}}</text>
</view> </view>
</template> </template>
</template> </template>
<template v-else> <template v-else>
<slot></slot> <slot></slot>
</template> </template>
</view> </view>
</template> </template>
<script> <script>
import Circle from './circle.uvue' import Circle from './circle.uvue'
// TODO 性能问题,其他类型暂时不对外开放 // TODO 性能问题,其他类型暂时不对外开放
// import Icon from './icon.uvue' // import Icon from './icon.uvue'
// import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue' // import UniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue'
// import { img_load_base } from './load-img.uts' // import { img_load_base } from './load-img.uts'
/** /**
* Loading-x 加载动画 * Loading-x 加载动画
* @description 用于数据加载场景,使用loading等待数据返回 * @description 用于数据加载场景,使用loading等待数据返回
* @tutorial https://ext.dcloud.net.cn/plugin?name=uni-loading-x * @tutorial https://ext.dcloud.net.cn/plugin?name=uni-loading-x
* @property {Boolean} loading 是否显示加载动画,默认:true * @property {Boolean} loading 是否显示加载动画,默认:true
* @property {String} type = [snow|circle|icon] 加载图标显示,默认:circle * @property {String} type = [snow|circle|icon] 加载图标显示,默认:circle
* @value snow 显示雪花加载动画,性能问题暂时不支持 * @value snow 显示雪花加载动画,性能问题暂时不支持
* @value circle 显示圆形加载动画 * @value circle 显示圆形加载动画
* @value icon 自定义图标 ,暂时不支持 * @value icon 自定义图标 ,暂时不支持
* @property {String} background 加载遮罩颜色,支持 rgba 色值,默认:rgba(255,255,255,0.6) * @property {String} background 加载遮罩颜色,支持 rgba 色值,默认:rgba(255,255,255,0.6)
* @property {String} color 加载图标以及加载文字颜色,默认:#333333 * @property {String} color 加载图标以及加载文字颜色,默认:#333333
* @property {String} size 加载图标大小,默认:20 * @property {String} size 加载图标大小,默认:20
* @property {String} text 加载文本,默认:不显示 * @property {String} text 加载文本,默认:不显示
* @property {String} iconType 自定义图标类型,参考 uni-icons ,当前版本暂不支持 * @property {String} iconType 自定义图标类型,参考 uni-icons ,当前版本暂不支持
*/ */
export default { export default {
name: "uni-loading", name: "uni-loading",
components: { Circle }, components: { Circle },
props: { props: {
loading: { loading: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
type: { type: {
type: String, type: String,
default: '' default: ''
}, },
iconType: { iconType: {
type: String, type: String,
default: 'gear-filled' default: 'gear-filled'
}, },
size: { size: {
type: Number, type: Number,
default: 0 default: 0
}, },
text: { text: {
type: String, type: String,
default: '' default: ''
}, },
background: { background: {
type: String, type: String,
default: 'rgba(255,255,255,0.6)' default: 'rgba(255,255,255,0.6)'
}, },
color: { color: {
type: String, type: String,
default: '#333' default: '#333'
} }
}, },
data() { data() {
return {}; return {};
}, },
computed: { computed: {
loadWidth() : number { loadWidth() : number {
let width = this.size let width = this.size
if (width == 0) { if (width == 0) {
return 20 return 20
} }
return width return width
}, },
styles() : string { styles() : string {
return `width:${this.loadWidth}px;height:${this.loadWidth}px;` return `width:${this.loadWidth}px;height:${this.loadWidth}px;`
} }
}, },
created() {}, created() {},
methods: {} methods: {}
} }
</script> </script>
<style scoped> <style scoped>
.uni-loading-main { .uni-loading-main {
position: relative; position: relative;
} }
.uni-loading-main.load-inline { .uni-loading-main.load-inline {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
} }
.block-text { .block-text {
margin-top: 8px; margin-top: 8px;
font-size: 14px; font-size: 14px;
} }
.inline-text { .inline-text {
margin-left: 8px; margin-left: 8px;
font-size: 14px; font-size: 14px;
} }
.uni-loading-mask { .uni-loading-mask {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0; top: 0;
left: 0; left: 0;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.uni-loading-mask { .uni-loading-mask {
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
z-index: 2; z-index: 2;
} }
.uni-load { .uni-load {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.load-text { .load-text {
font-size: 14px; font-size: 14px;
color: #fff; color: #fff;
margin-top: 12px; margin-top: 12px;
} }
.uni-load .image, .uni-load .image,
.load-image { .load-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.load-ani { .load-ani {
transition-property: transform; transition-property: transform;
transition-duration: 0.1s; transition-duration: 0.1s;
transition-timing-function: linear; transition-timing-function: linear;
transform: rotate(0deg); transform: rotate(0deg);
} }
</style> </style>
/** /**
* hex颜色转rgba * hex颜色转rgba
*/ */
export const hexToRgba = (hex : string, alpha : number) : string => { export const hexToRgba = (hex : string, alpha : number) : string => {
// 去除 # 符号(如果有的话) // 去除 # 符号(如果有的话)
hex = hex.replace('#', ''); hex = hex.replace('#', '');
let hexArray = hex.split(''); let hexArray = hex.split('');
// 检查颜色值长度,如果不符合预期则返回默认值或者抛出错误 // 检查颜色值长度,如果不符合预期则返回默认值或者抛出错误
if (hexArray.length != 3 && hexArray.length != 6) { if (hexArray.length != 3 && hexArray.length != 6) {
// 返回默认值或者抛出错误,这里使用默认值为黑色 // 返回默认值或者抛出错误,这里使用默认值为黑色
return 'rgba(0,0,0,1)'; return 'rgba(0,0,0,1)';
// 或者抛出错误 // 或者抛出错误
// throw new Error('Invalid hex color value'); // throw new Error('Invalid hex color value');
} }
let extendedHex : string[] = []; let extendedHex : string[] = [];
if (hex.length == 3) { if (hex.length == 3) {
for (let i = 0; i < hexArray.length; i++) { for (let i = 0; i < hexArray.length; i++) {
extendedHex.push(hexArray[i]); extendedHex.push(hexArray[i]);
extendedHex.push(hexArray[i]); extendedHex.push(hexArray[i]);
} }
hexArray = extendedHex; hexArray = extendedHex;
} }
hex = '' hex = ''
for (let h = 0; h < hexArray.length; h++) { for (let h = 0; h < hexArray.length; h++) {
hex += hexArray[h] hex += hexArray[h]
} }
// // 拆分颜色值为 R、G、B // // 拆分颜色值为 R、G、B
const r = parseInt(hex.substring(0, 2), 16); const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16); const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16); const b = parseInt(hex.substring(4, 6), 16);
// // 返回 rgba 值 // // 返回 rgba 值
return `rgba(${r},${g},${b},${alpha})`; return `rgba(${r},${g},${b},${alpha})`;
} }
export const easeInOutCubic = (t : number) : number => { export const easeInOutCubic = (t : number) : number => {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
} }
{ {
"id": "uni-loading", "id": "uni-loading",
"displayName": "uni-loading", "displayName": "uni-loading",
"version": "1.0.4", "version": "1.0.5",
"description": "加载动画组件多用在页面内数据加载时,提供一个loading动画,列表的上拉加载,下拉刷新等都需要加载动画", "description": "加载动画组件多用在页面内数据加载时,提供一个loading动画,列表的上拉加载,下拉刷新等都需要加载动画",
"keywords": [ "keywords": [
"loading", "loading",
"加载动画", "加载动画",
"上拉刷新", "上拉刷新",
"下拉加载" "下拉加载"
], ],
"repository": "", "repository": "",
"engines": { "engines": {
"HBuilderX": "^3.97" "HBuilderX": "^3.97"
}, },
"dcloudext": { "dcloudext": {
"type": "component-vue", "type": "component-vue",
"sale": { "sale": {
"regular": { "regular": {
"price": "0.00" "price": "0.00"
}, },
"sourcecode": { "sourcecode": {
"price": "0.00" "price": "0.00"
} }
}, },
"contact": { "contact": {
"qq": "" "qq": ""
}, },
"declaration": { "declaration": {
"ads": "无", "ads": "无",
"data": "无", "data": "无",
"permissions": "无" "permissions": "无"
}, },
"npmurl": "" "npmurl": ""
}, },
"uni_modules": { "uni_modules": {
"dependencies": [ "dependencies": [
"uni-icons" "uni-icons"
], ],
"encrypt": [], "encrypt": [],
"platforms": { "platforms": {
"cloud": { "cloud": {
"tcb": "y", "tcb": "y",
"aliyun": "y", "aliyun": "y",
"alipay": "y" "alipay": "y"
}, },
"client": { "client": {
"Vue": { "Vue": {
"vue2": "n", "vue2": "n",
"vue3": "y" "vue3": "y"
}, },
"App": { "App": {
"app-vue": "n", "app-vue": "n",
"app-nvue": "n", "app-nvue": "n",
"app-uvue": "y" "app-uvue": "y"
}, },
"H5-mobile": { "H5-mobile": {
"Safari": "n", "Safari": "n",
"Android Browser": "n", "Android Browser": "n",
"微信浏览器(Android)": "n", "微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n" "QQ浏览器(Android)": "n"
}, },
"H5-pc": { "H5-pc": {
"Chrome": "n", "Chrome": "n",
"IE": "n", "IE": "n",
"Edge": "n", "Edge": "n",
"Firefox": "n", "Firefox": "n",
"Safari": "n" "Safari": "n"
}, },
"小程序": { "小程序": {
"微信": "n", "微信": "n",
"阿里": "n", "阿里": "n",
"百度": "n", "百度": "n",
"字节跳动": "n", "字节跳动": "n",
"QQ": "n", "QQ": "n",
"钉钉": "n", "钉钉": "n",
"快手": "n", "快手": "n",
"飞书": "n", "飞书": "n",
"京东": "n" "京东": "n"
}, },
"快应用": { "快应用": {
"华为": "n", "华为": "n",
"联盟": "n" "联盟": "n"
} }
} }
} }
} }
} }
\ No newline at end of file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<!-- 修改加载图标大小 --> <!-- 修改加载图标大小 -->
<uni-loading :size="30"></uni-loading> <uni-loading :size="30"></uni-loading>
<!-- 修改加载图标类型 : 当前只支持 circle--> <!-- 修改加载图标类型 : 当前只支持 circle-->
<uni-loading type="circle"></uni-loading> <uni-loading type="circle"></uni-loading>
``` ```
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册