Skip to content

Commit

Permalink
feat: countdown
Browse files Browse the repository at this point in the history
  • Loading branch information
BeADre committed Mar 8, 2021
1 parent 8015120 commit f3fd177
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 34 deletions.
7 changes: 7 additions & 0 deletions packages/varlet-cli/site/site.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,13 @@
"en-US": "Steps"
},
"doc": "steps"
},
{
"text": {
"zh-CN": "Countdown 倒计时",
"en-US": "Countdown"
},
"doc": "countdown"
}
]
},
Expand Down
152 changes: 152 additions & 0 deletions packages/varlet-ui/src/countdown/Countdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<template>
<div class="var-countdown">
<slot v-bind="timeData">
{{ showTime }}
</slot>
</div>
</template>

<script lang="ts">
import { defineComponent, Ref, ref, watch } from 'vue'
import dayjs, { UnitTypeLongPlural } from 'dayjs'
import duration, { Duration } from 'dayjs/plugin/duration'
import { props } from './props'
import { requestAnimationFrame, cancelAnimationFrame } from '../utils/elements'
import { toNumber } from '../utils/shared'
dayjs.extend(duration)
type Format = {
DD: Duration['asDays']
HH: Duration['asHours']
mm: Duration['asMinutes']
ss: Duration['asSeconds']
}
type TimeData = Exclude<UnitTypeLongPlural, 'months' | 'years' | 'dates'>
export default defineComponent({
name: 'VarCountdown',
props,
setup(props) {
const endTime: Ref<number> = ref(0)
const isStart: Ref<boolean> = ref(false)
const showTime: Ref<string> = ref('')
const handle: Ref<number> = ref(0)
const pauseTime: Ref<number> = ref(0)
const timeData: Ref<Record<TimeData, number>> = ref({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
milliseconds: 0,
})
const isSameTime = (durationTime: number, newFormat: string): boolean => {
if (!showTime.value) return true
return showTime.value === dayjs.duration(durationTime).format(newFormat)
}
const replaceValue = (milliseconds: number, fixedLength: number): string => {
const len = `${milliseconds}`.length
if (fixedLength === 3) {
return len === 1 ? `00${milliseconds}` : len === 2 ? `0${milliseconds}` : `${milliseconds}`
}
const sliceMilliseconds = `${milliseconds}`.slice(0, 2)
return len === 1 ? `0${sliceMilliseconds}` : `${sliceMilliseconds}`
}
const formatMilliseconds = (milliseconds: number, newFormat: string): string => {
let time = newFormat
if (time.includes('SSS')) time = time.replaceAll('SSS', replaceValue(milliseconds, 3))
if (time.includes('SS')) time = (time || time).replaceAll('SS', replaceValue(milliseconds, 2))
if (time.includes('S')) time = (time || time).replaceAll('S', `${milliseconds}`.slice(0, 1))
return time
}
const formatTime = (type: keyof Format, durationTime: number) => {
const duration = dayjs.duration(durationTime)
const format: Format = {
DD: duration.asDays,
HH: duration.asHours,
mm: duration.asMinutes,
ss: duration.asSeconds,
}
const millis = duration.milliseconds()
const method = format[type].bind(duration)
let roundTime = `${Math.floor(method())}`
roundTime = +roundTime < 10 ? `0${roundTime}` : roundTime
let newFormat = props.format.replaceAll(type, roundTime)
newFormat = formatMilliseconds(millis, newFormat)
if (!isSameTime(durationTime, newFormat)) props.onChange?.()
timeData.value = {
days: duration.days(),
hours: duration.hours(),
minutes: duration.minutes(),
seconds: duration.seconds(),
milliseconds: duration.milliseconds(),
}
showTime.value = duration.format(newFormat)
}
const countdown = () => {
const { format, time, onEnd, autoStart } = props
const now = Date.now()
if (!endTime.value) endTime.value = now + toNumber(time)
let durationTime = endTime.value - now
if (durationTime < 0) durationTime = 0
pauseTime.value = durationTime
if (format.includes('DD')) formatTime('DD', durationTime)
else if (format.includes('HH')) formatTime('HH', durationTime)
else if (format.includes('mm')) formatTime('mm', durationTime)
else if (format.includes('ss')) formatTime('ss', durationTime)
else showTime.value = `${durationTime}`
if (durationTime === 0) {
onEnd?.()
return
}
if (autoStart || isStart.value) handle.value = requestAnimationFrame(countdown)
}
// expose
const start = () => {
if (isStart.value) return
isStart.value = true
endTime.value = Date.now() + (pauseTime.value || toNumber(props.time))
countdown()
}
// expose
const pause = () => {
isStart.value = false
}
// expose
const reset = () => {
endTime.value = 0
isStart.value = false
cancelAnimationFrame(handle.value)
countdown()
}
watch(
() => props.time,
() => reset(),
{ immediate: true }
)
return {
showTime,
timeData,
start,
pause,
reset,
}
},
})
</script>
7 changes: 7 additions & 0 deletions packages/varlet-ui/src/countdown/__tests__/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import example from '../example'
import { render } from '@testing-library/vue'

