【前端-组件】手摸手实现一个基于css的文本跟随的展开收起组件-bysking

152 阅读1分钟

一、背景介绍

大家好,我是bysking, 废话不多说,直接上干货,实现一个展开收起组件,本期你chen哥直接手摸手实现一个基于css+js的展开收起组件

二、效果展示

  • 收起状态 image.png

  • 展开状态 image.png

使用

  <MoreText
    text={text2}
    line={3}
    btnRender={({ toggle, isOpen }) => (
      <a onClick={() => toggle?.()}>{isOpen ? '收起' : '展开'}</a>
    )}
  ></MoreText>

三、老规矩直接上代码:

3.1 组件目录结构

image.png

3.2 more-text/renderBtn/index.tsx

import { ReactNode, useEffect, useState } from 'react';

type typeProps = {
  btnRender?: (params: {
    isOpen: boolean;
    text: string;
    toggle: () => void;
  }) => ReactNode;
  onClickToggle: () => void;
  isAll: boolean;
  text: string;
  refWrap;
};
const RenderBtn = (props: typeProps) => {
  const [show, setShow] = useState(false);

  const init = () => {
    let parentNode = props.refWrap.current;
    if (parentNode) {
      let scrollHeight = parentNode.scrollHeight;
      let offsetHeight = parentNode.offsetHeight;
      if (scrollHeight - offsetHeight > 2) {
        setShow(true);
      } else {
        setShow(false);
      }
    }
  };

  useEffect(() => {
    init();
  }, []);

  return (
    <div
      style={{
        display: show ? 'block' : 'none',
        float: 'right',
        clear: 'both',
      }}
    >
      {props.btnRender ? (
        props.btnRender?.({
          isOpen: props.isAll,
          text: props.text,
          toggle: props.onClickToggle,
        })
      ) : (
        <a
          onClick={() => {
            props.onClickToggle();
          }}
        >
          {props.isAll ? '收起' : '展开'}
        </a>
      )}
    </div>
  );
};

export default RenderBtn;

3.3 more-text/index.less

.more-text-wrap {
  overflow: hidden;
  text-overflow: ellipsis;

  /* stylelint-disable-next-line value-no-vendor-prefix */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  position: relative;
  word-wrap: break-all;
  text-align: justify;
  white-space: normal;
}

3.4 more-text/index.tsx

import { ReactNode, useEffect, useRef, useState } from 'react';
import './index.less';
import RenderBtn from './renderBtn';

type typeProps = {
  text: string;
  line?: number;
  btnRender?: (params: {
    isOpen: boolean;
    text: string;
    toggle: () => void;
  }) => ReactNode;
};

const RenderMoreText = (props: typeProps) => {
  const text = props.text;
  const [line, setLine] = useState(props.line);
  const refWrap = useRef<HTMLDivElement>();

  const initStyle = (line: number) => {
    let dom = document.querySelector('.more-text-wrap');
    if (dom) {
      dom.style['-webkit-line-clamp'] = line;
    }
  };

  const isAll = line !== props.line;
  const onClickToggle = () => {
    let maxLine = 9999;
    setLine(isAll ? props.line : maxLine);
  };

  useEffect(() => {
    initStyle(line);
  }, [line]);

  useEffect(() => {
    setLine(props.line);
  }, [props.line]);

  return (
    <div>
      <div style={{ display: 'flex' }}>
        <div className="more-text-wrap" ref={refWrap}>
          <div
            style={{
              content: '',
              height: 'calc(100% - 22px)', // 减去按钮高度
              float: 'right',
            }}
          ></div>
          <RenderBtn
            btnRender={props.btnRender}
            isAll={isAll}
            text={props.text}
            onClickToggle={onClickToggle}
            refWrap={refWrap}
          />
          {String(text)}
        </div>
      </div>
    </div>
  );
};

export default RenderMoreText;

四、回顾用法

import MoreText from './more-text';

<MoreText
    text={text2}
    line={3}
    btnRender={({ toggle, isOpen }) => (
      <a onClick={() => toggle?.()}>{isOpen ? '收起' : '展开'}</a>
    )}
></MoreText>

看到这里,欢迎点赞收藏加关注走一波,持续更新干货