CSS自定义样式运用

4 阅读2分钟

背景:

需要绝对定位某一个元素,但是元素的right值需要根据另一个元素的宽度动态变化;

方案

  1. 获取到某一个元素的固定宽度
  2. 将变量传到less文件中

实现

a. 获取元素的宽度

    const headerRightRef = useRef<HTMLDivElement>(null);
  const setHeaderRightWidth = useUaiHeaderLayout((s) => s.setHeaderRightWidth);

  useEffect(() => {
    const el = headerRightRef.current;
    if (!el) return;

    const update = () => {
      setHeaderRightWidth(Math.round(el.getBoundingClientRect().width));
    };

    update();

    if (typeof ResizeObserver !== 'undefined') {
      const ro = new ResizeObserver(() => update());
      ro.observe(el);
      return () => ro.disconnect();
    }

    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, [setHeaderRightWidth]);
  
  return (<div><div ref={headerRightRef} className={styles['header-right-wrapper']}>....</div></div>)
import { create } from 'zustand';

type HeaderLayoutState = {
headerRightWidth: number;
setHeaderRightWidth: (width: number) => void;
};

const useUaiHeaderLayout = create<HeaderLayoutState>((set) => ({
headerRightWidth: 0,
setHeaderRightWidth: (width) => set({ headerRightWidth: width }),
}));

export default useUaiHeaderLayout;

2.将宽度定义成变量传到样式文件.less文件中

const headerRightWidth = useUaiHeaderLayout((s) => s.headerRightWidth);
...
return
 (
  <div
      className={
        mainAppHeaderRightWidthNumber != null
          ? `${styles['communication-notification']} ${styles['communication-notification--main-right']}`
          : styles['communication-notification']
      }
      style={
        mainAppHeaderRightWidthNumber != null
          ? ({
              ['--main-app-header-right-width' as unknown as keyof CSSProperties]: `${headerRightWidth}px`,
            } as CSSProperties)
          : undefined
      }
    >
...
    </div>)
    
.communication-notification--main-right {
right: var(--main-app-header-right-width, 415px);
}

原理

上面的可以改成下面的写法:

 <div
    style={
      mainAppHeaderRightWidthNumber != null
        ? ({
            '--main-app-header-right-width': `${headerRightWidth}px`,
          } as CSSProperties)
        : undefined
    }
  >
  </div>)
  • 核心是css自定义属性(css 自变量),你在React中把一个"变量"写到元素的style上,然后在.less里有var(--xxx)读出来,用来控制right。
  • 上面代码做了两件事 *1. 在这个组件根节点上“声明一个 CSS 变量”
style={{
  ['--main-app-header-right-width' as ...]: '123px'
}}

等价于在 HTML 上这样写:

<div style="--main-app-header-right-width: 123px;"></div>
  1. 在 CSS 里“读取这个变量”并用它参与布局
.communication-notification--main-right {
  right: var(--main-app-header-right-width, 415px);
}

含义是:right 优先用 --main-app-header-right-width 的值;如果没定义,就用默认 415px(这是 var() 的 fallback)。

常见使用方法

1) 动态主题色(按钮/高亮)

<div style={{'--primary':'#1677ff'} as React.Cssproperties} className={styles.box}>
.box{
border-color: var(--primary);
color:var(--primary)
}

2) 动态尺寸:侧边栏宽度影响内容区

<div style={{ '--sidebar-w': '280px' } as React.CSSProperties} className={styles.layout} />
.layout {
  padding-left: var(--sidebar-w);
}ar(--primary)
}

3) 动态做“计算”:用 calc() 拼装

<div style={{ '--w': '360px' } as React.CSSProperties} className={styles.pop} />
.pop {
  right: calc(var(--w) + 12px); // 在 CSS 里加偏移
}

4) 动画时长/延迟由业务控制

<div style={{ '--dur': '200ms' } as React.CSSProperties} className={styles.anim} />
.anim {
  transition: opacity var(--dur) ease;
}

5) 同一个变量控制多个子元素(继承作用域)

<div style={{ '--gap': '8px' } as React.CSSProperties} className={styles.list}>
  ...
</div>
.list { gap: var(--gap); }
.list :global(.ant-btn) { margin-left: var(--gap); }

只要在父元素上定义 --x,子元素都能用(CSS 变量会沿 DOM 向下“继承”):