Web Animations API实现一个精确计时的时钟示例

目录
  • 正文
  • JavaScript 计时器问题
    • 低精度
  • Web Animations API
    • 开始时间
    • 示例:精确计时的时钟
  • 总结

正文

在 JavaScript 中,当事情准时发生时,很自然地会想到使用计时器函数。 但是,当某件事由于其他事情依赖于它而在准确的时刻发生时,你很快就会发现计时器会存在一个不准时的问题。而本文所要介绍的 Web Animations API 可以在某些情况下替代计时器函数,同时保持精确。

当你需要处理精确的视觉呈现时,你就会发现你花费了太多时间来解决 JavaScript 无法准确解决代码何时实际执行的问题。

例如,下面就举了一个计时器准确性的问题。

JavaScript 计时器问题

在 JavaScript 中,每个任务都会经过一个队列。 包括你的代码、用户交互、网络事件等都会放入各自的任务队列,进行事件循环处理。 这么做能够保证任务按顺序发生。例如,当事件触发或计时器到期时,你在回调中定义的任务将进入到队列。 一旦事件循环轮到了它,你的代码就会被执行。

可是,当在任务队列中执行计数器函数时,问题就会暴露了。

低精度

在将任务放入队列之前,我们可以准确定义超时应该等待多长时间。 但是,我们无法预测的是目前队列中会出现什么。这是因为 setTimeout 保证在将事物放入队列之前的最小延迟。 但是没有办法知道队列中已经有什么。

曾经我不得不为一个网站实现随机翻转图块,其中一个错误是由休眠标签引起的。 因为每个图块都有自己的计时器,所以当标签激活时,它们都会同时触发。那个案例如下代码所示:

<article id="demo">
  <section>
    <h3>Timeouts</h3>
    <div class="row">
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
    </div>
  </section>
  <section>
    <h3>Animations</h3>
    <div class="row">
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
      <div class="square">
        <div></div>
        <div></div>
      </div>
    </div>
  </section><button type="button">‣ Run</button>
</article>
#demo {
  display: flex;
  background-color: white;
  color: black;
  flex-flow: column nowrap;
  align-items: center;
  padding: 2rem;
  gap: 2rem;
}
.row {
    display: flex;
    gap: 0.5rem;
}
.square {
  display: flex;
  width: 5rem;
  height: 5rem;
  position: relative;
  transform-style: preserve-3d;
}
.square > * {
  flex: 1 0 100%;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  background-color: green;
}
.square > *:last-child {
  background-color: rgb(227, 227, 0);
  position: absolute;
  width: 100%;
  height: 100%;
  transform: rotateY(0.5turn);
}
(function () {
    "use strict";
    const flip_keyframe = {
        transform: [
            "rotateX(0turn)",
            "rotateX(0.5turn)",
        ]
    };
    const timing_options = {
        duration: 1000,
        fill: "forwards"
    }
    function create(element) {
        const animation = element.animate(flip_keyframe, timing_options);
        animation.pause();
        return animation;
    }
    function reset(animation) {
        animation.pause();
        animation.currentTime = 0;
    }
    const id = "demo";
    const demo = document.getElementById(id);
    const sections = demo.querySelectorAll("section");
    const first_row_animations = Array.from(
        sections[0].lastElementChild.children
    ).map(create);
    const second_row_animations = Array.from(
        sections[1].lastElementChild.children
    ).map(create);
    const button = document.querySelector("button");
    button.addEventListener("click", function (event) {
        const start_time = document.timeline.currentTime;
        first_row_animations.forEach(reset);
        second_row_animations.forEach(reset);
        first_row_animations.forEach(function (animation, index) {
            setTimeout(function () {
                animation.play();
            }, 250 * index);
        });
        second_row_animations.forEach(function (animation, index) {
            animation.startTime = start_time + (250 * index);
        });
        setTimeout(function () {
            const start = Date.now();
            while (Date.now() - start < 400) {}
        }, 500);
    });
}());

为了解决这个问题,我想到了 Web Animations API

Web Animations API

Web Animations API 引入了时间线的概念。 默认情况下,所有动画都与文档的时间轴相关联。 这意味着动画共享相同的“内部时钟”——即从页面加载开始的时钟。

共享时钟使我们能够协调动画。无论是某种节奏还是一种模式,你都不必担心某些事情会延迟或超前发生。

开始时间

