用vue实现一个简单的时间屏幕

前言

去年用了一个小的 app,叫做 一个木函,本来想着用来做动画优化就删掉了的,不过看到他有个时间屏幕的小工具,就点进去看了下,觉得挺好玩的,就想着能不能自己实现一下。

ps: 闲话不多说,先上例子点我查看,觉得没啥意思的就不需要再往下看了

简单的搭建一下

初始化一个 vue 项目

vue create vue-time

然后无脑下一步就好了(回车),这里我打算用 scss 方便我们书写样式 ,所以创建完成后,我们在安装下 scss

cd vue-time
npm i sass-loader node-sass -D

ps: 如果网络不好,就换下源或者用 cnpm

新建时间屏幕组件

components 目录下新建 TimeScreen.vue 文件,然后通过 vbase 指令生成生成一个最基础的 vue 代码片段

ps: vbasevscode 中vue代码片段的一个插件

<template>
    <div>

    </div>
</template>

<script>
    export default {
        name: "TimeScreen"
    }
</script>

<style lang="scss" scoped>

</style>

思考一下,如何做时间切换的动画

emmm... 不知是否有看过我之前的一篇文章用jq实现的单个span为单个的数字动画,没错,其实我们实现的思路,和这里基本一样,所以接下来我们就分析分析我们该怎么来实现了吧

首先,我们要明确一下,要有多少个 span,众所周知,一天最后就是23:59:59,所以我们所需要的 span 数组为 [3, 10, 0, 6, 10, 0, 6, 10]

ps: 中间的 : 是只需要一个 span

因为布局不是我们要将的重点,所以我们就想想我们该怎么获取我们需要的东西。比如,怎么获取到Saturday 14 March,怎么获取到当前时间

众所周知啦,我们知道 js 中提供了 Date 这个对象,所以我们可以通过他可以获取我们想要的东西,废话不多说了,开始写代码吧。

新建 utils 目录,在该目录下新建 time.js 文件,内容如下

// 月份
const months = [
    "January","February","March","April","May","June",
    "July","August","September","October","November","December"
];
// 星期
const weekday = [
    "Sunday","Monday","Tuesday","Wednesday",
    "Thursday","Friday","Saturday"
];
// 获取日期
function getTime() {
    const date = new Date();
    const days = date.getDate();
    const month = date.getMonth();
    const day = date.getDay();
    const hours = toDou(date.getHours());
    const minutes = toDou(date.getMinutes());
    const seconds = toDou(date.getSeconds());
    return {
        date: `${weekday[day]} ${days} ${months[month]}`,
        time: `${hours}:${minutes}:${seconds}`
    };
}
// 转成两位 eg: 6 => 06
function toDou(str) {
    const num = ~~str;
    return num > 9 ? num : "0" + str;
}

// 测试一下我们写的方法,上线记得注释掉
// console.log(getTime()) // {date: "Saturday 14 March", time: "18:53:40"}

export default getTime

通过上面代码,我们就得到我们需要的格式啦,接下来就是写布局啦,但这里不是我们的重点,所以略过

<template>
    <div
        class="time-container"
        :class="{ dark: isDark }"
        @click="toggleClass"
        :date="date"
    >
        <div class="time">
            <template v-for="(str, idx) in time">
                <div
                    class="time-num"
                    v-if="str !== ':'"
                    :style="numStyle[idx]"
                    :key="idx"
                >
                    <span
                        v-for="(i, spanIdx) in haveSpan[idx]"
                        :key="spanIdx"
                        >{{ i - 1 }}</span
                    >
                    <span>0</span>
                </div>
                <div class="time-dist" v-else :key="idx">
                    <span>{{ str }}</span>
                </div>
            </template>
        </div>
    </div>
</template>

<script>
import getTime from "../utils/time";
// 设置样式
function setStyle(val) {
    return `transform: translateY(-${~~val * 100}%)`;
}
// 每个字的样式
function numStyle(time) {
    return time.split("").map(val => setStyle(val));
}
export default {
    name: "TimeScreen",
    data() {
        const { time,date } = getTime();
        return {
            isDark: 0,
            time,
            date,
            numStyle: numStyle(time)
        };
    },
    methods: {
        // 切换样式
        toggleClass() {
            this.isDark = !this.isDark;
        },
    },
    created() {
        // 判单有多少个Span
        // 比较小时数最多24小时,所以第一位最多是3个,0、1、2
        // 这里使用 freeze 是因为这个值已经固定,没有必要进行数据劫持
        this.haveSpan = Object.freeze([3, 10, 0, 6, 10, 0, 6, 10]);
    }
};
</script>
<style lang="scss" scoped>
%flexCenter {
    display: flex;
    justify-content: center;
    align-items: center;
}
$timeColor: #d9d4d0;
$white: #fff;
.time-container {
    background: $white;
    color: $timeColor;
    position: absolute;
    width: 100%;
    height: 100%;
    max-width: 540px;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    &.dark {
        background: #000;
        color: $white;
    }
    &::after {
        content: attr(date);
        position: absolute;
        color: $timeColor;
        font-size: 18px;
        line-height: 1;
        transform: rotate(90deg);
        bottom: 20%;
        left: -48px;
    }
    @extend %flexCenter;
    .time {
        font-size: 70px;
        transform: rotate(90deg);
        position: relative;
        height: 106px;
        line-height: 106px;
        overflow: hidden;
        @extend %flexCenter;

        .time-num {
            position: relative;
            width: 100%;
            height: 100%;
            text-align: center;
            text-shadow: 0 0 2px $white;
            transition: 0.5s all;
            span {
                display: block;
            }
        }
        .time-dist {
            padding-bottom: 15px;
            margin: 0 10px;
        }
    }
}
</style>

