使用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,我觉得不合理,所以我没有尝试这种方法

结尾

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

相关推荐
凹凸曼打不赢小怪兽2 小时前
react 受控组件和非受控组件
前端·javascript·react.js
鑫宝Code3 小时前
【React】状态管理之Redux
前端·react.js·前端框架
2401_857610037 小时前
深入探索React合成事件(SyntheticEvent):跨浏览器的事件处理利器
前端·javascript·react.js
fighting ~8 小时前
react17安装html-react-parser运行报错记录
javascript·react.js·html
老码沉思录9 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
老码沉思录9 小时前
React Native 全栈开发实战班 - 状态管理入门(Context API)
javascript·react native·react.js
老码沉思录12 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
老码沉思录12 小时前
React Native 全栈开发实战班 - 第四部分:用户界面进阶之动画效果实现
react native·react.js·ui
奔跑草-18 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
林太白1 天前
❤React-React 组件通讯
前端·javascript·react.js