15-实战篇:实战大操作-变量控件
juejin_logo copyCreated with Sketch.

前言

在第8章使用整章介绍变量,变量作为CSS体系中最高逼格的特性,随着浏览器日益完善,变量可大范围在项目中使用,无需关注其兼容性。

虽说变量可在CSS中起到领头羊的作用,但变量的设计初衷是为了更便利CSSJS间的联系。CSS使用变量有如下好处。

  • 减少样式代码的重复性
  • 增加样式代码的扩展性
  • 提高样式代码的灵活性
  • 增加一种CSSJS的通讯方式
  • 无需深层遍历DOM改变某个样式

本章的主题是变量控件,主要是基于变量与JS通讯简化基于JS逻辑的效果。常见控件有放大镜滚动渐变背景

放大镜

传统的放大镜效果需依赖很多JS逻辑,移动与显示的效果都依赖JS,通过JS计算偏移量再渲染样式。

本次使用变量简化这些JS逻辑,将计算偏移量的逻辑整合到变量中,还记得第7章的calc()吗?calc()用于动态计算单位,是本次改造的核心用法。

放大镜

基于上述需求,实时获取鼠标的左偏移量上偏移量,而这两个偏移量是相对父节点的。通过左偏移量上偏移量结合calc()计算放大镜显示内容相对父节点的显示位置。

event提供以下偏移量,若不了解其概念很易发生混淆。

  • screenX/screenY:相对屏幕区域左上角定位,若发生滚动行为,则相对该区域定位
  • pageX/pageY:相对网页区域左上角定位
  • clientX/clientY:相对浏览器可视区域左上角定位
  • offsetX/offsetY:相对父节点区域左上角定位,若无父节点则相对<html><body>定位

理解这些偏移量概念,发现offsetX/offsetY最符合需求,所以使用其作为放大镜显示内容相对父节点的显示位置。

<div class="magnifier"></div>
const magnifier = document.getElementsByClassName("magnifier")[0];

magnifier.addEventListener("mousemove", e => {
	e.target.style.setProperty("--x", `${e.offsetX}px`);
	e.target.style.setProperty("--y", `${e.offsetY}px`);
});

接着使用Scss构建放大镜效果。放大镜显示内容其实就是将原图像放大N倍,通过上述偏移量根据比例截取一定区域显示内容。

先定义相关Scss变量。设定放大倍率为2倍,那被放大图像的宽高也是原来宽高的2倍。声明两个变量,分为--x--y

$ratio: 2;
$box-w: 600px;
$box-h: 400px;
$box-bg: "https://jowayyoung.github.io/static/img/icss/gz.jpg";
$outbox-w: $box-w * $ratio;
$outbox-h: $box-h * $ratio;
.magnifier {
	--x: 0;
	--y: 0;
	overflow: hidden;
	position: relative;
	width: $box-w;
	height: $box-h;
	background: url($box-bg) no-repeat center/100% 100%;
	cursor: pointer;
}

还记得第9章的伪元素吗?在该场景中很明显无需加入子节点作为放大镜的容器,使用::before即可。

放大镜在使用时宽高为100px,不使用时宽高为0px。通过绝对定位布局放大镜随鼠标移动的位置,声明lefttop,再声明transform:translate(-50%,-50%)将放大镜补位,使放大镜中心与鼠标光标位置一样。因为声明lefttop定位放大镜的位置,那还需声明will-change改善lefttop因改变而引发的性能问题。