要使动画在某个时刻开始,请使用 startTime 属性。 startTime 的值以页面加载后的毫秒数为单位。 开始时间设置为 1000.5 的动画将在文档时间轴的 currentTime 属性等于 1000.5 时开始播放。

你是否注意到开始时间值中的小数点了吗? 是的,你可以使用毫秒的分数来精确时间。 但是,精确度取决于浏览器设置。

另一个有趣的事情是开始时间也可以是负数。 你可以自由地将其设置为未来的某个时刻或过去的某个时刻。 将该值设置为 -1000,你的动画状态就像页面加载时已经播放了一秒钟一样。 对于用户来说,动画似乎在他们甚至还没有考虑访问你的页面之前就已经开始播放了。

下面我们给出一个示例一起来看下如何使用 Web Animations API。

示例:精确计时的时钟

这个例子是一个精确计时的时钟,代码如下:

<template id="tick">
    <div class="tick"><span></span></div>
</template>
<template id="digit"><span class="digit" style="--len: 10;"><span></span></span></template>
<div id="analog-clock">
    <div class="hour-ticks"></div>
    <div class="minute-ticks"></div>
    <div class="day"></div>
    <div class="hand second"><div class="shadow"></div><div class="body"></div></div>
    <div class="hand minute"><div class="shadow"></div><div class="body"></div></div>
    <div class="hand hour"><div class="shadow"></div><div class="body"></div></div>
    <div class="dot"></div>
</div>
<div id="digital-clock">
    <span class="hours"></span><span>:</span><span class="minutes"></span><span>:</span><span class="seconds"></span><span>.</span><span class="milliseconds"></span>
</div>
:root {
    --face-size: 15rem;
}
body {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-family: sans-serif;
}
body > * {
    margin: 1rem;
}
#analog-clock {
    width: var(--face-size);
    height: var(--face-size);
    position: relative;
    border: 3px solid #555;
    border-radius: 50%;
    font-weight: 400;
}
.dot {
    --size: 9px;
    position: absolute;
    left: calc(50% - calc(var(--size) / 2));
    top: calc(50% - calc(var(--size) / 2));
    width: var(--size);
    height: var(--size);
    background-color: #333;
    border-radius: 50%;
    filter: drop-shadow(1px 1px 1px #333);
}
.hand {
    position: absolute;
    bottom: 50%;
    left: calc(50% - calc(var(--width) / 2));
    width: var(--width);
    transform-origin: center bottom;
}
.hand > * {
    position: absolute;
    height: 100%;
    width: 100%;
    border-radius: 4px;
}
.hand .body {
    background-color: #333;
}
.hand .shadow {
    background-color: black;
    opacity: 0.2;
    filter: drop-shadow(0 0 1px black);
}
.second {
    --width: 1px;
    height: 50%;
    transform-origin: center 80%;
    margin-bottom: calc(var(--face-size) * -0.1)
}
.second .body {
    background-color: black;
}
.minute {
    --width: 3px;
    height: 35%;
}
.hour {
    --width: 5px;
    height: 25%;
}
.day {
    --size: 2ch;
    position: absolute;
    left: calc(50% - calc(var(--size) / 2));
    top: calc(50% - calc(var(--size) / 2));
    width: var(--size);
    height: var(--size);
    transform: translate(calc(var(--face-size) * 0.2));
}
.tick {
    --width: 2px;
    --height: 29px;
    --shift: translateY(calc(var(--face-size) / -2));
    position: absolute;
    width: var(--width);
    height: var(--height);
    background-color: #666;
    top: 50%;
    left: calc(50% - calc(var(--width) / 2));
    transform-origin: top center;
}
.tick > span {
    --width: calc(calc(var(--face-size) * 3.141592653589793) / 24);
    position: absolute;
    width: var(--width);
    top: 3px;
    left: calc(var(--width) / -2);
    text-align: center;
}
.hour-ticks .tick:nth-child(even) > span {
    display: none;
}
.hour-ticks .tick:nth-child(odd) {
    background: none;
}
.hour-ticks .tick {
    transform: rotate(calc(var(--index) * 15deg)) var(--shift);
}
.minute-ticks .tick {
    --width: 1px;
    --height: 5px;
    --shift: translateY(calc(var(--face-size) / -2.5));
    background-color: black;
    transform: rotate(calc(var(--index) * 6deg)) var(--shift);
}
.minute-ticks .tick:nth-child(5n+1) {
    display: none;
}
#digital-clock {
    font-size: 1.5rem;
    line-height: 1;
}
#digital-clock > span {
    display: inline-block;
    vertical-align: top;
}
.digit {
    display: inline-block;
    overflow: hidden;
    max-width: 1ch;
}
.digit.wide {
    max-width: 2ch;
}
.digit > span {
    display: inline-flex;
    align-items: flex-start;
}
.digit.wide > span > span {
    min-width: 2ch;
    text-align: right;
}
.day .digit > span > span {
    text-align: center;
}
const ms = 1;
const s = ms * 1000;
const m = s * 60;
const h = m * 60;
const d = h * 24;
const start_time = (function () {
    const time = new Date();
    const document_time = document.timeline.currentTime;
    const hour_diff = time.getHours() - time.getUTCHours();
    const current_time = (Number(time) % d) + (hour_diff * h);
    return document_time - current_time;
}());
const single_digit_keyframes = [
    {transform: "translateX(0)"},
    {transform: "translateX(calc(var(--len, 10) * -1ch)"}
];
const double_digit_keyframes = [
    {transform: "translateX(0)"},
    {transform: "translateX(calc(var(--len) * -2ch)"}
];
function range(len) {
    return new Array(len).fill(true);
}
function digits(len = 10, zero_based = true) {
    const digit = document.getElementById("digit").content.cloneNode(true);
    digit.firstElementChild.style.setProperty("--len", len);
    digit.firstElementChild.firstElementChild.append(
        ...range(len).map(function (ignore, index) {
            const span = document.createElement("span");
            span.textContent = zero_based ? index : index + 1;
            return span;
        })
    );
    if (len > 10) {
        digit.firstElementChild.classList.add("wide");
    }
    return digit;
}
(function build_analog_clock() {
    const clock = document.getElementById("analog-clock");
    const tick_template = document.getElementById("tick");
    const hour_marks_container = clock.querySelector(".hour-ticks");
    const minute_marks_container = clock.querySelector(".minute-ticks");
    const day = clock.querySelector(".day");
    hour_marks_container.append(...range(24).map(function (ignore, index) {
        const tick = tick_template.content.cloneNode(true);
        const shifted = index + 1;
        tick.firstElementChild.style.setProperty("--index", shifted);
        tick.firstElementChild.firstElementChild.textContent = shifted;
        return tick;
    }));
    minute_marks_container.append(...range(60).map(function (ignore, index) {
        const tick = tick_template.content.cloneNode(true);
        tick.firstElementChild.style.setProperty("--index", index);
        tick.firstElementChild.firstElementChild.remove();
        return tick;
    }));
}());
(function build_digital_clock() {
    const clock = document.getElementById("digital-clock");
    const hours = clock.querySelector(".hours");
    const minutes = clock.querySelector(".minutes");
    const seconds = clock.querySelector(".seconds");
    const milliseconds = clock.querySelector(".milliseconds");
    hours.append(digits(24));
    minutes.append(digits(6), digits());
    seconds.append(digits(6), digits());
    milliseconds.append(digits(), digits(), digits());
}());
(function start_analog_clock() {
    const clock = document.getElementById("analog-clock");
    if (clock === null) {
        return;
    }
    const second = clock.querySelector(".second");
    const minute = clock.querySelector(".minute");
    const hour = clock.querySelector(".hour");
    const hands = [second, minute, hour];
    const hand_durations = [m, h, d];
    const steps = [60, 60, 120];
    const movement = [];
    hands.forEach(function (hand, index) {
        const duration = hand_durations[index];
        const easing = `steps(${steps[index]}, end)`;
        movement.push(hand.animate(
            [
                {transform: "rotate(0turn)"},
                {transform: "rotate(1turn)"}
            ],
            {duration, iterations: Infinity, easing}
        ));
        const shadow = hand.querySelector(".shadow");
        if (shadow) {
            movement.push(shadow.animate(
                [
                    {transform: "rotate(1turn) translate(3px) rotate(0turn)"},
                    {transform: "rotate(0turn) translate(3px) rotate(1turn)"}
                ],
                {duration, iterations: Infinity, iterationStart: 0.9, easing}
            ));
        }
    });
    movement.forEach(function (move) {
        move.startTime = start_time;
    });
}());
(function start_digital_clock() {
    const clock = document.getElementById("digital-clock");
    if (clock === null) {
        return;
    }
    const milliseconds = clock.querySelector(".milliseconds");
    const seconds = clock.querySelector(".seconds");
    const minutes = clock.querySelector(".minutes");
    const hours = clock.querySelector(".hours");
    const sections = [seconds, minutes];
    const durations = [s, m, h];
    const animations = [];
    Array.from(
        milliseconds.children
    ).reverse().forEach(function (digit, index) {
        animations.push(digit.firstElementChild.animate(
            single_digit_keyframes,
            {
                duration: ms * (10 ** (index + 1)),
                iterations: Infinity,
                easing: "steps(10, end)"
            }
        ));
    });
    sections.forEach(function (section, index) {
        Array.from(
            section.children
        ).forEach(function (digit) {
            const nr_digits = digit.firstElementChild.children.length;
            animations.push(digit.firstElementChild.animate(
                single_digit_keyframes,
                {
                    duration: (
                        nr_digits === 10
                        ? durations[index] * 10
                        : durations[index + 1]
                    ),
                    iterations: Infinity,
                    easing: `steps(${nr_digits}, end)`
                }
            ));
        });
    });
    Array.from(hours.children).forEach(function (digit) {
        const nr_digits = digit.firstElementChild.children.length;
        animations.push(
            digit.firstElementChild.animate(
                double_digit_keyframes,
                {
                    duration: d,
                    iterations: Infinity,
                    easing: `steps(${nr_digits}, end)`
                }
            )
        );
    });
    animations.forEach(function (animation) {
        animation.startTime = start_time;
    });
}());
(function set_up_date_complication() {
    const day = document.querySelector(".day");
    if (day === null) {
        return;
    }
    function month() {
        const now = new Date();
        return digits(
            (new Date(now.getFullYear(), now.getMonth() + 1, 0)).getDate(),
            false
        );
    }
    function create_animation(digit) {
        const nr_digits = digit.firstElementChild.children.length;
        const duration = d * nr_digits;
        return digit.firstElementChild.animate(
            double_digit_keyframes,
            {
                duration,
                easing: `steps(${nr_digits}, end)`,
                iterationStart: (d * ((new Date()).getDate() - 1)) / duration
            }
        );
    }
    const new_day = day.cloneNode();
    new_day.append(month());
    day.replaceWith(new_day);
    Array.from(new_day.children).forEach(function (digit) {
        const complication = create_animation(digit);
        complication.startTime = start_time;
        complication.finished.then(set_up_date_complication);
    });
}());

