This commit is contained in:
haomingming
2023-03-06 17:57:39 +08:00
parent 3794faceae
commit 1066e37c96
4230 changed files with 193453 additions and 0 deletions
@@ -0,0 +1,24 @@
:: BASE_DOC ::
## API
### CountDown Props
name | type | default | description | required
-- | -- | -- | -- | --
auto-start | Boolean | true | \- | N
content | String / Slot | 'default' | \- | N
custom-style `v0.25.0` | String | - | \- | N
format | String | HH:mm:ss | \- | N
millisecond | Boolean | false | \- | N
size | String `v0.5.1` | 'small' | optionssmall/medium/large | N
split-with-unit `v0.5.1` | Boolean | false | \- | N
theme | String `v0.5.1` | 'default' | optionsdefault/round/square | N
time | Number | - | required | Y
### CountDown Events
name | params | description
-- | -- | --
change | `(time: TimeData)` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/count-down/type.ts)。<br/>`interface TimeData { days: number; hours: number; minutes: number; seconds: number; milliseconds: number }`<br/>
finish | \- | \-
@@ -0,0 +1,50 @@
---
title: CountDown 倒计时
description: 用于实时展示倒计时数值。
spline: data
isComponent: true
---
<span class="coverages-badge" style="margin-right: 10px"><img src="https://img.shields.io/badge/coverages%3A%20lines-99%25-blue" /></span><span class="coverages-badge" style="margin-right: 10px"><img src="https://img.shields.io/badge/coverages%3A%20functions-100%25-blue" /></span><span class="coverages-badge" style="margin-right: 10px"><img src="https://img.shields.io/badge/coverages%3A%20statements-99%25-blue" /></span><span class="coverages-badge" style="margin-right: 10px"><img src="https://img.shields.io/badge/coverages%3A%20branches-85%25-blue" /></span>
## 引入
全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。
```json
"usingComponents": {
"t-count-down": "tdesign-miniprogram/count-down/count-down"
}
```
## 代码演示
### 基础倒计时
{{ base }}
### 调整尺寸
{{ size }}
## API
### CountDown Props
名称 | 类型 | 默认值 | 说明 | 必传
-- | -- | -- | -- | --
auto-start | Boolean | true | 是否自动开始倒计时 | N
content | String / Slot | 'default' | 最终倒计时的展示内容,值为'default'时使用默认的格式,否则使用自定义样式插槽 | N
custom-style `v0.25.0` | String | - | 自定义组件样式 | N
format | String | HH:mm:ss | 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 | N
millisecond | Boolean | false | 是否开启毫秒级渲染 | N
size | String `v0.5.1` | 'medium' | 倒计时尺寸。可选项:small/medium/large | N
split-with-unit `v0.5.1` | Boolean | false | 使用时间单位分割 | N
theme | String `v0.5.1` | 'default' | 倒计时风格。可选项:default/round/square/hightlight | N
time | Number | - | 必需。倒计时时长,单位毫秒 | Y
external-classes | Array | - | 组件类名,分别用于设置 组件外层类名、计时器类型、分隔线类名 等。`['t-class', 't-class-count', 't-class-split']` | N
### CountDown Events
名称 | 参数 | 描述
-- | -- | --
change | `(time: TimeData)` | 时间变化时触发。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/count-down/type.ts)。<br/>`interface TimeData { days: number; hours: number; minutes: number; seconds: number; milliseconds: number }`<br/>
finish | \- | 倒计时结束时触发
@@ -0,0 +1,33 @@
import { SuperComponent } from '../common/src/index';
export default class CountDown extends SuperComponent {
externalClasses: string[];
properties: import("./type").TdCountDownProps;
observers: {
time(): void;
};
data: {
prefix: string;
classPrefix: string;
timeDataUnit: {
DD: string;
HH: string;
mm: string;
ss: string;
SSS: string;
};
timeData: import("./utils").TimeData;
formattedTime: string;
};
timeoutId: null | number;
lifetimes: {
detached(): void;
};
methods: {
start(): void;
pause(): void;
reset(): void;
getTime(): number;
updateTime(remain: number): void;
doCount(): void;
};
}
@@ -0,0 +1,100 @@
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { SuperComponent, wxComponent } from '../common/src/index';
import config from '../common/config';
import props from './props';
import { isSameSecond, parseFormat, parseTimeData, TimeDataUnit } from './utils';
const { prefix } = config;
const name = `${prefix}-count-down`;
let CountDown = class CountDown extends SuperComponent {
constructor() {
super(...arguments);
this.externalClasses = [`${prefix}-class`, `${prefix}-class-count`, `${prefix}-class-split`];
this.properties = props;
this.observers = {
time() {
this.reset();
},
};
this.data = {
prefix,
classPrefix: name,
timeDataUnit: TimeDataUnit,
timeData: parseTimeData(0),
formattedTime: '0',
};
this.timeoutId = null;
this.lifetimes = {
detached() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
},
};
this.methods = {
start() {
if (this.counting) {
return;
}
this.counting = true;
this.endTime = Date.now() + this.remain;
this.doCount();
},
pause() {
this.counting = false;
this.timeoutId && clearTimeout(this.timeoutId);
},
reset() {
this.pause();
this.remain = this.properties.time;
this.updateTime(this.remain);
if (this.properties.autoStart) {
this.start();
}
},
getTime() {
return Math.max(this.endTime - Date.now(), 0);
},
updateTime(remain) {
const { format } = this.properties;
this.remain = remain;
const timeData = parseTimeData(remain);
this.triggerEvent('change', timeData);
const { timeText } = parseFormat(remain, format);
const timeRange = format.split(':');
this.setData({
timeRange,
timeData,
formattedTime: timeText.replace(/:/g, ' : '),
});
if (remain === 0) {
this.pause();
this.triggerEvent('finish');
}
},
doCount() {
this.timeoutId = setTimeout(() => {
const time = this.getTime();
if (this.properties.millisecond) {
this.updateTime(time);
}
else if (!isSameSecond(time, this.remain) || time === 0) {
this.updateTime(time);
}
if (time !== 0) {
this.doCount();
}
}, 33);
},
};
}
};
CountDown = __decorate([
wxComponent()
], CountDown);
export default CountDown;
@@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-icon": "../icon/icon"
}
}
@@ -0,0 +1,20 @@
<wxs module="_"> module.exports.format = function(num) { return num < 10 ? '0' + num : num; } </wxs>
<view
style="{{ customStyle }}"
class="{{classPrefix}} {{classPrefix}}--{{theme}} {{classPrefix}}--{{size}} {{prefix}}-class "
aria-role="option"
>
<slot wx:if="{{content !== 'default'}}" />
<block wx:elif="{{theme == 'default'}}">{{formattedTime}}</block>
<block wx:else>
<block wx:for="{{timeRange}}" wx:key="index">
<text class="{{classPrefix}}__item {{prefix}}-class-count">{{_.format(timeData[timeRange[index]])}}</text>
<text
wx:if="{{ splitWithUnit || timeRange.length - 1 !== index}}"
class="{{classPrefix}}__split {{classPrefix}}__split--{{splitWithUnit ? 'text' : 'dot'}} {{prefix}}-class-split"
>{{splitWithUnit ? timeDataUnit[timeRange[index]] : ':'}}</text
>
</block>
</block>
</view>
@@ -0,0 +1,156 @@
.t-float-left {
float: left;
}
.t-float-right {
float: right;
}
@keyframes tdesign-fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.hotspot-expanded.relative {
position: relative;
}
.hotspot-expanded::after {
content: '';
display: block;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
transform: scale(1.5);
}
.t-count-down--small.t-count-down--default {
font-size: var(--td-font-size-base, 28rpx);
}
.t-count-down--small.t-count-down--round > .t-count-down__item {
font-size: var(--td-font-size-s, 24rpx);
}
.t-count-down--small.t-count-down--square > .t-count-down__item {
font-size: var(--td-font-size-s, 24rpx);
}
.t-count-down--small.t-count-down--hightlight > .t-count-down__item {
font-size: var(--td-font-size-m, 32rpx);
}
.t-count-down--small.t-count-down--round > .t-count-down__item,
.t-count-down--small.t-count-down--square > .t-count-down__item {
width: 40rpx;
height: 40rpx;
}
.t-count-down--small.t-count-down--round > .t-count-down__split--dot,
.t-count-down--small.t-count-down--square > .t-count-down__split--dot,
.t-count-down--small.t-count-down--hightlight > .t-count-down__split--dot {
margin: 0 calc(var(--td-spacer, 16rpx) * 0.25);
font-size: var(--td-font-size-base, 28rpx);
font-weight: 700;
}
.t-count-down--small.t-count-down--round > .t-count-down__split--text,
.t-count-down--small.t-count-down--square > .t-count-down__split--text,
.t-count-down--small.t-count-down--hightlight > .t-count-down__split--text {
margin: 0 calc(var(--td-spacer, 16rpx) * 0.5);
font-size: var(--td-font-size, 20rpx);
}
.t-count-down--medium.t-count-down--default {
font-size: var(--td-font-size-m, 32rpx);
}
.t-count-down--medium.t-count-down--round > .t-count-down__item {
font-size: var(--td-font-size-base, 28rpx);
}
.t-count-down--medium.t-count-down--square > .t-count-down__item {
font-size: var(--td-font-size-base, 28rpx);
}
.t-count-down--medium.t-count-down--hightlight > .t-count-down__item {
font-size: 36rpx;
}
.t-count-down--medium.t-count-down--round > .t-count-down__item,
.t-count-down--medium.t-count-down--square > .t-count-down__item {
width: 48rpx;
height: 48rpx;
}
.t-count-down--medium.t-count-down--round > .t-count-down__split--dot,
.t-count-down--medium.t-count-down--square > .t-count-down__split--dot,
.t-count-down--medium.t-count-down--hightlight > .t-count-down__split--dot {
margin: 0 calc(var(--td-spacer, 16rpx) * 0.375);
font-size: var(--td-font-size-m, 32rpx);
font-weight: 700;
}
.t-count-down--medium.t-count-down--round > .t-count-down__split--text,
.t-count-down--medium.t-count-down--square > .t-count-down__split--text,
.t-count-down--medium.t-count-down--hightlight > .t-count-down__split--text {
margin: 0 calc(var(--td-spacer, 16rpx) * 0.625);
font-size: var(--td-font-size-s, 24rpx);
}
.t-count-down--large.t-count-down--default {
font-size: 36rpx;
}
.t-count-down--large.t-count-down--round > .t-count-down__item {
font-size: var(--td-font-size-m, 32rpx);
}
.t-count-down--large.t-count-down--square > .t-count-down__item {
font-size: var(--td-font-size-m, 32rpx);
}
.t-count-down--large.t-count-down--hightlight > .t-count-down__item {
font-size: 36rpx;
}
.t-count-down--large.t-count-down--round > .t-count-down__item,
.t-count-down--large.t-count-down--square > .t-count-down__item {
width: 56rpx;
height: 56rpx;
}
.t-count-down--large.t-count-down--round > .t-count-down__split--dot,
.t-count-down--large.t-count-down--square > .t-count-down__split--dot,
.t-count-down--large.t-count-down--hightlight > .t-count-down__split--dot {
margin: 0 calc(var(--td-spacer, 16rpx) * 0.75);
font-size: 36rpx;
font-weight: 700;
}
.t-count-down--large.t-count-down--round > .t-count-down__split--text,
.t-count-down--large.t-count-down--square > .t-count-down__split--text,
.t-count-down--large.t-count-down--hightlight > .t-count-down__split--text {
margin: 0 calc(var(--td-spacer, 16rpx) * 0.75);
font-size: var(--td-font-size-base, 28rpx);
}
.t-count-down {
font-family: 'DIN Alternate', 'Courier New', Courier, monospace;
}
.t-count-down .t-count-down__item,
.t-count-down .t-count-down__split {
display: inline-flex;
align-items: center;
justify-content: center;
}
.t-count-down--square > .t-count-down__split--dot,
.t-count-down--round > .t-count-down__split--dot,
.t-count-down--hightlight > .t-count-down__split--dot {
color: var(--td-error-color-6, #e34d59);
}
.t-count-down--square > .t-count-down__split--text,
.t-count-down--round > .t-count-down__split--text,
.t-count-down--hightlight > .t-count-down__split--text {
color: var(--td-font-gray-1, rgba(0, 0, 0, 0.9));
}
.t-count-down--default {
color: var(--td-countdown-default-color, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)));
}
.t-count-down--square {
color: var(--td-countdown-round-color, var(--td-font-white-1, #ffffff));
}
.t-count-down--square > .t-count-down__item {
border-radius: var(--td-countdown-square-border-radius, var(--td-radius-small, 6rpx));
background: var(--td-countdown-bg-color, var(--td-error-color-6, #e34d59));
}
.t-count-down--round {
color: var(--td-countdown-round-color, var(--td-font-white-1, #ffffff));
}
.t-count-down--round > .t-count-down__item {
border-radius: var(--td-countdown-round-border-radius, var(--td-radius-circle, 50%));
background: var(--td-countdown-bg-color, var(--td-error-color-6, #e34d59));
}
.t-count-down--hightlight {
color: var(--td-countdown-hightlight-color, var(--td-error-color-6, #e34d59));
}
@@ -0,0 +1,3 @@
import { TdCountDownProps } from './type';
declare const props: TdCountDownProps;
export default props;
@@ -0,0 +1,38 @@
const props = {
autoStart: {
type: Boolean,
value: true,
},
content: {
type: String,
value: 'default',
},
customStyle: {
type: String,
value: '',
},
format: {
type: String,
value: 'HH:mm:ss',
},
millisecond: {
type: Boolean,
value: false,
},
size: {
type: String,
value: 'medium',
},
splitWithUnit: {
type: Boolean,
value: false,
},
theme: {
type: String,
value: 'default',
},
time: {
type: Number,
},
};
export default props;
@@ -0,0 +1,38 @@
export interface TdCountDownProps {
autoStart?: {
type: BooleanConstructor;
value?: boolean;
};
content?: {
type: StringConstructor;
value?: string;
};
customStyle?: {
type: StringConstructor;
value?: string;
};
format?: {
type: StringConstructor;
value?: string;
};
millisecond?: {
type: BooleanConstructor;
value?: boolean;
};
size?: {
type: StringConstructor;
value?: 'small' | 'medium' | 'large';
};
splitWithUnit?: {
type: BooleanConstructor;
value?: boolean;
};
theme?: {
type: StringConstructor;
value?: 'default' | 'round' | 'square' | 'hightlight';
};
time: {
type: NumberConstructor;
value?: number;
};
}
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,25 @@
export interface TimeData {
DD: number;
HH: number;
mm: number;
ss: number;
SSS: number;
}
export declare const TimeDataUnit: {
DD: string;
HH: string;
mm: string;
ss: string;
SSS: string;
};
export declare const parseTimeData: (time: number) => TimeData;
export declare const isSameSecond: (time1: number, time2: number) => boolean;
export declare type TTimeList = {
digit: string;
unit: string;
match: string;
}[];
export declare const parseFormat: (time: number, format: string) => {
timeText: string;
timeList: TTimeList;
};
@@ -0,0 +1,61 @@
export const TimeDataUnit = {
DD: '天',
HH: '时',
mm: '分',
ss: '秒',
SSS: '毫秒',
};
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
export const parseTimeData = function (time) {
const days = Math.floor(time / DAY);
const hours = Math.floor((time % DAY) / HOUR);
const minutes = Math.floor((time % HOUR) / MINUTE);
const seconds = Math.floor((time % MINUTE) / SECOND);
const milliseconds = Math.floor(time % SECOND);
return {
DD: days,
HH: hours,
mm: minutes,
ss: seconds,
SSS: milliseconds,
};
};
export const isSameSecond = function (time1, time2) {
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
};
export const parseFormat = function (time, format) {
const obj = {
'D+': Math.floor(time / 86400000),
'H+': Math.floor((time % 86400000) / 3600000),
'm+': Math.floor((time % 3600000) / 60000),
's+': Math.floor((time % 60000) / 1000),
'S+': Math.floor(time % 1000),
};
const timeList = [];
let timeText = format;
Object.keys(obj).forEach((prop) => {
if (new RegExp(`(${prop})`).test(timeText)) {
timeText = timeText.replace(RegExp.$1, (match, offset, source) => {
const v = `${obj[prop]}`;
let digit = v;
if (match.length > 1) {
digit = (match.replace(new RegExp(match[0], 'g'), '0') + v).substr(v.length);
}
const unit = source.substr(offset + match.length);
const last = timeList[timeList.length - 1];
if (last) {
const index = last.unit.indexOf(match);
if (index !== -1) {
last.unit = last.unit.substr(0, index);
}
}
timeList.push({ digit, unit, match });
return digit;
});
}
});
return { timeText, timeList };
};