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)
}

到这里就制作完了,如需下载代码,请点击:滚动的数字时钟

上一篇下一篇

猜你喜欢

热点阅读