这几日学习了 svg 相关知识,想到曾经有同事说 win 10 日历背景特效狠厉害。当时还没有学习 svg,仅用 HTML + CSS 实现的话感觉非常复杂。这几日看了大部分 svg 相关知识后,觉得使用 svg 来实现的话会稍微简单些。
点击 win 10 右下角的日期,用鼠标在日历间移动时,可以看到背景使用了一个径向渐变效果,并且这个渐变仅作用在边框上。
html + css 实现
如果使用 HTML + CSS,那么就需要在径向渐变的背景上使用 HTML 遮盖渐变,使其仅留出边框的区域。这听起来挺勉强的,实现起来也是一样,这将为每个日期框生成一个冗余的元素。因为是使用 HTML 元素对径向渐变进行覆盖,并配色上就不那么灵活,如果遇到背景是一张图片,那么这个将难以实现。
假定背景为纯黑的情况下,使用 HTML + CSS 实现如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.root div {
pointer-events: none;
}
.root {
background: #000;
width: 500px;
height: 500px;
position: relative;
}
.gradient {
position: absolute;
left: 20px;
top: 30px;
width: 200px;
height: 200px;
background: radial-gradient(rgba(200, 200, 200, 1), rgba(200, 200, 200, 0) 75%, rgba(200, 200, 200, 0));
}
.root div:not(.gradient) {
position: relative;
z-index: 1;
box-sizing: border-box;
width: 100px;
height: 100px;
float: left;
padding: 10px;
background: content-box #000;
border: 2px solid #000;
}
</style>
</head>
<body>
<div id="root" class="root">
<div id="gradient" class="gradient"></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<script>
const root = document.getElementById("root");
const gradient = document.getElementById("gradient");
root.onmousemove = function (e) {
gradient.style.left = e.offsetX - 100 + "px";
gradient.style.top = e.offsetY - 100 + "px";
};
</script>
</body>
</html>
使用 svg 实现
使用 svg 实现会变得很自然,因为 svg 是可以把径向渐变绘制到日期边框所在区域上。
我们只会要把日期框路径绘制出来,然后填充径向渐变即可。因为日期框是规则的,所以只需要一个二重循环即可生成路径。
详细代码如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<svg id="Calendar" version="1.1" xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 70 70">
<defs>
<style type="text/css">
rect {
pointer-events: all;
}
</style>
<radialGradient id="Linear-1" cx="0.2" cy="0.5" r="0.2">
<stop stop-color="#bbb" stop-opacity="1" offset="0" />
<stop stop-color="#bbb" stop-opacity="0" offset="1" />
</radialGradient>
<g id="boxs">
<rect x="0" y="0" width="100%" height="100%" fill="#000" />
</g>
</defs>
<use href="#boxs" />
<path stroke="#eee" stroke-width="0.5" />
</svg>
<script>
(function () {
const NS = "http://www.w3.org/2000/svg";
const boxs = document.getElementById("boxs");
// 行数
const rows = [..."1234567"];
// 列数
const dayLabels = [..."日一二三四五六"];
function createSVGEl(name) {
return document.createElementNS(NS, name);
}
function setElAttrbute(obj, attrs) {
Object.keys(attrs).forEach((key) => obj.setAttribute(key, attrs[key]));
}
// 渐变路径
const pathBox = createSVGEl("path");
// 日期框数组,用来高亮每个日期框
const rectBoxItems = [];
// 整理路径
const d = rows.reduce(function (prev, _, r) {
var dRow = dayLabels.reduce(function (prev, _, i) {
var x = i * 10 + 0.5;
var y = r * 10 + 0.5;
// 顺带生成日期框
const rectBoxItem = createSVGEl("rect");
setElAttrbute(rectBoxItem, {
x,
y,
width: 9,
height: 9,
"stroke-width": 0.5,
fill: "transparent",
});
// :hover 伪类不生效,只能使用事件高亮
rectBoxItem.onmouseenter = () => (rectBoxItem.style.stroke = "#ddd");
rectBoxItem.onmouseout = () => (rectBoxItem.style.stroke = "");
rectBoxItems.push(rectBoxItem);
return (prev += `M ${x} ${y} l 9 0 l 0 9 l -9 0 z `);
}, "");
return (prev += dRow);
}, "");
// 使用渐变渲染路径框
setElAttrbute(pathBox, {
d,
stroke: "url(#Linear-1)",
"stroke-width": 0.5,
});
// 挂载路径和日期框
boxs.appendChild(pathBox);
rectBoxItems.forEach((v) => boxs.appendChild(v));
// 获取日历背景和渐变
const Calendar = document.getElementById("Calendar");
const Linear1 = document.getElementById("Linear-1");
// 让渐变跟随日历内的鼠标移动
Calendar.addEventListener("mousemove", function (e) {
const { width, height } = e.target.getBoundingClientRect();
const x = e.offsetX / width;
const y = e.offsetY / height;
requestAnimationFrame(function () {
Linear1.setAttribute("cx", x);
Linear1.setAttribute("cy", y);
});
});
})();
</script>
</body>
</html>
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!