双11小黑盒很炫酷?咱们用CSS变量来改进一下!

手撕红黑树 2020-11-11 23:28:23
css 变量 黑盒 改进 小黑


前言

刚过去的双11大家都去哪些网站购物了呢?不管去哪个网站,估计都少不了天猫吧,在逛天猫时发现了这样一种有趣的现象:

虽然说这种效果还比较常见,但有一个细节与众不同,比如说咱们先来看一个普通的 header 随窗口滚动而渐隐渐现的效果:

对比一下发现哪里不同了么,普通版的是一开始完全透明,随着窗口的滚动而慢慢显示出来,靠的是控制opacity透明度属性来实现的。

而小黑盒那版是一开始就有一个渐变色,渐变到接近半透明,这种方式看起来更加的优雅,但看起来只是一开始进入页面还没滚动的时候带着很漂亮的渐变色,一滚动的时候行为又和以前靠透明度的时候差不多。

咱们要改进的就是用滚动的距离来控制background-position,随着窗口的滚动,会慢慢改变渐变色渐变的程度而控制显隐效果。

样式

首先咱们先把header的样式写出来:

header {
/* 设置一个控制背景色位置的CSS变量,方便JS控制 */
--position: 100;
/* 居中靠下显示子元素 */
display: grid;
place-items: end center;
/* 设置为固定定位 */
position: fixed;
/* 距离上边左边为0 */
top: 0;
left: 0;
/* 宽度铺满屏幕 */
width: 100%;
/* 给个合适的高度 */
height: 40px;
/* 白色字体 */
color: white;
/* 字体大小 */
font-size: 16px;
/* 让字体细一点 */
font-weight: 100;
/* 增加下内变局,防止文字过于靠下 */
padding-bottom: 10px;
/* 设置过渡效果 */
transition: background-position .2s;
/* 黑色渐变背景 */
background: linear-gradient(black, rgba(148, 88, 88, 0.3) 80%, rgba(0, 0, 0, 0)) 0 calc(var(--position) * 1%) / 100% 300%;
}

运行结果:

可以看到我们在header里面设置了一条CSS变量,如果不太清楚什么是CSS变量的话请点击这里,这里还用到了 gird 来进行居中,当然并不是完完全全的居中,而是稍稍偏下了一点,因为我看天猫的小黑盒就是做的偏下居中。

整体实现

然后咱们开始写 JS 代码了,因为纯 CSS 的话还是无法获取屏幕的滚动距离,但是为了能够更方便的进行双方交互,我们还是采用了CSS变量,因为CSS变量带来的提升绝不仅仅是节约点 CSS 代码,以及降低 CSS 开发和维护成本这点作用。

更重要的是,把组件中众多的交互开发从原来的JS转移到了CSS代码中,让组件代码更简洁,同时让视觉表现实现更加灵活了。 ——张鑫旭

CSS变量具体能为我们带来哪些好处,可以参考张鑫旭的博客:

《CSS变量对JS交互组件开发带来的提升与变革》

来看一下CSS变量是怎么与 JS 进行交互的:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>属于自己的渐变小黑盒</title>
<style>
/* 清除默认样式 */
* { padding: 0; margin: 0 }
header {
/* 设置一个控制背景色位置的CSS变量,方便JS控制 */
--position: 100;
/* 居中靠下显示子元素 */
display: grid;
place-items: end center;
/* 设置为固定定位 */
position: fixed;
/* 距离上边左边为0 */
top: 0;
left: 0;
/* 宽度铺满屏幕 */
width: 100%;
/* 给个合适的高度 */
height: 40px;
/* 白色字体 */
color: white;
/* 字体大小 */
font-size: 16px;
/* 让字体细一点 */
font-weight: 100;
/* 增加下内变局,防止文字过于靠下 */
padding-bottom: 10px;
/* 设置过渡效果 */
transition: background-position .2s;
/* 黑色渐变背景 */
background: linear-gradient(black, rgba(148, 88, 88, 0.3) 80%, rgba(0, 0, 0, 0)) 0 calc(var(--position) * 1%) / 100% 300%;
}
main {
/* 给个合适的高度 */
height: 1000px;
}
</style>
</head>
<body>
<header>咱们自己的小黑盒</header>
<main></main>
<script>
// 获取header
const header = document.getElementsByTagName('header')[0]
addEventListener('scroll', () => {
// 获取偏移值
const top = document.documentElement.scrollTop
// 设置一个合适的范围
if (top <= 200) {
// 令header的渐变色位置变成计算后的渐变位置
header.style.setProperty('--position', 100 - Math.min(100, top))
} else {
// 在移动一定范围后令其完全不透明
header.style.setProperty('--position', 0)
}
})
</script>
</body>
</html>

运行结果:

想象一下如果不用CSS变量的话会变成什么样:

header.style.backgroundPosition = '0 ' + 100 - Math.min(100, top) + '%'

虽然看起来好像没啥的,但当要控制的属性较多时这将会是一种灾难,而且这种方式要时刻都带着 CSS 单位,像 px、%、rem 这些,为我们增加了不必要的心智负担,而且也拖慢了程序的运行效率。

用了CSS变量之后都不用带单位了,直接赋值一个数字即可,那么为什么可以不带单位呢?答案就在 calc 函数上:

calc( var(--position) * 1%)

