欢送关注微信公众号:前端侦探
练习 CSS 一个很好的办法就是绘制各式各样的 UI,比方这样一个时钟?
你也能够拜访这个 CSS clock查看实际效果
CSS 绘制这样一个布局有几个难点:
- 环形排列的刻度
- 环形散布的数字
- 主动运行的指针
上面就来一一实现它,置信能学到很多 CSS 绘制和动画的小技巧
一、环形排列的刻度
提到“环形”,能够想到锥形突变 conic-gradient。假如有这样一个容器
<clock></clock>
加上一点锥形突变
clock{ width: 300px; height: 300px; background: conic-gradient(#333 0 15deg, #cddc39 0deg 30deg);}
能够失去这样的成果
如何做出交织相间的成果呢?能够试试 repeating-conic-gradient
clock{ /**/ background: repeating-conic-gradient(#333 0 15deg, #cddc39 0deg 30deg);}
成果如下
还是看不出和刻度有啥关系?没关系,咱们把彩色局部的角度改小一点
clock{ /**/ background: repeating-conic-gradient(#333 0 1deg, #cddc39 0deg 30deg);}
成果如下
这样绘制进去的几条线是不是刚好能够对应时钟的刻度?
而后将整个形态变成圆环,能够用 MASK 来实现,实现如下
clock{ /**/ border-radius: 50%; -webkit-mask: radial-gradient(transparent 145px, red 0);}
成果如下
其实,这里还有一个小细节,彩色局部并不是居中的,须要修改一下(能够更改起始角度,指定 from)。而后,将这个草绿色换成通明就能够了,残缺代码如下
clock{ /**/ background: repeating-conic-gradient(from -.5deg, #333 0 1deg, transparent 0deg 30deg); border-radius: 50%; -webkit-mask: radial-gradient(transparent 145px, red 0);}
最终成果
分钟的刻度也是同样的情理,因为共有 60 个刻度,所以最小角度是 6 度(360 / 60),实现如下
clock{ /**/ background: repeating-conic-gradient(#333 0 1deg, #cddc39 0deg 6deg);}
利用 CSS 背景能够有限叠加的个性,能够将这两个背景绘制在同一个元素下,所以残缺代码如下
clock{ /**/ background: repeating-conic-gradient(from -.5deg, #333 0 1deg, transparent 0deg 30deg), repeating-conic-gradient(from -.5deg, #ccc 0 1deg, transparent 0deg 6deg); border-radius: 50%; -webkit-mask: radial-gradient(transparent 145px, red 0);}
最终表盘刻度成果如下
二、环形散布的数字
看到这种布局,我的第一反馈其实是 textPath,这个 SVG 元素能够让文本沿着指定门路进行排列,比方上面这个 MDN 上的例子
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <path id="MyPath" fill="none" stroke="red" d="M10,90 Q90,90 90,45 Q90,10 50,10 Q10,10 10,40 Q10,70 45,70 Q70,70 75,50" /> <text> <textPath href="#MyPath"> Quick brown fox jumps over the lazy dog. </textPath> </text></svg>
成果如下
然而,这种形式有一个缺点,无奈扭转文字的角度,只能沿着门路垂直方向,而时钟的数字方向都是失常的。
通过一番推敲,发现还有一种形式也有相似沿着门路的布局形式,那就是 offset-path! 上面是 MDN 上的一个演示成果
那么和环形排列数字有什么关系呢?假如有这样一个布局
<clock-pane> <num>1</num></clock-pane>
而后将这个数字指定到一个圆形的门路上(目前仅反对 path )
num{ offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');}
成果如下(起始点是追随 path 门路的)
而后,能够通过 offset-distance来扭转元素在门路上的地位,并且反对百分比
num{ offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z'); offset-distance: 100%}
上面是从 0 到 100% 的变动
默认状况下元素的角度也是自适应垂直于门路的,和 textPath 比拟相似。然而咱们能够手动指定固定角度,须要 offset-rotate,指定为 0deg 就行了
num{ offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z'); offset-rotate: 0deg; offset-distance: 100%}
成果如下,角度曾经齐全归正了
如果有相似的布局需要,是不是能够参考这个案例呢?
接下来,咱们通过 CSS 变量,把 12 个数字主动归位到指定地位
<clock-pane> <num style="--i:1">1</num> <num style="--i:2">2</num> <num style="--i:3">3</num> <num style="--i:4">4</num> <num style="--i:5">5</num> <num style="--i:6">6</num> <num style="--i:7">7</num> <num style="--i:8">8</num> <num style="--i:9">9</num> <num style="--i:10">10</num> <num style="--i:11">11</num> <num style="--i:12">12</num></clock-pane>
配合 calc 计算,残缺代码如下
num{ position: absolute; offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z'); offset-distance: calc( var(--i) * 10% / 1.2 - 25%); offset-rotate: 0deg;}
成果如下
三、主动运行的指针
三个指针的绘制应该没有太大的难度,假如构造如下
<hour></hour><min></min><sec></sec>
须要留神一下旋转的核心
hour{ position: absolute; width: 4px; height: 60px; background: #333; transform-origin: center bottom; transform: translateY(-50%) rotate(30deg);}min{ position: absolute; width: 4px; height: 90px; background: #333; transform-origin: center bottom; transform: translateY(-50%) rotate(60deg);}sec{ position: absolute; width: 2px; height: 120px; background: red; transform-origin: center bottom; transform: translateY(-50%) rotate(90deg);}sec::after{ content: ''; position: absolute; width: 10px; height: 10px; border-radius: 50%; left: 50%; bottom: 0; background: #fff; border: 4px solid #333; transform: translate(-50%, 50%);}
成果如下
到这一步动态布局就算实现了,那么如何运行呢?
之前在这篇文章 [还在应用定时器吗?CSS 也能实现电子时钟]() 有具体解说到,不借助定时器,也齐全能够用 CSS 动画来实现!
回到这里,运行的原理很简略,就是一个有限循环的 CSS 动画,如下
@keyframes clock { to { transform: translateY(-50%) rotate(360deg); }}
不同的是,时针、分针、秒针的周期不一样,时针转一圈是 12 小时、分针是 60 分钟、秒针是 60 秒,各自须要换算成 秒数(CSS 单位只反对 秒 和 毫秒),为了不便测试,这里将速度调快了 60s → 6s
代码实现就是(--step
是一分钟)
hour{ /**/ transform: translateY(-50%) rotate(0); animation: clock calc(var(--step) * 60 * 12) infinite;}min{ /**/ transform: translateY(-50%) rotate(0); animation: clock calc(var(--step) * 60) infinite;}sec{ /**/ transform: translateY(-50%) rotate(0); animation: clock var(--step) infinite;}
成果如下
是不是有些奇怪?秒针在旋转时先缓缓变快,而后又缓缓变慢,这是因为默认的动画函数是ease
,所以须要改成linear
sec{ /**/ animation: clock var(--step) infinite linear;}
这样就好多了。不过平时所见的时钟,秒针通常都那种走一下,停下的,还有一种“滴答滴答”的节奏感,并不是这种无缝的。在 CSS 动画中,是不是有点像阶梯状,没错,能够用到 CSS 的 steps 函数,不理解这个的能够参考张老师的这篇文章:CSS3 animation属性中的steps性能符深刻介绍,实现如下
sec{ /**/ animation: clock var(--step) infinite steps(60);}
成果如下
接下来须要通过 JS 初始化工夫,仅此而已。须要留神的是,在获取的时候加上偏移量,比方 12:30,分针其实是 12.5,以此类推。代码实现如下
const d = new Date()const h = d.getHours();const m = d.getMinutes();const s = d.getSeconds();clock.style.setProperty('--ds', s)clock.style.setProperty('--dm', m + s/60)clock.style.setProperty('--dh', h + m/60 + s/3600)
而后 CSS 中能够通过 animation-delay
来指定动画的起始地位
hour{ /**/ animation: clock calc(var(--step) * 60 * 12) infinite linear; animation-delay: calc( -1 * var(--step) * var(--dh) * 60);}min{ /**/ animation: clock calc(var(--step) * 60) infinite linear; animation-delay: calc( -1 * var(--step) * var(--dm));}sec{ /**/ animation: clock var(--step) infinite steps(60); animation-delay: calc( -1 * var(--step) * var(--ds) / 60 );}
而后加点轮廓装璜,就实现了文章结尾的成果
残缺代码能够查看 CSS clock
四、简略总结一下
以上就是CSS 绘制时钟的全副过程了,本文更侧重于绘制过程,特地是是环形布局技巧,这里简略总结一下
- 碰到环形图案能够想到 conic-gradient
- 环形刻度绘制的原理是 conic-gradient 和 MASK 裁剪
- 文字沿门路排列能够应用 textPath
- textPath 不反对扭转文字角度
- offset-path 能够让元素沿指定门路进行布局
- offset-path 能够设置元素在门路的偏移和角度
- 时钟主动运行的原理的是 CSS 动画
- 时钟、分钟、秒钟的区别是动画时长不同
- “滴答滴答”的成果能够用 steps 实现
- 工夫初始化能够通过 animation delay 实现
当然本文重点并不仅仅是在于实现了一个时钟,而是在于 CSS 绘制技巧的积攒和把握。有了相干的积攒,当前在碰到相似的布局,在脑子里过滤一下,马上就能找到解决方案。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤
欢送关注微信公众号:前端侦探