test('test countdown example', async () => {
const wrapper = render(example)
console.log(wrapper)
})
Empty file.
70 changes: 70 additions & 0 deletions packages/varlet-ui/src/countdown/docs/zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Countdown 倒计时

### 介绍

用于实时展示倒计时数值,支持毫秒精度。

### 引入

```js
import { Countdown } from '@varlet/ui'

export default defineComponent({
components: {
[Countdown.name]: Countdown
}
})
```

### 基本使用

`time` 属性表示倒计时总时长,单位为毫秒。

```html
<var-countdown :time="time" />
```

```js
import { defineComponent, ref } from 'vue'
import { Countdown } from '@varlet/ui'

export default defineComponent({
components: {
[Countdown.name]: Countdown
},
setup() {
const time = ref(30 * 60 * 60 * 1000)

return {
time
}
}
})
```
### 自定义格式

通过 `format` 属性设置倒计时文本的内容。

```html
<var-countdown :time="time" format="DD 天 HH 时 mm 分 ss 秒" />
```

### 显示毫秒

通过 `S` 文本显示毫秒。

```html
<var-countdown :time="time" format="HH:mm:ss:SS" />
```

### 自定义样式

通过插槽自定义倒计时的样式,`timeData` 对象格式见下方表格。。

```html
<van-count-down :time="time">
<template #default="timeData">

</template>
</van-count-down>
```
87 changes: 87 additions & 0 deletions packages/varlet-ui/src/countdown/example/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<template>
<div>
<app-type>基本使用</app-type>
<var-countdown time="108000000" />
</div>
<div>
<app-type>自定义格式</app-type>
<var-countdown time="108000000" format="DD 天 HH 时 mm 分 ss 秒" />
</div>
<div>
<app-type>显示毫秒</app-type>
<var-countdown time="108000000" format="HH : mm : ss : SS" />
</div>
<div>
<app-type>自定义样式</app-type>
<var-countdown time="108000000">
<template #default="timeData">
<span class="block">{{ timeData.hours }}</span>
<span class="colon">:</span>
<span class="block">{{ timeData.minutes }}</span>
<span class="colon">:</span>
<span class="block">{{ timeData.seconds }}</span>
</template>
</var-countdown>
</div>
<div>
<app-type>手动控制</app-type>
<var-countdown :time="time" ref="countdown" :auto-start="false" format="ss : SSS" @end="end" @change="change" />
<div style="display: flex; justify-content: space-between; margin-top: 10px">
<var-button type="primary" @click="$refs.countdown.start()">开始</var-button>
<var-button @click="$refs.countdown.pause()">暂停</var-button>
<var-button @click="$refs.countdown.reset()">重置</var-button>
</div>
</div>
</template>

<script>
import { defineComponent, ref } from 'vue'
import Countdown from '..'
import Snackbar from '../../snackbar'
import Button from '../../button'
export default defineComponent({
name: 'CountdownExample',
components: {
[Countdown.name]: Countdown,
[Button.name]: Button,
},
setup() {
const countdown = ref(null)
const time = ref(3000)
const end = () => {
Snackbar.info('end!')
}
const change = () => {
console.log('change')
}
return {
time,
end,
countdown,
change,
}
},
})
</script>

<style scoped>
.block {
background: #2e67ba;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
}
.colon {
margin: 0 5px;
font-size: 18px;
font-weight: 500;
}
</style>
8 changes: 8 additions & 0 deletions packages/varlet-ui/src/countdown/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { App } from 'vue'
import Countdown from './Countdown.vue'

Countdown.install = function (app: App) {
app.component(Countdown.name, Countdown)
}

export default Countdown
20 changes: 20 additions & 0 deletions packages/varlet-ui/src/countdown/props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const props = {
time: {
type: [String, Number],
default: 0,
},
format: {
type: String,
default: 'HH:mm:ss',
},
autoStart: {
type: Boolean,
default: true,
},
onEnd: {
type: Function,
},
onChange: {
type: Function,
},
}
4 changes: 2 additions & 2 deletions packages/varlet-ui/src/progress/docs/zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { Progress } from '@varlet/ui'

export default defineComponent({
components: {
[Progress.name]: Progress,
},
[Progress.name]: Progress
}
})
```

Expand Down
Loading

0 comments on commit f3fd177

Please sign in to comment.