Canvas制作滚动的数字时钟
2022-05-08 本文已影响0人
ohityes
这是一款滚动的数字时钟,使用 canvas 制作,采用 12 小时制,右上角显示 AM 或 PM,时钟以秒为单位,从上往下滚动。
查看效果:滚动的数字时钟演示
制作过程
1、HTML
由于是把时钟绘制到 canvas 里,所以 html 机构就比较简单了。另外,还使用到了 lightning.js,所以 html 结构如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>滚动的数字时钟演示_dowebok</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="app"></div>
<script src="lightning.min.js"></script>
<script src="script.js"></script>
</body>
</html>
2、CSS
CSS 也很简单,代码如下:
html,
body {
padding: 0;
margin: 0;
font-size: 16px;
background: #000;
}
body {
font-family: 'peacock';
}
canvas {
display: block;
object-fit: contain;
width: 100vw;
height: 100vh;
}
3、Javascript
因为是用 canvas 绘制数字时钟,所以主要的代码都有 JS 实现:
const width = window.innerWidth < 450 ? 450 : window.innerWidth
const height = window.innerHeight
const lineHeight = 80
const commonFont = {
fontFace: 'peacock',
fontSize: lineHeight,
lineHeight: lineHeight,
textColor: 0xffffffff
}
class App extends lng.Application {
static _template() {
return {
TimeWrapper: {
flex: { direction: 'row' },
mount: 0.5,
x: width * 0.5,
y: height * 0.5,
Hours: {
w: 100,
children: [{ type: ChangeValue }, { type: ChangeValue, x: 50 }]
},
ColonOne: {
text: Object.assign({ text: ':' }, commonFont)
},
Minutes: {
w: 100,
children: [{ type: ChangeValue }, { type: ChangeValue, x: 50 }]
},
ColonTwo: {
text: Object.assign({ text: ':' }, commonFont)
},
Seconds: {
w: 100,
children: [{ type: ChangeValue }, { type: ChangeValue, x: 50 }]
},
AmPm: {
w: 61,
x: 10,
y: 10,
type: ChangeValue,
fontSize: 40,
lineHeight: 40
}
}
}
}
setTime(lastTime) {
const currentDate = new Date()
currentDate.setSeconds(currentDate.getSeconds() - 1)
if (!lastTime || currentDate.toString() !== lastTime.toString()) {
const nextDate = new Date()
this.setCurrentNext(
'Hours',
this.set12Hours(currentDate.getHours()),
this.set12Hours(nextDate.getHours()),
false
)
this.setCurrentNext('Minutes', currentDate.getMinutes(), nextDate.getMinutes())
this.setCurrentNext('Seconds', currentDate.getSeconds(), nextDate.getSeconds())
this.tag('AmPm').patch({
currentValue: this.getAmPm(currentDate.getHours()),
nextValue: this.getAmPm(nextDate.getHours())
})
}
window.requestAnimationFrame(() => this.setTime(currentDate))
}
getAmPm(hours) {
let ampm = 'AM'
if (hours >= 12 && hours < 24) {
ampm = 'PM'
}
return ampm
}
set12Hours(hours) {
return hours % 12 || 12
}
doubleZero(val) {
return val < 10 ? '0' + val : val.toString()
}
setCurrentNext(tagName, currentValue = '', nextValue = '', leading = true) {
const tag = this.tag(tagName)
currentValue = this.doubleZero(currentValue).split('')
nextValue = this.doubleZero(nextValue).split('')
if (!leading) {
currentValue[0] = currentValue[0] === '0' ? '' : currentValue[0]
nextValue[0] = nextValue[0] === '0' ? '' : nextValue[0]
}
tag.children = tag.children.map((child, i) => {
child.patch({
currentValue: currentValue[i],
nextValue: nextValue[i]
})
return child
})
}
_init() {
this.setTime()
}
}
class ChangeValue extends lng.Component {
_construct() {
this.duration = 0.7
this.commonFont = commonFont
}
_init() {
this.tag('Group').children.forEach((child) => {
child.patch({
text: {
fontSize: this.fontSize || this.commonFont.fontSize,
lineHeight: this.lineHeight || this.commonFont.lineHeight
}
})
})
}
static _template(that) {
return {
Group: {
Current: {
text: Object.assign(
{
text: this.bindProp(['currentValue', 'nextValue'], (ctx) => {
const currentValue = ctx.currentValue
const nextValue = ctx.nextValue
if (currentValue !== nextValue) {
ctx.changePosition(ctx)
}
return currentValue
})
},
that.commonFont
)
},
Next: {
y: that.commonFont.lineHeight * -1,
alpha: 1,
scale: 0.5,
text: Object.assign({ text: this.bindProp(['nextValue']) }, that.commonFont)
}
}
}
}
changePosition(ctx) {
ctx.tag('Group').setSmooth('y', ctx.commonFont.lineHeight, { duration: ctx.duration })
ctx.tag('Current').setSmooth('alpha', 0, { duration: ctx.duration })
ctx.tag('Current').setSmooth('scale', 0.5, { duration: ctx.duration })
ctx.tag('Next').setSmooth('alpha', 1, { duration: ctx.duration })
ctx.tag('Next').setSmooth('scale', 1, { duration: ctx.duration })
ctx.tag('Next')
.transition('scale')
.on('finish', () => {
ctx.resetPosition(ctx)
})
}
resetPosition(ctx) {
ctx.tag('Current').patch({
text: { text: ctx.nextValue },
alpha: 1,
scale: 1
})
ctx.tag('Group').patch({ y: 0 })
ctx.tag('Next').patch({ alpha: 0, scale: 0.5 })
}
}
const StartApp = new App({
stage: {
w: adjustDevicePixelRatio(width),
h: adjustDevicePixelRatio(height),
precision: adjustDevicePixelRatio(),
clearColor: '0x00ff0000',
useImageWorker: false
},
debug: true
})
const canvasApp = StartApp.stage.getCanvas()
document.querySelector('.app').appendChild(canvasApp)
function adjustDevicePixelRatio(val = 1) {
return val * (window.devicePixelRatio || 1)
}
到这里就制作完了,如需下载代码,请点击:滚动的数字时钟