React:FrontendWiki前端维基项目&&用纯CSS实现树

2022-08-01 15:01:42 浏览数 (1)

15号的时候,来到了字节青训营进阶班,课程结题要完成一个项目

首先很幸运,我们的组长来自北大研院,从未与北大学子共事过,今日闻名不如人,真真实实感受到思维上的不一样之处,从思维角度就让我或多或少有些成长。

项目说明

FrontendWiki,如其名,是前维基,通过收集网络上大前端相关相关学习链接综合起来,形成方便用户进行前端学习的网站

项目技术框架

  • 前端在初期有考虑使用React Ts Sass的整体技术框架,不过最终定下来使用React Css module
  • 后端采用SQL数据库 微服务的形式

What I do?

一棵树

真真切切的树,这是一颗从左向右生长的树

本来预估这里要用canvas或是svg实现,而且样式也不是这样。预估比较难,所以我和SH是一起接手了主页部分。 后来...后来,我通过递归的方式,再辅以css的巧妙应用,于是最终通过div css完成了hhh

大致做法如下:

  • TreeNode:写一个节点组件,这里通过class的方式写组件
代码语言:javascript复制
render(){
return(
<div className={styles.nodeContain}>
        <div>
          {this.props.data.content}
        </div>
      </div>
      )
   }

styles.nodeContain即为css moudle的基本使用方法 这里是一个基本框架,那么我们之后要逐步实现这个组件,现在先写树组件

  • Tree:预估逻辑会比较复杂,所以仍然使用class 那这个Tree怎么写?
  1. 首先我们应该确认一下数据结构,构建一个方便递归的数据,这部分数据理论上从云端调用,所以直接写一个专门获取数据的方法
代码语言:javascript复制
getTreeData() {
    axios
      .get("xxx")
      .then((res) => {
        this.setState({
          data: res,
        });
      })
      .catch((e) => {
        this.setState({
          data: [
            {
              content: "CSS",
              id: "1",
              level: 0,
              childrens: [
                {content: "动画属性",id: "1-1",level: 1,
                  childrens: [
                  {content: "transition",id: "1-1-1",level: 2,childrens: [],},
                  {content: "animation",id: "1-1-2",level: 2,childrens: [],},
                  {content: "贝塞尔函数",id: "1-1-3",level: 2,childrens: [],},
                  ],
                },
                { content: "选择器", id: "1-2", level: 1, childrens: [] },
              ],
            },
            {content: "HTML",id: "2",level: 0,
            childrens: [
                {content: "标签集",id: "2-1",level: 1,
                  childrens: [
                    { content: "div", id: "2-1-1", level: 2, childrens: [] },
                  ],
                },
                { content: "规范", id: "2-2", level: 1, childrens: [] },
              ],
            },
            { content: "React", id: "3", level: 0, childrens: [] },
            { content: "JS", id: "4", level: 0, childrens: [] },
          ],
        });
      });
  }

这个结构可以说是非常清晰了,每个节点包含这些数据

  • content,代表显示的内容,如CSS
  • id,用来作为key的参数
  • level,代表重要程度,在树中会有样式的差异
  • childrens(语义上这里不应该有s...),子节点列表
  1. 接下来就是通过递归生成树
代码语言:javascript复制
getNode(data) {
    return data.map((item) => {
      return (
        <div key={item.id} className="flexRowNone">
          <TreeNode
            data={item}
          />
          <div>{this.getNode(item.childrens)}</div>
        </div>
      );
    });
  }

这里即为简单的递归生成,其中flexRowNone的样式如下,简单的flex row,确定树是向右生长,如果改竖直的话这里用column,后边也有地方需要修改

代码语言:javascript复制
.flexRowNone {
  display: flex;
  flex-direction: row;
}
  1. 上一步我们实现了树,不过现在的树没有样式 我们回到TreeNode,给TreeNode确定样式,非常简单的实现
代码语言:javascript复制
.nodeContain{
    position: relative;
    display: flex;
    margin-left: .6rem;
    margin-top: .9rem;
}

完成这一步你就会发现,成功生成了一颗树,当然它现在没有任何样式

  1. 锦上添花

我希望用户的点击可以在相关区域生成一定的点按反馈,这样可以有效提升用户反馈

