Skip to content

Commit

Permalink
chore: add playground demo page
Browse files Browse the repository at this point in the history
  • Loading branch information
byronogis committed Dec 19, 2024
1 parent 0d9e9db commit 9c17457
Show file tree
Hide file tree
Showing 8 changed files with 453 additions and 28 deletions.
53 changes: 53 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Deploy playground demo # 工作流的名称

on: # 触发条件
push:
branches:
- main # 当 main 分支收到推送时触发
workflow_dispatch: # 允许手动触发

permissions: # GitHub token 的权限设置
contents: read # 读取仓库内容
pages: write # 写入 GitHub Pages
id-token: write # 写入身份令牌

concurrency: # 并发控制
group: pages # 同一时间只允许一个部署任务运行
cancel-in-progress: true # 如果有新的部署,取消正在进行的部署

jobs:
deploy:
environment: # 环境配置
name: github-pages # 环境名称
url: ${{ steps.deployment.outputs.page_url }} # 部署后的 URL

runs-on: ubuntu-latest # 运行环境

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: pnpm

- run: pnpm playground:build

# 添加缓存验证步骤
- name: Verify build
run: |
if [ ! -d "playground/dist" ]; then
echo "Playground build failed - dist directory not found"
exit 1
fi
- uses: actions/configure-pages@v5
with:
enablement: true
- uses: actions/upload-pages-artifact@v3
with:
path: playground/dist # 指定要部署的目录
- uses: actions/deploy-pages@v4
id: deployment
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const timer = pausableTimers(() => {
timer.pause()
timer.resume()
timer.clear()
timer.reset()
timer.restart()
timer.isPaused()
timers.getRemainingTime()
timer.isCompleted() // only for timeout mode
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
"scripts": {
"build": "unbuild",
"dev": "unbuild --stub",
"playground:dev": "vite playground",
"playground:build": "vite build playground",
"playground:preview": "vite preview playground",
"lint": "eslint .",
"prepublishOnly": "automd && nr build",
"release": "bumpp && pnpm publish",
Expand Down
50 changes: 50 additions & 0 deletions playground/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- GitHub Corner -->
<a href="https://github.com/byronogis/pausable-timers" class="github-corner" aria-label="View source on GitHub">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--primary); color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
</svg>
</a>
<div class="container">
<div class="project-info">
<h1></h1>
<span class="version"></span>
</div>
<div class="timer-display">
<div class="progress-container">
<div class="progress-bar"></div>
</div>
<div class="status-text"></div>
</div>

<div class="control-panel">
<div class="input-group">
<input type="number" id="timeInput" value="5000" min="100" step="100">
<label>ms</label>
<select id="modeSelect">
<option value="timeout">Timeout</option>
<option value="interval">Interval</option>
</select>
</div>

<div class="button-group">
<button id="startBtn">Start</button>
<button id="pauseBtn" disabled>Pause</button>
<button id="clearBtn" disabled>Clear</button>
<button id="restartBtn" disabled>Restart</button>
</div>
</div>
</div>
<script type="module" src="./main.js"></script>
</body>
</html>
27 changes: 0 additions & 27 deletions playground/index.ts

This file was deleted.

180 changes: 180 additions & 0 deletions playground/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import pkg from '../package.json'
import { pausableTimers } from '../src/index'

// 设置页面标题和项目信息
document.title = `${pkg.name} | Playground`
document.querySelector('.project-info h1').textContent = pkg.name
document.querySelector('.version').textContent = `v${pkg.version}`

const elements = {
progressBar: document.querySelector('.progress-bar'),
statusText: document.querySelector('.status-text'),
timeInput: document.querySelector('#timeInput'),
modeSelect: document.querySelector('#modeSelect'),
startBtn: document.querySelector('#startBtn'),
pauseBtn: document.querySelector('#pauseBtn'),
clearBtn: document.querySelector('#clearBtn'),
restartBtn: document.querySelector('#restartBtn'),
}

let timer = null
let animationFrameId = null
let cycleCount = 0

function updateProgress(remaining, total) {
const progress = (remaining / total) * 100
const progressBar = elements.progressBar

// 完全移除过渡效果
progressBar.style.transition = 'none'
progressBar.style.width = `${progress}%`

if (remaining === total) {
// 当需要重置到 100% 时,保持 none
return
}

// 确保 DOM 更新后再添加过渡效果
requestAnimationFrame(() => {
progressBar.style.transition = 'width 0.1s linear'
})
}

function updateButtons(isRunning) {
elements.startBtn.disabled = isRunning
elements.pauseBtn.disabled = !isRunning
elements.clearBtn.disabled = !isRunning
elements.restartBtn.disabled = !isRunning
elements.timeInput.disabled = isRunning
elements.modeSelect.disabled = isRunning
}

function animate(total) {
if (!timer)
return

const remaining = timer.getRemainingTime()
updateProgress(remaining, total)

// 根据模式显示不同的状态信息
if (elements.modeSelect.value === 'interval') {
elements.statusText.textContent = `Cycle ${cycleCount} - Remaining: ${remaining}ms`
}
else {
elements.statusText.textContent = `Remaining: ${remaining}ms`
}

if (timer.isCompleted()) {
elements.statusText.textContent = 'Completed!'
updateButtons(false)
return
}

animationFrameId = requestAnimationFrame(() => animate(total))
}

function startTimer() {
const delay = Number.parseInt(elements.timeInput.value)
// 添加输入值检查
if (delay < 100) {
elements.statusText.textContent = 'Please enter a value greater than 100ms'
return
}

const mode = elements.modeSelect.value

// 先清理现有的计时器和动画
if (timer) {
timer.clear()
}
if (animationFrameId) {
cancelAnimationFrame(animationFrameId)
animationFrameId = null
}

elements.statusText.textContent = ''
timer = pausableTimers(() => {
if (mode === 'timeout') {
cancelAnimationFrame(animationFrameId)
updateProgress(0, delay)
elements.statusText.textContent = 'Completed!'
updateButtons(false)
}
else {
// 在 interval 模式下,先取消动画帧
cancelAnimationFrame(animationFrameId)
// 立即重置进度条到 100%(无动画)
updateProgress(delay, delay)
// 在 interval 模式下添加循环次数显示
cycleCount++
elements.statusText.textContent = `Cycle ${cycleCount}`
// 立即开始新的倒计时(有动画)
requestAnimationFrame(() => animate(delay))
}
}, delay, { mode })

updateButtons(true)
// 启动时也应用相同的逻辑
updateProgress(delay, delay)
requestAnimationFrame(() => animate(delay))
}

// Event Listeners
elements.startBtn.addEventListener('click', startTimer)

elements.pauseBtn.addEventListener('click', () => {
if (!timer)
return
if (timer.isPaused()) {
timer.resume()
elements.pauseBtn.textContent = 'Pause'
animate(Number.parseInt(elements.timeInput.value))
}
else {
timer.pause()
elements.pauseBtn.textContent = 'Resume'
cancelAnimationFrame(animationFrameId)
}
})

elements.clearBtn.addEventListener('click', () => {
if (!timer)
return
timer.clear()
// 确保动画帧被取消
if (animationFrameId) {
cancelAnimationFrame(animationFrameId)
animationFrameId = null
}
updateProgress(100, 100)
elements.statusText.textContent = ''
updateButtons(false)
elements.pauseBtn.textContent = 'Pause'
cycleCount = 0
})

elements.restartBtn.addEventListener('click', () => {
if (!timer)
return
// 先清理现有的动画帧
if (animationFrameId) {
cancelAnimationFrame(animationFrameId)
animationFrameId = null
}
timer.restart()
elements.pauseBtn.textContent = 'Pause'
updateProgress(Number.parseInt(elements.timeInput.value), Number.parseInt(elements.timeInput.value))
animate(Number.parseInt(elements.timeInput.value))
cycleCount = 0
})

// 添加输入框验证
elements.timeInput.addEventListener('input', (e) => {
const value = Number.parseInt(e.target.value)
if (value < 100) {
e.target.setCustomValidity('Please enter a value greater than 100ms')
}
else {
e.target.setCustomValidity('')
}
})
Loading

0 comments on commit 9c17457

Please sign in to comment.