.magnifier {
	&::before {
		--size: 0;
		position: absolute;
		left: var(--x);
		top: var(--y);
		border-radius: 100%;
		width: var(--size);
		height: var(--size);
		box-shadow: 1px 1px 3px rgba(#000, .5);
		content: "";
		will-change: left, top;
		transform: translate(-50%, -50%);
	}
	&:hover::before {
		--size: 100px;
	}
}

接着使用background实现放大镜显示内容。根据放大倍率为2倍,声明size:$outbox-w $outbox-h,再声明position-xposition-y移动背景,最终连写为background: #333 url($box-bg) no-repeat $scale-x $scale-y/$outbox-w $outbox-h,而$scale-x$scale-y相应position-xposition-y,用于随着鼠标移动而改变背景位置。

水平方向偏移量 = offsetX * 倍率 - 放大镜宽度 / 倍率
垂直方向偏移量 = offsetY * 倍率 - 放大镜高度 / 倍率

基于第10章的background-position正负值问题,上述两条公式需乘以-1,则变成以下公式。

水平方向偏移量 = 放大镜宽度 / 倍率 - offsetX * 倍率
垂直方向偏移量 = 放大镜高度 / 倍率 - offsetY * 倍率

此时将两条公式代入到$scale-x$scale-y两个Scss变量中,若在calc()中使用Scss变量,需使用#{}的方式包括Scss变量,否则会根据字符串的方式解析。

$scale-x: calc(var(--size) / #{$ratio} - #{$ratio} * var(--x));
$scale-y: calc(var(--size) / #{$ratio} - #{$ratio} * var(--y));

最终的scss文件如下。

$ratio: 2;
$box-w: 600px;
$box-h: 400px;
$box-bg: "https://jowayyoung.github.io/static/img/icss/gz.jpg";
$outbox-w: $box-w * $ratio;
$outbox-h: $box-h * $ratio;
.magnifier {
	--x: 0;
	--y: 0;
	overflow: hidden;
	position: relative;
	width: $box-w;
	height: $box-h;
	background: url($box-bg) no-repeat center/100% 100%;
	cursor: pointer;
	&::before {
		--size: 0;
		$scale-x: calc(var(--size) / #{$ratio} - #{$ratio} * var(--x));
		$scale-y: calc(var(--size) / #{$ratio} - #{$ratio} * var(--y));
		position: absolute;
		left: var(--x);
		top: var(--y);
		border-radius: 100%;
		width: var(--size);
		height: var(--size);
		background: #333 url($box-bg) no-repeat $scale-x $scale-y/$outbox-w $outbox-h;
		box-shadow: 1px 1px 3px rgba(#000, .5);
		content: "";
		will-change: left, top;
		transform: translate(-50%, -50%);
	}
	&:hover::before {
		--size: 100px;
	}
}

滚动渐变背景

使用移动端时可能会发现某些网页在滚动时,顶部背景色会发生细微的变化,该效果一直随着滚动距离的变化而变化。

滚动渐变背景

有了上述示例作为铺垫,可清楚知道变量结合鼠标事件能完成更多的酷炫效果,而关键点是把鼠标的某些参数(例如偏移量)赋值到变量中,让变量随着鼠标参数的变化而变化。了解该技巧,就能开发出很多变量与JS通讯的动画关联效果与事件响应效果。

因为本示例与滚动有关,那毫不犹豫地想起event.target.scrollTop,监听滚动事件并将event.target.scrollTop赋值到变量中。另外,当滚动距离超过一定值时需做一些限制,例如背景颜色不再发生变化。

<div class="dynamic-scroll-bg">
	<header></header>
	<main>
		<div>
			<p>网易公司(NASDAQ:
				NTES),1997年由创始人兼CEO丁磊先生在广州创办,2000年在美国NASDAQ股票交易所挂牌上市,是中国领先的互联网技术公司。在开发互联网应用、服务及其它技术方面,始终保持中国业界领先地位。本着对中国互联网发展强烈的使命感,缔造美好生活的愿景,网易利用最先进的互联网技术,加强人与人之间信息的交流和共享。
			</p>
			<p>网易公司推出了门户网站、在线游戏、电子邮箱、在线教育、电子商务、在线音乐、网易bobo等多种服务。网易在广州天河智慧城的总部项目计划2019年1月建成,网易游戏总部将入驻。2016年,游戏业务营业收入在网易总营收中占比73.3%。2011年,网易杭州研究院启用。网易传媒等业务在北京。网易在杭州上线了网易考拉海购、网易云音乐等项目。
			</p>
			<p>网易2019全年财报显示,网易公司2019年全年净收入为592.4亿元;基于非美国通用会计准则,归属于网易公司股东的持续经营净利润为156.6亿元。</p>
			<p>2019年,网易深入推进战略聚焦,坚守内容消费领域,积极布局游戏、教育、音乐、电商等核心赛道,取得重大突破。在保持稳健增长的同时,网易有道、创新及其他等业务板块爆发强大潜力,为未来的长期发展提供源源不断的动能。
			</p>
		</div>
	</main>
</div>
.dynamic-scroll-bg {
	--scrolly: 250;
	overflow: hidden;
	position: relative;
	border: 1px solid #66f;
	width: 400px;
	height: 400px;
	header {
		--angle: calc(var(--scrolly) * 2deg);
		--size: calc(1500px - var(--scrolly) * 2px);
		--x: calc(var(--size) / 2 * -1);
		--y: calc(var(--scrolly) * -1px);
		--ratio: calc(50% - var(--scrolly) / 20 * 1%);
		position: absolute;
		left: 50%;
		bottom: 100%;
		margin: 0 0 var(--y) var(--x);
		border-radius: var(--ratio);
		width: var(--size);
		height: var(--size);
		background-color: #3c9;
		filter: hue-rotate(var(--angle));
		animation: rotate 5s linear infinite;
	}
	main {
		overflow: auto;
		position: relative;
		width: 100%;
		height: 100%;
		div {
			padding: 300px 20px 50px;
		}
		p {
			line-height: 1.2;
			text-align: justify;
			text-indent: 2em;
		}
	}
}
@keyframes rotate {
	to {
		transform: rotate(1turn);
	}
}
const bg = document.getElementsByClassName("dynamic-scroll-bg")[0];
const content = bg.getElementsByTagName("main")[0];

content.addEventListener("scroll", e => {
	const top = e.target.scrollTop;
	if (top <= 250) {
		bg.style.setProperty("--scrolly", 250 - top);
	} else {
		bg.style.setProperty("--scrolly", 0);
	}
});

留言
Ctrl + Enter
全部评论(5)
母雞抖的头像
删除
問個:
var(--size) / #{$ratio}
應該除以2就好了吧?變成var(--size) / 2
因為都是減掉半徑
点赞
回复
二把刀gz的头像
删除
后端工程师 @ 待业
笔者是网易的大神吗?多次出现网易字眼和广州地标小蛮腰
点赞
3
删除
(作者)
是呀,网易人,坐标广州,顺便宣传下网易和广州,哈哈
1
回复
删除
网易坑我钱了,我玩了网易的游戏,网易游戏太费钱了😭今天从大佬你这get到知识点了,我要去网易把钱挣回来,能推一下吗😢
是呀,网易人,坐标广州,顺便宣传下网易和广州,哈哈
2
回复
查看更多回复