这几日学习了 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介绍。
 
- 提示下载完但解压或打开不了?
 
- 找不到素材资源介绍文章里的示例图片?
 
- 模板不会安装或需要功能定制以及二次开发?
 
                    
    
发表评论
还没有评论,快来抢沙发吧!