写到这里,其实基本的样子已经出来了,这里我们用到了 attr 函数,用来回选择元素的属性值,这个小技巧在一些场景很用用哦。

这里我们多加了一个 <span>0</span> 这里主要是为了无缝,那么我们如何做到无缝呢?即,当我们滚动最低下的时候,在500ms之内让动画取消。

ps: 这里的 500ms 是因为动画设置了 500ms,所以需要用 1s - 500ms 得出来的500ms

既然知道了原理,那么我们就开始写我们的代码了

首先定义一下清空动画之后的样式,即

// 清除样式
const style = "transform: translateY(0%);transition:0s all";

那么什么时候清空呢,前面也说了,当滚动到最下面的时候,也就是当 time 这个字符串某个为 0 的时候,我们就要清空了,所以

this.time.split("").forEach((val, idx) => {
    // 当 val 为 0 时,说明已经滚到最底下,这里需要清除动画,并让他回到最顶上来实现无缝
    if (val == 0) {
        if (this.numStyle[idx] !== style) {
            // 500ms后清除当前这个span的动画
            this.removeAnimate(idx);
            // 设置样式
            this.numStyle[idx] = setStyle(this.haveSpan[idx]);
        }
    } else {
        this.numStyle[idx] = setStyle(val);
    }
})

// 清除动画
removeAnimate(idx) {
    setTimeout(() => {
        this.numStyle[idx] = style;
        this.numStyle = [...this.numStyle];
    }, 500);
}

最后,就是写一个简单的定时器啦,我想这应该难不倒各位小伙伴啦,所以我就不详解啦,就贴一下代码

<template>
    <div
        class="time-container"
        :class="{ dark: isDark }"
        @click="toggleClass"
        :date="date"
    >
        <div class="time">
            <template v-for="(str, idx) in time">
                <div
                    class="time-num"
                    v-if="str !== ':'"
                    :style="numStyle[idx]"
                    :key="idx"
                >
                    <span
                        v-for="(i, spanIdx) in haveSpan[idx]"
                        :key="spanIdx"
                        >{{ i - 1 }}</span
                    >
                    <span>0</span>
                </div>
                <div class="time-dist" v-else :key="idx">
                    <span>{{ str }}</span>
                </div>
            </template>
        </div>
    </div>
</template>

<script>
import getTime from "../utils/time";

// 清除样式
const style = "transform: translateY(0%);transition:0s all";

// 设置样式
function setStyle(val) {
    return `transform: translateY(-${~~val * 100}%)`;
}
// 每个字的样式
function numStyle(time) {
    return time.split("").map(val => setStyle(val));
}

export default {
    name: "TimeScreen",
    data() {
        // 获取时间
        let { time, date } = getTime();
        return {
            isDark: 0,
            time,
            date,
            numStyle: numStyle(time)
        };
    },
    methods: {
        // 更新样式
        updateStyle() {
            this.time.split("").forEach((val, idx) => {
                if (val == 0) {
                    if (this.numStyle[idx] !== style) {
                        this.removeAnimate(idx);
                        this.numStyle[idx] = setStyle(this.haveSpan[idx]);
                    }
                } else {
                    this.numStyle[idx] = setStyle(val);
                }
            });
        },
        // 切换样式
        toggleClass() {
            this.isDark = !this.isDark;
        },
        // 清除样式
        removeAnimate(idx) {
            setTimeout(() => {
                this.numStyle[idx] = style;
                this.numStyle = [...this.numStyle];
            }, 500);
        },
        // 每秒更新时间
        updateTime() {
            const { time, date } = getTime();
            this.time = time;
            this.date = date;
            this.updateStyle();
        }
    },
    created() {
        // 判单有多少个Span
        // 比较小时数最多24小时,所以第一位最多是3个,0、1、2
        this.haveSpan = Object.freeze([3, 10, 0, 6, 10, 0, 6, 10]);
        // 定时器
        this.timer = null;
    },
    mounted() {
        // 触发定时器
        this.timer = setInterval(this.updateTime, 1000);
    },
    destroyed() {
        // 清除定时器
        clearInterval(this.timer);
    }
};
</script>

<style lang="scss" scoped>
%flexCenter {
    display: flex;
    justify-content: center;
    align-items: center;
}
$timeColor: #d9d4d0;
$white: #fff;
.time-container {
    background: $white;
    color: $timeColor;
    position: absolute;
    width: 100%;
    height: 100%;
    max-width: 540px;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    &.dark {
        background: #000;
        color: $white;
    }
    &::after {
        content: attr(date);
        position: absolute;
        color: $timeColor;
        font-size: 18px;
        line-height: 1;
        transform: rotate(90deg);
        bottom: 20%;
        left: -48px;
    }
    @extend %flexCenter;
    .time {
        font-size: 70px;
        transform: rotate(90deg);
        position: relative;
        height: 106px;
        line-height: 106px;
        overflow: hidden;
        @extend %flexCenter;

        .time-num {
            position: relative;
            width: 100%;
            height: 100%;
            text-align: center;
            text-shadow: 0 0 2px $white;
            transition: 0.5s all;
            span {
                display: block;
            }
        }
        .time-dist {
            padding-bottom: 15px;
            margin: 0 10px;
        }
    }
}
</style>

最后的最后

感谢各位观众老爷的观看啦O(∩_∩)O,希望大家可以一起进步

原文地址:https://www.cnblogs.com/gating/p/12495000.html

时间: 2024-10-09 09:44:25

用vue实现一个简单的时间屏幕的相关文章

简介(1)-概述、一个简单的时间获取客户程序

1.概述 大多数网络应用划分:客户(client)和服务器(server) 一些复杂的网络应用:异步回调通信,即服务器向客户发起请求消息. 协议栈:应用协议.TCP协议.IP协议.以太网协议 局域网(local area network,LAN),广域网(wide area network,WAN). 路由器是广域网的架构设备. 因特网:当今最大的广域网. POSIX:一种被多数厂商采纳的标准. 2.一个简单的时间获取客户程序 1)创建套接字 socket函数 2)指定服务器的IP地址和端口 s

Vue小白踩坑 使用vue做一个简单的todo list

前言 寒假待在家,无所事事,便开始做起了毕设,由于要写跨端应用,基础是vue,所以先攻克vue 做一个简单的todo 当然是自己做的,效率有点儿低,不过能做出来已经很不错了把 hhh 源码 本质上毕设的前端展示,也就是对一个todo-list加一个好看的ui,数据排版等等花里胡哨的东西,本质的东西还是数据的crud <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title

使用Laravel 和 Vue 构建一个简单的SPA

本教程是作者自己在学习Laravel和Vue时的一些总结,有问题欢迎指正. Laravel是PHP的一个框架,Vue是前端页面的框架,这两个框架如何结合起来构建一个SPA(Single Page Application)呢?流程大致分为下面三步: 页面请求Laravel的一个路由 路由返回渲染一个包含了Vue的SPA框架 在上面渲染出来的框架中使用Vue来加载不同的页面单元模块 主要会学习使用到三个东西: laravel vue.js Vue-router axios 上面是一个简单的流程图,从

vue封装一个简单的div框选时间的组件

记录一下我前段时间封装的一个vue组件吧.技术需要积累,有时间我把我之前写的还不错的组件都开源出来.并尝试vue和react 两种方式的组件封装.今天简单写下鼠标框选div选中效果的封装吧. div框选实现 div框选效果,其实没有什么好的方法,就是获取鼠标事件,根据鼠标的位置,动态创建一个跟随鼠标的div.[注:这种方式需要依赖position的定位方式,一般鼠标事件位置是针对全局的,所以鼠标框选的div 位置的position最好父级元素是根元素的定位.不然,鼠标框选区域和被框选区域很难保持

vue实现一个简单的选项卡

用vue来实现一个小的选项卡切换,比之前要简单.方便很多. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="js/vue.min.js"></script> <style> button{

一个简单的时间获取服务器程序

程序执行流程: 1.创建TCP套接字 listenfd = Socket(AF_INET, SOCK_STREAM, 0) 2.清空sockaddr_in  servaddr结构体 bzero(&servaddr, sizeof(servaddr)) 3.填写网际套接字地址结构 我们指定IP地址为INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任意网络接口上接受客户连接 servaddr.sin_family      = AF_INET; servaddr.sin_a

UNIX网络编程1.5一个简单的时间获取服务器程序1.6客户服务器程序索引表

#include "../lib/unpsunyj.h" #include <time.h> int main(int argc, char ** argv) { int listenfd; int connfd; sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; // TCP套接字的创建 // listenfd = Socket(AF_INET, SOCK_STREAM, 0); if ((listenfd =

用 Vue 开发一个简单的答题应用(一)

Vue 之类的 MVVM 框架,能帮助我们用更少的代码实现复杂的业务. 为了简单一点,开发计划分成三阶段: 一,数据写死,实现基本的答题效果.支持多种题型. 二,使用本地存储保存数据,支持题目录入的功能. 三,使用数据库,后台实现接口给前端调用. 目前,只实现了第一阶段. Demo,源码 原文地址:https://www.cnblogs.com/yunser/p/8331667.html

基于netty的一个简单的时间服务器的实现(netty学习)

基本功能:与客户端建立连接后立刻发送当前时间 先建立一个时间的类 package timeExample; import java.sql.Date; public class UnixTime { private final long value; public UnixTime() { this(System.currentTimeMillis()/1000L); } public UnixTime(long value) { this.value = value; } public lon