效果如下:

因为时钟是一种精密仪器,所以我让秒针和分针在它们对应的值发生变化的那一刻改变它们的位置。 下面的代码说明了如何进行精确计时:

const clock = document.getElementById("analog-clock");
const second = clock.querySelector(".second");
const minute = clock.querySelector(".minute");
const hour = clock.querySelector(".hour");
const s = 1000;
const m = s * 60;
const h = m * 60;
const d = h * 24;
const hands = [second, minute, hour];
const hand_durations = [m, h, d];
const steps = [60, 60, 120];
const movement = hands.map(function (hand, index) {
    return hand.animate(
        [
            {transform: "rotate(0turn)"},
            {transform: "rotate(1turn)"}
        ],
        {
            duration: hand_durations[index],
            iterations: Infinity,
            easing: `steps(${steps[index]}, end)`
        }
    );
});
movement.forEach(function (move) {
    move.startTime = start_time;
});

秒针每转一圈需要 60000 毫秒,而分针比秒针慢 60 倍。

为了将时钟指针的操作与相同的时间概念联系起来(以确保分针在秒针完成旋转的那一刻准确地更新其位置),我使用了 startTime 属性。

另一方面,数字时钟有点违反直觉。每个数字都是一个带有溢出的容器:overflow: hidden;。在里面,有一排从零到一的数字坐在等宽的单元格中。通过将行水平平移单元格的宽度乘以数字值来显示每个数字。与模拟时钟上的指针一样,这是为每个数字设置正确持续时间的问题。虽然从毫秒到分钟的所有数字都很容易做到,但小时数需要一些技巧。

让我们看一下 start_time 变量的值:

const start_time = (function () {
    const time = new Date();
    const hour_diff = time.getHours() - time.getUTCHours();
    const my_current_time = (Number(time) % d) + (hour_diff * h);
    return document.timeline.currentTime - my_current_time;
}());

为了计算所有元素必须开始的确切时间,我取了 Date.now() 的值(自 1970 年 1 月 1 日以来的毫秒数),从中去掉一整天,并通过 与 UTC 时间的差异。 这给我留下了自今天开始以来经过的毫秒数。 这是我的时钟需要显示的唯一数据:小时、分钟和秒。

为了将该值转换为正常格式,我需要根据从加载此页面到调用 Date.now() 所经过的时间来调整它。 为此,我从 currentTime 中减去它。

总结

动画共享相同的时间参考,通过调整它们的 startTime 属性,你可以将它们与你需要的任何模式对齐。

Web Animations API 带有强大的 API,可让你显着减少工作量。 它还具有精确度,为实现一些需要精确性的应用程序提供了可能性。

希望我在本文中提供的示例能让你更好地了解它。

以上就是Web Animations API实现一个精确计时的时钟示例的详细内容,更多关于Web Animations API时钟计时的资料请关注我们其它相关文章!

(0)

相关推荐

  • js实现计算器和计时器功能

    本文实例为大家分享了js实现计算器和计时器的具体代码,供大家参考,具体内容如下 完成简单的计算器 <!DOCTYPE html> <html>     <head>         <meta charset="utf-8">         <title></title>         <style type="text/css">             #box {     

  • Android开发之Animations动画用法实例详解

    本文实例讲述了Android开发之Animations动画用法.分享给大家供大家参考,具体如下: 一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动画效果 Java Code代码中 AlphaAnimation 渐变透明度动画效果 ScaleAnimation 渐变尺寸

  • javascript实现倒计时小案例

    本文实例为大家分享了javascript实现倒计时效果的具体代码,供大家参考,具体内容如下 效果如下: 代码思路: 1.这个倒计时是不断变化的,因此需要定时器来自动变化 ( setInterval )2.三个黑色盒子里面分别存放时分秒3.三个黑色盒子利用innerHTML放入计算的小时分钟秒数4.第一次执行也是间隔毫秒数,因此刷新页面会有空白5.最好采取封装函数的方法,这样可以先调用一次这个函数 html部分: <div>         <h4>距离2022-6-14 24:00

  • js实现酷炫倒计时动画

    本文实例为大家分享了js实现酷炫倒计时动画的具体代码,供大家参考,具体内容如下 前段时间和朋友去音乐餐厅吃饭,中间有个活动,然后看到他们软件公众号H5有个活动开始的倒计时的动画效果,于是想了下实现思路. <!DOCTYPE html> <html> <head>     <meta charset="utf-8" />     <title>js实现酷炫倒计时动画效果</title>     <style&g

  • Vue组件实现卡片动画倒计时示例详解

    目录 前言 需求拆解 组件设计思路 具体开发 animate-clock.vue animate-card 项目中使用 后记 前言 最近有朋友在做投票的项目,里面有用到一个倒计时的组件,还想要个动画效果.cv大法浸染多年的我,首先想到的是直接找个现有的组件. 通过一通搜索,看上的只有一个 vue2-flip-countdown,但是当我要修改大小和颜色的时候发现改不了,从而直接把源码拉到项目里面,改起来也挺麻烦. 而且,在搜索大法运行几个周天以后,其实心理已经有了一个倒计时开发整体思路,便决定自

  • JavaScript实现简单的倒计时效果

    本文实例为大家分享了JavaScript实现简单倒计时效果的具体代码,供大家参考,具体内容如下 <!DOCTYPE html> <html>     <head lang="en">         <meta charset="utf8">         <meta http-equiv="X-UA-Compatible" cotent="IE=edge,chrome=1&quo

  • Web Animations API实现一个精确计时的时钟示例

    目录 正文 JavaScript 计时器问题 低精度 Web Animations API 开始时间 示例:精确计时的时钟 总结 正文 在 JavaScript 中,当事情准时发生时,很自然地会想到使用计时器函数. 但是,当某件事由于其他事情依赖于它而在准确的时刻发生时,你很快就会发现计时器会存在一个不准时的问题.而本文所要介绍的 Web Animations API 可以在某些情况下替代计时器函数,同时保持精确. 当你需要处理精确的视觉呈现时,你就会发现你花费了太多时间来解决 JavaScri

  • python调用百度地图WEB服务API获取地点对应坐标值

    本篇博客介绍如何使用Python调用百度地图WEB服务API获取地点对应坐标值,现有一系列结构化地址数据(如:北京市海淀区上地十街十号),目的是获取对应坐标值. 百度地图开发者平台路线规划使用说明网址 最终结果是写入了txt文件,所以需要在循环遇到错误的时候写入对应的可识别的值(看到这个值就知道这个结果是错误的,可以写对应数量的NA或者0值),方便后续分析. # -*- coding: utf-8 -*- """ Created on Fri Aug 15 10:06:16

  • 基于Web Audio API实现音频可视化效果

    网页音频接口最有趣的特性之一它就是可以获取频率.波形和其它来自声源的数据,这些数据可以被用作音频可视化.这篇文章将解释如何做到可视化,并提供了一些基础使用案例. 基本概念节 要从你的音频源获取数据,你需要一个 AnalyserNode节点,它可以用 AudioContext.createAnalyser() 方法创建,比如: var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); var analyser

  • 浅谈Web Storage API的使用

    目录 一.浏览器的本地存储技术 1.1.sessionStorage 1.2.localStorage 二.Web Storage相关接口 三.浏览器兼容性 四.隐身模式 五.使用Web Storage API 一.浏览器的本地存储技术 除了最早的使用cookie来进行本地存储之外,现代浏览器使用Web Storage API来方便的进行key/value的存储. Web Storage有两种存储方式: 1.1.sessionStorage 对于每一个访问源,都会维持一个独立的存储区域.只要浏览

  • C#调用百度翻译API实现一个翻译功能

    前言 虽然百度翻译相对于谷歌翻译在准确性方面还有很大的提升空间,但网络的现实情况及百度翻译接口的免费易用性方面让我们选择百度翻译接口.下面来一起看看详细的步骤吧 方法如下 appId 和 passWord 需要到百度翻译开放平台申请 using System; using System.Net; using System.Web.Security; using System.Web.Script.Serialization; namespace TranslatorOfWang { class

  • .NET或.NET Core Web APi基于tus协议实现断点续传的示例

    前言 前两天我采用技巧式方案基本实现大文件分片上传,这里只是重点在于个人思路和亲身实践,若在实际生产环境要求比较高的话肯定不行,仍存在一些问题需要深入处理,本文继续在之前基础上给出基于tus协议的轮子方案,本打算再次尝试利用.NET Core实现此协议,但在github上一搜索早在2016年就已有此协议对应的.NET和.NET Core方案,并且一直更新到最近的.NET Core 3.x版本,完全满足各位所需,本文是我写出的一点demo,demo地址:https://github.com/wan

  • js前端如何写一个精确的倒计时代码

    关于写倒计时大家可能都都比较熟悉,使用 setTimeout 或 setInterval 就可以搞定.几秒钟或者几分钟的倒计时这样写没有问题,但是如果是长时间的倒计时,这样写就会不准确.如果用户修改了他的设备时间,这样的倒计时就没有意义了.今天就说说写一个精确的倒计时的方法. 原理 众所周知 setTimeout 或者 setInterval 调用的时候会有微小的误差.有人做了一个 demo 来观察这个现象并对其做了修正.短时间的误差倒也可以接受,但是作为一个长时间的倒计时,误差累计就会导致倒计

  • 使用 Rails API 构建一个 React 应用程序的详细步骤

    目录 后端:Rails API部分 前端:React部分 React组件 使用 axios 获取 API 数据 [51CTO.com快译]使用React创建项目时,动态数据无法保存的问题要怎么办呢?为此,我开始寻找一个充当备份的API来解决这一问题. 在本文中,我将介绍如何设置和构建一个以React作为前端的Rails API的一些要点,以帮助那些和我遇到一样问题的人. 本文计划使用Rails API作为后端,React作为前端,所以需要学习本文的人遵循同样的路径和步骤. 后端:Rails AP

  • 利用React高阶组件实现一个面包屑导航的示例

    什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理完成后的 React 组件.React 高阶组件在 React 生态中使用的非常频繁,比如react-router 中的 withRouter 以及 react-redux 中 connect 等许多 API 都是以这样的方式来实现的. 使用 React 高阶组件的好处 在工作中,我们经常会有很多功能相似,组件代码重复的页面需求,通常我们可以通过完全复制一遍代码的方式实现功能,但是这

  • java使用监听器实现一个统计网站在线人数的示例

    本文主要介绍了java使用监听器实现一个统计网站在线人数的示例,具有一定的参考价值,有需要的朋友可以了解一下. (1)创建一个监听器实现类 要大致统计一个网站的在线人数,首先,可以通过ServletContextListener监听,当Web应用上下文启动时,在ServletContext中添加一个List,用来准备存放在线的用户名:然后,可以通过HttpSessionAttributeListener监听,当用户登录成功把用户名设置到Session中时同时将用户名存放到ServletConte

随机推荐