代码语言:javascript复制
class TreeNode extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      point: false,
      top: 0,
      left: 0,
    };
  }
  pointShow(e) {
    let { layerX, layerY, offsetX, offsetY } = e.nativeEvent;
    this.setState({
      top: layerY || offsetY,
      left: layerX || offsetX,
      point: true,
    });
    setTimeout(() => {
      this.setState({
        point: false,
      });
    }, 600);
  }
  render() {
    return (
      <div className={styles.nodeContain}>
        <div
          onClick={(e) => {
            this.pointShow(e);
          }}
          style={styleMap[this.props.data.level]}
        >
          {this.state.point ? (
            <div
              className={styles.pointCircle}
              style={{ top: this.state.top, left: this.state.left }}
            ></div>
          ) : (
            <></>
          )}
          {this.props.data.content}
        </div>
      </div>
    );
  }
}

上边的 {this.state.point ? ( <div className={styles.pointCircle} style={{ top: this.state.top, left: this.state.left }} ></div> ) : ( <></> )} 实际上就是点按反馈的核心代码;下边则是相关动态的css

代码语言:javascript复制
.pointCircle{
    border: solid .6px rgba(20, 20, 20, 0.24);
    width: .1px;
    height: .1px;
    position: absolute;
    border-radius: 100%;
    animation: showPonitCircle .6s ease;
    z-index: 10;
}
@keyframes showPonitCircle{
    to{transform: scale(100);opacity: 0;}
}

实现后效果

光树是不行滴,现在需要处理点击树节点后的弹出

这一块的代码和Tree之间没有很大的关系,分开处理增强组件复用性

  • 预估实现效果
  • 在最外层控制弹出

对应样式我就不放出来了,这里通过控制选中id来控制组件的展示

  • 其中左下角的小球实现相对简单,可以简单陈述,其他效果最好看我源代码

可以确定的是,我把它放到了最外层

代码语言:javascript复制
export var shareFunc={

}
function Home() {
  const [headShow,setHeadShow]=useState(true);
  const [showParcel, setParcel] = useState(false);
  shareFunc.setParcel=setParcel;
  return (
    <>
      {showParcel ? <div className={styles.parcel}></div> : <></>}
      {
        headShow?<HomeHead />:<></>
      }
      <HomeMain setHeadShow={setHeadShow}/>
    </>
  );
}

export default Home;
代码语言:javascript复制
.parcel{
  position: fixed;
  height: 100%;
  border-radius: 100%;
  border: solid .6px #000;
  width: 100%;
  width: .1px;
  height: .1px;
  bottom: 0;
  left: 0;
  z-index: 100000;
  animation: parcelShow ease 1s forwards;
}
@keyframes parcelShow {
  50%{transform: scale(3000);opacity: 1;}
  100%{transform: none;opacity: 0;}
}
复制代码

这里通过setParcel控制小球的显隐,为了方便各个地方调用, 我通过shareFunc将它导出

于是在在其他地方点击后就可以通过以下方式控制动画效果,这一块其实可以再次封装

代码语言:javascript复制
import {shareFunc} from '../Home.jsx';

onClick=()=>{
        shareFunc.setParcel(true);
        setTimeout(()=>{
           shareFunc.setParcel(false);
        },1500)
}

上边主要都是UI,现在则是数据块,通过redux获取并存储数据

这里简单说说点击树节点后要获取标签列表的示例

Reducer
代码语言:javascript复制
const initState = {
  linkList:[]
};
const linkReducer = (state = initState, action) => {
  console.log('state',state)
  switch (action.type) {
    case "getLinkListStart":
    case "getLinkListSuccess":
    case "getLinkListFail":
      state.linkList={...state.linkList, ...action.payload};
      return {...state};
    default:
      return state;
  }
};
export default linkReducer;

Reducer在redux中提供对于Action的处理,具体可以看看下边的action

Action
代码语言:javascript复制
/**
 * @author source
 * @updateTime 2021/8/20 16:00
 * 通过词条id获得词条下的链接列表;
 * @param term {String}
 * 非必填
 */
const getLinkListAction = (term) => (dispatch) => {
  getLinksbyTerm(term)
    .then((res) => {
      dispatch({
        type: "getLinkListSuccess",
        payload: {
          [`${term}`]: {
            data: { ...res },
            code: 1,
          },
        },
      });
    })
    .catch((e) => {
      dispatch({
        type: "getLinkListFail",
        payload: {
          [`${term}`]: {
            data: { ...e },
            code: -1,
          },
        },
      });
      console.log("store", store.getState().linkReducer);
    });
};

综合Reducer和Action你就会知道,我们触发Action后,Reducer会接收到数据,通过type来处理对应数据更新到Store中,这里通过动态的key来确保每次数据都不会覆盖

结尾

其实项目难度不高,不过很有意义和价值,网络上确实缺少前端大汇总这种网站,希望每个前端都能找到自己的路

0 人点赞