记住在这里可不能再用 JS 的思维来写 calc 函数了,在 JS 里我们用的是+,因为这代表了字符串拼接,而在这里数字乘以百分之一,就会变成具体的百分数,同理如果你需要的是其他单位的话可以根据具体需求进行灵活修改:

calc( var(--position) * 1px)

️注意 px 前面那个1很重要,不能省略掉!

而且也不是必须要写1,还可以根据具体的需求按倍数来:

calc( var(--position) * 6.6rem)

扩展

其实从代码和我们日常生活中见到的效果可以得知,基本上滚动一段距离后header的透明度就固定住了不会再发生变化,浪费了监听 onscroll 事件。但又不能取消监听,因为不知道用户何时会再滑到顶部去,但这种情况却非常适合做另一种效果:

我忘记了这种效果叫啥来着,总之就是随着用户滚动页面的距离而在顶部显示一个类似于进度条之类的东西,方便用户得知自己处在网页中大概什么样的一种位置,来看代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>属于自己的渐变小黑盒</title>
<style>
/* 清除默认样式 */
* { padding: 0; margin: 0 }
header {
/* 设置一个控制位置的CSS变量,方便JS控制 */
--position: 0;
/* 设置为固定定位 */
position: fixed;
/* 距离上边左边为0 */
top: 0;
left: 0;
/* 宽度铺满屏幕 */
width: 100%;
/* 给个合适的高度 */
height: 10px;
/* 设置过渡效果 */
transition: transform .1s;
/* 渐变背景 */
background: linear-gradient(to right,#4481eb,#04befe);
/* 设置形变效果 */
transform: scaleX(var(--position));
/* 设置变形参照点 */
transform-origin: left;
}
main {
/* 给个合适的高度 */
height: 10000px;
/* 渐变背景 */
background: linear-gradient(#30cfd0,#330867)
}
</style>
</head>
<body>
<header></header>
<main></main>
<script>
// 获取header
const header = document.getElementsByTagName('header')[0]
addEventListener('scroll', () => {
// 获取偏移值
const top = document.documentElement.scrollTop
// 获取页面总高度
const height = document.documentElement.scrollHeight
// 设置CSS变量
if (top > height - document.documentElement.clientHeight - 1) {
header.style.setProperty('--position', 1)
} else {
header.style.setProperty('--position', top / height)
}
})
</script>
</body>
</html>

本文首发于微信公众号:《前端学不动》

版权声明
本文为[手撕红黑树]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000038139672

  1. [front end -- JavaScript] knowledge point (IV) -- memory leakage in the project (I)
  2. This mechanism in JS
  3. Vue 3.0 source code learning 1 --- rendering process of components
  4. Learning the realization of canvas and simple drawing
  5. gin里获取http请求过来的参数
  6. vue3的新特性
  7. Get the parameters from HTTP request in gin
  8. New features of vue3
  9. vue-cli 引入腾讯地图(最新 api,rocketmq原理面试
  10. Vue 学习笔记(3,免费Java高级工程师学习资源
  11. Vue 学习笔记(2,Java编程视频教程
  12. Vue cli introduces Tencent maps (the latest API, rocketmq)
  13. Vue learning notes (3, free Java senior engineer learning resources)
  14. Vue learning notes (2, Java programming video tutorial)
  15. 【Vue】—props属性
  16. 【Vue】—创建组件
  17. [Vue] - props attribute
  18. [Vue] - create component
  19. 浅谈vue响应式原理及发布订阅模式和观察者模式
  20. On Vue responsive principle, publish subscribe mode and observer mode
  21. 浅谈vue响应式原理及发布订阅模式和观察者模式
  22. On Vue responsive principle, publish subscribe mode and observer mode
  23. Xiaobai can understand it. It only takes 4 steps to solve the problem of Vue keep alive cache component
  24. Publish, subscribe and observer of design patterns
  25. Summary of common content added in ES6 + (II)
  26. No.8 Vue element admin learning (III) vuex learning and login method analysis
  27. Write a mini webpack project construction tool
  28. Shopping cart (front-end static page preparation)
  29. Introduction to the fluent platform
  30. Webpack5 cache
  31. The difference between drop-down box select option and datalist
  32. CSS review (III)
  33. Node.js学习笔记【七】
  34. Node.js learning notes [VII]
  35. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  36. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  37. 【JQuery框架,Java编程教程视频下载
  38. [jQuery framework, Java programming tutorial video download
  39. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  40. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  41. 【Vue,阿里P8大佬亲自教你
  42. 【Vue基础知识总结 5,字节跳动算法工程师面试经验
  43. [Vue, Ali P8 teaches you personally
  44. [Vue basic knowledge summary 5. Interview experience of byte beating Algorithm Engineer
  45. 【问题记录】- 谷歌浏览器 Html生成PDF
  46. [problem record] - PDF generated by Google browser HTML
  47. 【问题记录】- 谷歌浏览器 Html生成PDF
  48. [problem record] - PDF generated by Google browser HTML
  49. 【JavaScript】查漏补缺 —数组中reduce()方法
  50. [JavaScript] leak checking and defect filling - reduce() method in array
  51. 【重识 HTML (3),350道Java面试真题分享
  52. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  53. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  54. [re recognize HTML (3) and share 350 real Java interview questions
  55. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  56. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  57. 【重识 HTML ,nginx面试题阿里
  58. 【重识 HTML (4),ELK原来这么简单
  59. [re recognize HTML, nginx interview questions]
  60. [re recognize HTML (4). Elk is so simple