心血来潮,想做一个苹果发布会的倒计时

访问地址

https://barnett617.github.io/clock/

过程

步骤如下:

  1. 首先,网页标题来个标志性的苹果图标

如何为网站添加图标:

在<head>标签中添加<link rel="shortcut icon" href="favicon.ico">,其会从项目根目录找 favicon.ico 文件

rel表示将要引用的资源类型,href表示指向资源的 URL, <link rel=“shortcut icon” href=“favicon.ico”>中 rel="shortcut icon"是一种固定写法,不写或错写会导致图标无法正常显示。

  1. 主体布局

背景

如果要背景铺满屏幕,来一个渐变色要怎么做呢

首先,为什么要用渐变,因为好看啊

其次,为什么渐变不用 PS 做的图片,因为 CSS3 提供了 gradients 属性,通过 CSS 实现其实渐变是由浏览器生成的,可以减少下载的事件和宽带的使用

既然渐变是浏览器生成的,就会涉及到不同浏览器的支持问题(Safari、Opera、Firefox 等)

另外,渐变的方式分为两种:线性渐变(Linear Gradients)径向渐变(Radial Gradients),顾名思义,线性渐变是从一个起点沿着一个方向从一种颜色渐变成另一种颜色,而径向渐变是从一个起点沿着一个角度渐变

线性:linear-gradient

径向:radial-gradient

其参数对于不同的浏览器有着不同的写法,但基本都是在配置渐变方向和渐变首末的颜色

从上到下(默认)

background: -webkit-linear-gradient(red, blue); /* Safari 5.1 - 6.0 */
background: -o-linear-gradient(red, blue); /* Opera 11.1 - 12.0 */
background: -moz-linear-gradient(red, blue); /* Firefox 3.6 - 15 */
background: linear-gradient(red, blue); /* 标准的语法 */

从左到右

background: -webkit-linear-gradient(left, red, blue); /* Safari 5.1 - 6.0 */
background: -o-linear-gradient(right, red, blue); /* Opera 11.1 - 12.0 */
background: -moz-linear-gradient(right, red, blue); /* Firefox 3.6 - 15 */
background: linear-gradient(to right, red, blue); /* 标准的语法 */

左上角到右下角

background: -webkit-linear-gradient(left top, red, blue); /* Safari 5.1 - 6.0 */
background: -o-linear-gradient(bottom right, red, blue); /* Opera 11.1 - 12.0 */
background: -moz-linear-gradient(
  bottom right,
  red,
  blue
); /* Firefox 3.6 - 15 */
background: linear-gradient(to bottom right, red, blue); /* 标准的语法 */

注意:Internet Explorer 9 及之前的版本不支持渐变

上面的是指定方向,也可以指定角度,如下:

background-image: linear-gradient(to top, #7a88ff, #7affaf);
/* 等价 */
background-image: linear-gradient(0deg, #7a88ff, #7affaf);

角度指的是过渡在哪个方向截止,浏览器会绘制一条经过元素中心点的假想线。指定的角度就是这条线的角度,同时还指明过度在哪里结束

度数解释描述
0deg表示元素的顶边to top
90deg表示元素的右边to right
180deg表示元素的底边to bottom
270deg表示元素的左边to left

如果仍然无法理解角度如何用,这里有一个在线演示

除此,透明度(transparent)使用

CSS3 渐变也支持透明度(transparent),可用于创建减弱变淡的效果。

可使用 rgba() 函数来定义颜色结点。rgba() 函数中的最后一个参数可以是从 0 到 1 的值,它定义了颜色的透明度:0 表示完全透明,1 表示完全不透明。

如下为从左侧完全透明的红色到右侧完全不透明的红色

background: -webkit-linear-gradient(
  left,
  rgba(255, 0, 0, 0),
  rgba(255, 0, 0, 1)
); /* Safari 5.1 - 6 */
background: -o-linear-gradient(
  right,
  rgba(255, 0, 0, 0),
  rgba(255, 0, 0, 1)
); /* Opera 11.1 - 12*/
background: -moz-linear-gradient(
  right,
  rgba(255, 0, 0, 0),
  rgba(255, 0, 0, 1)
); /* Firefox 3.6 - 15*/
background: linear-gradient(
  to right,
  rgba(255, 0, 0, 0),
  rgba(255, 0, 0, 1)
); /* 标准的语法 */

以上都是线性渐变

接下来是径向渐变

radial-gradient(center, shape size, start-color, ..., last-color);

若不指定前面的参数,只指明颜色,则按照默认情况对颜色进行均匀分布

可以根据需求添加任意多个颜色。额外添加的颜色叫色标(color stop)

添加色标后,背景会从第一个颜色过渡到第二个颜色,再从第二个颜色过渡到第三个颜色,直到渐变的最后一个颜色为止。浏览器会平均分布各个颜色

background: -webkit-radial-gradient(red, green, blue); /* Safari 5.1 - 6.0 */
background: -o-radial-gradient(red, green, blue); /* Opera 11.6 - 12.0 */
background: -moz-radial-gradient(red, green, blue); /* Firefox 3.6 - 15 */
background: radial-gradient(red, green, blue); /* 标准的语法 */

如下为背景色从左到右开始渐变,最左边是玫红,在元素宽度 20%的位置变成青色,80%的位置变成黄色,最后是蓝色。

linear-gradient(to right, #E94E65, #15A892 20%, #A89215 80%, #1574A8);

使用多色渐变时,第一个颜色和最后一个颜色无需指定位置,因为浏览器会嘉定第一个颜色从 0%的位置开始,最后一个颜色在 100%的位置结束。除非想把第一个颜色或最后一个颜色的位置放在指定的位置开始,才需要专门定位。

若想颜色不均匀分布,可手动对颜色增加权重

background: -webkit-radial-gradient(
  red 5%,
  green 15%,
  blue 60%
); /* Safari 5.1 - 6.0 */
background: -o-radial-gradient(
  red 5%,
  green 15%,
  blue 60%
); /* Opera 11.6 - 12.0 */
background: -moz-radial-gradient(
  red 5%,
  green 15%,
  blue 60%
); /* Firefox 3.6 - 15 */
background: radial-gradient(red 5%, green 15%, blue 60%); /* 标准的语法 */

shape 参数定义了形状。它可以是值 circle 或 ellipse。其中,circle 表示圆形,ellipse 表示椭圆形。默认值是 ellipse。

background: -webkit-radial-gradient(
  circle,
  red,
  yellow,
  green
); /* Safari 5.1 - 6.0 */
background: -o-radial-gradient(
  circle,
  red,
  yellow,
  green
); /* Opera 11.6 - 12.0 */
background: -moz-radial-gradient(
  circle,
  red,
  yellow,
  green
); /* Firefox 3.6 - 15 */
background: radial-gradient(circle, red, yellow, green); /* 标准的语法 */

repeating-radial-gradient() 函数用于重复径向渐变

/* Safari 5.1 - 6.0 */
background: -webkit-repeating-radial-gradient(red, yellow 10%, green 15%);
/* Opera 11.6 - 12.0 */
background: -o-repeating-radial-gradient(red, yellow 10%, green 15%);
/* Firefox 3.6 - 15 */
background: -moz-repeating-radial-gradient(red, yellow 10%, green 15%);
/* 标准的语法 */
background: repeating-radial-gradient(red, yellow 10%, green 15%);

布局

需要文字内容水平垂直居中,这就涉及到一个老生常谈的问题

一开始随手一加,使用 flex 布局

<body>
  <div class="container">
    <span>倒计时</span>
  </div>
</body>
.container {
  display: flex;
  justify-content: center;
  align-items: center;
}

发现效果并不理想,只水平居中了,垂直方向并没有居中

这里犯了一个 flex 布局的理解错误,对某个元素进行 flex 的设置,将影响其子元素,而不是其本身的布局,所以这里其实想 container 这一级水平垂直居中,那么display: flex;应该加在它的父级,即 body 的属性上

而在 body 上加过多的样式不是理想的做法,我们是想以 container 为页面根级,所以在其上加 flex 布局,然后将其子元素达到居中的效果,那么要让 container 作为页面根级,则需要其铺满屏幕,这里需要做一些样式设计。

首先,发现 container 并未铺满屏幕,即元素没有贴边浏览器,这是因为 body 有一个默认外边距,会随着不同的浏览器有着不同的行为,所以即使 container 高度和宽度 100%贴着 body,也无法占满屏幕,这就是为什么要对 css 作全局初始样式,重置其默认样式

body {
  display: block;
  margin: 8px;
}

需要在 body 上进行重置

button,
p,
pre {
  margin: 0;
}

另外,要让渐变色的背景铺满屏幕,这里采用了对 html 和 body 元素设置高度 100%,从而达到自适应,背景总占满屏幕的效果

html,
body {
  width: 100%;
  height: 100%;
}
body {
  background-image: linear-gradient(45deg, #7a88ff, #7affaf);
}

倒计时

接下来,需要一个定时器来展示倒计时

这里结束时间从简处理,即使用(后续可优化为目标时间从目标网站中获取)

思路

倒计时即是要计算剩余时间,即目标时间的时间戳减去当下时间的时间戳来做一个格式化显示,因为当下时间是实时变化的,所以并不需要做特殊处理,只要用目标时间戳(常量) - 当前时间(变量)便可得到倒计时应该显示的剩余时间

这里国际化是重点

首先来看一下 js 提供的当地时间(根据浏览器所设地区显示相应时间)方法

new Date().toLocaleString();
// "2018/9/4 下午5:04:02"
new Date().toLocaleDateString();
// "2018/9/4"
new Date().toLocaleTimeString();
// "下午5:04:21"

这是 js i18n程序提供的时间格式化方法

Date 对象是 js 原生时间库,以1970年1月1日00:00:00作为起点

Date 对象作为普通函数直接调用会返回代表当前时间的字符串(无论是否传递参数)

Date();
// "Tue Sep 04 2018 17:23:42 GMT+0800 (中国标准时间)"
Date("2018-08-08");
// "Tue Sep 04 2018 17:23:55 GMT+0800 (中国标准时间)"
Date(2018, 08, 08);
// "Tue Sep 04 2018 17:24:05 GMT+0800 (中国标准时间)"

Date 作为构造函数时,使用 new 返回一个 Date 对象实例

new Date();
// Tue Sep 04 2018 17:25:49 GMT+0800 (中国标准时间)
new Date("2018-08-08");
// Wed Aug 08 2018 08:00:00 GMT+0800 (中国标准时间)
new Date(2018, 08, 08);
// Sat Sep 08 2018 00:00:00 GMT+0800 (中国标准时间)

*** 特殊地

普通对象求值,都默认调用其 valueOf()方法,而 Date 实例求值,默认调用 toString()方法

var a = "hello";
a;
// "hello"
a.valueOf();
// "hello"
a.toString();
// "hello"

var b = 3;
b;
// 3
b.valueOf();
// 3
b.toString();
// "3"

var c = new Date();
c;
// Tue Sep 04 2018 17:31:30 GMT+0800 (中国标准时间)
c.valueOf();
// 1536053490129
c.toString();
// "Tue Sep 04 2018 17:31:30 GMT+0800 (中国标准时间)"

使用 Date 构造函数生产日期实例可传递各种类型的参数

new Date().valueOf();
// 1536053659322

// 参数为时间零点开始计算的毫秒数
new Date(1536053659322);
// Tue Sep 04 2018 17:34:19 GMT+0800 (中国标准时间)

// 参数为日期字符串
new Date("Sep 4, 2018");
// Tue Sep 04 2018 00:00:00 GMT+0800 (中国标准时间)

new Date("Oct 4, 2018");
// Thu Oct 04 2018 00:00:00 GMT+0800 (中国标准时间)

new Date("Oct 4, 2018, 08:59:59");
// Thu Oct 04 2018 08:59:59 GMT+0800 (中国标准时间)

new Date("Oct 4, 2018, 08:61:59");
// Invalid Date

// 参数为多个整数
// 代表年、月、日、小时、分钟、秒、毫秒
new Date(2018, 10, 23, 6, 34, 59, 500);
// Fri Nov 23 2018 06:34:59 GMT+0800 (中国标准时间)

new Date("Fri Nov 23 2018 06:34:59");
// Fri Nov 23 2018 06:34:59 GMT+0800 (中国标准时间)
new Date("Fri Nov 23 2018 06:34:59").valueOf();
// 1542926099000

既然这样,那么 Date()构造方法的参数都可以是什么格式的呢?

答:任何可以被 Date.parse()方法解析的字符串

注意点

  1. 参数可以是负整数,代表 1970 年元旦之前的时间

  2. 参数为多个整数时,至少需要两个参数,即年和月,若只传递一个参数,将按照毫秒数处理

  3. 参数取值范围

年:使用四位数年份,比如2000。如果写成两位数或个位数,则加上1900,即10代表1910年。如果是负数,表示公元前

月:0表示一月,依次类推,11表示12月

日:1到31

小时:0到23

分钟:0到59

秒:0到59

毫秒:0到999
  1. 参数如果超出了正常范围,会被自动折算

  2. 参数只有一个时,使用负整数表示1970年元旦之前的时间,参数为多个整数时,使用负整数表示从基准日扣去相应的时间

new Date(-1542926099000)
// Wed Feb 09 1921 09:25:01 GMT+0800 (中国标准时间)

new Date(2018, 09, -1)
// Sat Sep 29 2018 00:00:00 GMT+0800 (中国标准时间)
new Date(2018, 0)
Mon Jan 01 2018 00:00:00 GMT+0800 (中国标准时间)
new Date(2018, 0, -1)
Sat Dec 30 2017 00:00:00 GMT+0800 (中国标准时间)
new Date(2018, 1, -1)
Tue Jan 30 2018 00:00:00 GMT+0800 (中国标准时间)
new Date(2018, 1)
Thu Feb 01 2018 00:00:00 GMT+0800 (中国标准时间)

留个疑问,如何求明年的昨天,比如,现在是Tue Sep 04 2018 18:04:58 GMT+0800 (中国标准时间),明年的昨天即是Wed Sep 03 2019 18:04:58 GMT+0800 (中国标准时间),有好的方法可以提 PR,

此处的目标时间如下:

2018 年 9 月 12 日上午 10 点(北京时间 9 月 13 日凌晨 1 点)

后记

没想到最终呈现出来这么简单的一个页面竟涉及到这么多东西,而且每一个环节都可以细究到很多深层次的东西,这就是 web 的世界

补充

使用 vscode+搜狗输入法编辑本 md 文件上传至 github 后发现隐藏字符

查得原因

在 mac 版 vscode 的中文输入法下,按下任意字母,出现中文候选后按删除键,删除完刚才输入的字母再按删除会出现这个控制字符

mac 版的 vscode 中这个隐藏字符默认隐藏,可通过修改如下配置打开,将文档中显示BS的退格符全局搜索并替换为空即可

//控制编辑器是否应呈现控制字符
"editor.renderControlCharacters": true

原理解析:BS为 ASCII 码中的退格符的 Unicode 表示法,而所有 ASCII控制字符都有一个图形外观

参考资料: