使用less的React项目的活跃图的实现问题

前言

产品要求实现一个类似github上的提交代码活跃图的日历格子,虽然觉得会很麻烦,但是需求到我手里,就得做了。

我使用了CSS的Grid布局,实现了展示上的需求,如下图所示,这会儿我还美滋滋的,觉得接下来会很顺利,没想到接下来和交互相关的部分,我陷入了深思

html结构

ts 复制代码
 <div className="contaniner">
        <ul className="weeks">
          {weeks.map((item, index) => {
            return <li key={index}>{item}</li>;
          })}
        </ul>
        <div>
          <ul className="graph">
            {infos.map((item, index) => {
              const classes = {};
              return (
                <Tooltip title={item?.year + '-' + item?.month + '-' + item?.date}>
                  <li
                    data-id={index}
                    onMouseDown={handleOnMouseDown}
                    onMouseUp={handleOnMouseUp}
                    data-level={item?.level}
                    key={index}
                    // style={pseElementStyle}
                    className="li-day li-new"
                  ></li>
                </Tooltip>
              );
            })}
          </ul>
          <ul className="months">
            {monthBar.map((item, index) => {
              return (
                <li  key={index} className="li-month">
                  {item.month}
                </li>
              );
            })}
          </ul>
        </div>
      </div>

CSS结构

less 复制代码
  .contaniner {
    display: flex;
    overflow-x: scroll;
    max-width: 740px;
    .weeks {
      display: grid;
      grid-template-rows: repeat(7, 28px); /*横向7列,21px宽*/
      margin: 20px 8px 5px 20px;
    }
    .graph {
      display: grid;
      grid-template-columns: repeat(auto-fill, 28px); /*竖向12列,21px宽*/
      grid-template-rows: repeat(7, 28px); /*横向7列,21px宽*/
      padding-inline-start: 0px;
      grid-auto-flow: column; /*生成7*12的格子后,设置为竖向排布*/
      margin: 20px 20px 5px 20px;
    }
    .li-day {
      width: 20px;
      height: 20px;
      list-style: none; 
      margin: 4px;
      border-radius: 4px;
    }
    .li-has-no-config {
      width: 20px;
      height: 28px;
      overflow: auto;
    }
    .select-rest {
      border: 1px solid #37caec;
    }
    .today-rest {
      border: 1px solid #255de6;
      color: #255de6;
    }
    .today-default {
      border: 1px solid #255de6;
      color: #255de6;
    }
    .today-config {
      border: 1px solid #255de6;
      color: #fff;
    }
    .click {
      border-radius: 4px;
      border: 1px solid #37caec;
      background: #255de6;
    }
    .today-click {
      background: #255de6;
      color: #ffff;
    }
    .graph li[data-level='1'] {
      background-color: #f5f6f7;
    }
    .graph li[data-level='2'] {
      background-color: rgba(37, 93, 230, 0.3);
    }
    .graph li[data-level='3'] {
      background-color: rgba(37, 93, 230, 0.7);
    }
    .months {
      display: grid;
      grid-template-columns: repeat(53, 28px);
      grid-template-rows: 28px;
      font-size: 12px;
      color: #aaa;
      padding-inline-start: 0px;
      margin: 5px 20px 5px 20px;
    }
    .li-month {
      display: inline-block;
    }
  }

产生的问题

接下来,我要在此基础上实现以下的两种交互:

就是既保持底部的grid格子的颜色,在鼠标点击,并一直按下的情况下,选中一些格子,并且给选中的格子添加一些背景色或者是边框。

我现在的项目中用的样式的预处理器是less, 项目的框架是React

我思考实现上面的交互,考虑了两个方案,以下就从这两个方案来分析一下,为什么上面这个交互没有办法实现。

方案一

通过鼠标的点击事件开始和结束,去判断开始和结束的小格子的index,然后通过判断小格子是不是在开始和结束的index的范围内,就添加一个新的className的类型li-selected

css 复制代码
 .li-selected::before {
      content: '';
      position: absolute;
      width: 20px;
      height: 28px;
      margin: 0;
      background-color: rgba(55, 202, 236, 0.5); /* 添加的叠加层颜色 */
    }

这种方法存在的问题是,从我在上面的截图,很清晰的看到,我要展示的是12个月份,由于布局的空间问题,还会有横向的滑动,用这种方法就会导致li-select的样式在li-day进行滑动的时候,就会和li-day的元素错位,所以这种方法pass

方案二

既然我使用了grid,那我就直接用nth去表示吧,我写了如下的样式

css 复制代码
 .li-day:nth-child(n + 12):nth-child(-n + 21)::before {
      content: '';
      position: absolute;
      width: 20px;
      height: 28px;
      margin: 0;
      background-color: rgba(255, 0, 0, 0.5); /* 添加的叠加层颜色 */
    }

上面的样式表示我给第12个到第21个的方块上方添加了一层红色的叠加,这时候考虑到我们鼠标点击的时候起始的结束的位置都是不确定的,所以,结合less, 我打算使用less中的变量:

less 复制代码
@start-index: 12;
@end-index: 26;
.generate-nth-child-styles(@start, @end) when (@start < @end) {
      .li-day:nth-child(n + @{start}):nth-child(-n + @{end}) {
        // 样式定义
        background-color: #ccc;

        &::before {
          content: '';
          position: absolute;
          width: 20px;
          height: 28px;
          margin: 0;
          background-color: rgba(255, 0, 0, 0.5); /* 添加的叠加层颜色 */
        }
      }
    }

    // /* 调用样式生成函数 */
    .generate-nth-child-styles(@start-index, @end-index);

现在我的问题是怎么动态的去给我的样式的开始和结束赋值,但是经过我的一番研究,存在以下矛盾:

  • Less 中的变量是在编译时静态地解析的,这意味着它们在 Less 文件编译成 CSS 文件的过程中就已经确定了其最终的值,并不能在运行时动态地改变。
  • 在React中使用内联样式是无法表示::before这样的伪类的

当然,我也在积极的尝试使用其他的方法,比如说,我尝试直接使用css,使用css里面的变量,但是css的变量在nth-child()中是不生效的。虽然说可以使用styled-components在内联样式里面表示伪类,但是为了程序的某一个小部分去替换less,我觉得不合理,所以我没有尝试这种方法

结尾

如果大家有其他的实现方式也可以在评论区给我积极评论,以上的内容就是我的一个思考的过程。

相关推荐
知识分享小能手5 小时前
React学习教程,从入门到精通,React AJAX 语法知识点与案例详解(18)
前端·javascript·vue.js·学习·react.js·ajax·vue3
NeverSettle_9 小时前
React工程实践面试题深度分析2025
javascript·react.js
学前端搞口饭吃10 小时前
react reducx的使用
前端·react.js·前端框架
努力往上爬de蜗牛10 小时前
react3面试题
javascript·react.js·面试
开心不就得了10 小时前
React 进阶
前端·javascript·react.js
谢尔登10 小时前
【React】React 哲学
前端·react.js·前端框架
学前端搞口饭吃12 小时前
react context如何使用
前端·javascript·react.js
GDAL12 小时前
为什么Cesium不使用vue或者react,而是 保留 Knockout
前端·vue.js·react.js
Dragon Wu1 天前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
YU大宗师1 天前
React面试题
前端·javascript·react.js