1. 程式人生 > >用Antd和React寫的Antd介紹網站,原始碼有詳細註釋

用Antd和React寫的Antd介紹網站,原始碼有詳細註釋

git倉庫地址 :https://github.com/mingChen2017/Antd-React-example

演示地址:39.108.54.113/antd


這是一個非常簡單的例子,利用了AntD和react,希望能幫助初學者學習,特別是在ModuleDescribe.js,即元件->元件介紹這一頁面有非常多詳細的註釋,後續我會繼續完善。

import React from 'react';
import QueueAnim from 'rc-queue-anim';
import PropTypes from 'prop-types';
import TweenOne, { TweenOneGroup } from 'rc-tween-one';
import Icon from 'antd/lib/icon';
import './moduleDescribe.css'


const textData = {
    //title: 'General',
};
//資料陣列,把頁面的顯示層再分為資料和邏輯
let dataArray = [
    {
        title: 'General',
        background: '#ffd8d8',
        subTitle:'按鈕和圖示',
        //如果在這裡要用css的符號實現分行或者是其他功能,那麼需要加上一個父級標籤
        //這裡的寫法還不夠純淨,作為model層應該只存放資料而不包括html語句,我個人認為合理的做法是把這部分資料設定成一個數組,在ViewMode層再遍歷後轉成html
        content:
            <div>
                <h3>	•按鈕</h3>
                <p></p>
                <h3>	•圖示(icon):提供了上百個符合antd設計規範的圖示</h3>
            </div>
    },
    {
        title: 'Layout',
        background: '#fed7ff',
        subTitle:'展示',
        content:
            <div>
                <p>	•柵格</p>
                <p>	•佈局:Layout Header Sider Content Footer</p>
            </div>
    },
    {
        title: 'Navigation' ,
        background: '#d3d1ff',
        subTitle:'導航',
        content:
            <div>
                <p>	•固釘:將頁面元素釘在可視範圍。</p>
                <p>	•麵包屑:顯示當前頁面在系統層級結構中的位置,並能向上返回。</p>
                <p>	•下拉選單</p>
                <p>	•導航選單</p>
                <p>	•分頁</p>
                <p>	•步驟條:引導使用者按照流程完成任務的導航條</p>
            </div>
    },
    {
        title: 'Data Entry' ,
        background: '#afdbe1',
        subTitle:'資料輸入',
        content:
            <div>
                <p>	•自動完成:輸入框自動完成功能</p>
                <p>	•級聯選擇</p>
                <p>	•多選框</p>
                <p>	•日期選擇框</p>
                <p>	•表單</p>
                <p>	•數字輸入框</p>
                <p>	•輸入款</p>
                <p>	•提及</p>
                <p>	•評分</p>
                <p>	•單選框</p>
                <p>	•滑動輸入條</p>
                <p>	•開關</p>
                <p>	•樹選擇</p>
                <p>	•時間選擇框</p>
                <p>	•穿梭框</p>
                <p>	•上傳</p>
            </div>
    },
    {
        title: 'Data Display' ,
        background: '#bee1c5',
        subTitle:'資料展示',
        content:
            <div>
                <p>	•頭像</p>
                <p>	•微標數:參考微信小紅點</p>
                <p>	•日曆</p>
                <p>	•卡片</p>
                <p>	•走馬燈</p>
                <p>	•摺疊面板</p>
                <p>	•氣泡卡片</p>
                <p>	•文字提示:移到文字上會顯示簡單的文字提示氣泡框。</p>
                <p>	•表格</p>
                <p>	•標籤頁</p>
                <p>	•標籤</p>
                <p>	•時間戳</p>
                <p>	•樹形控制元件</p>
            </div>},
    {
        title: 'Feedback' ,
        background: '#ecffd8',
        subTitle:'反饋',
        content:<div>
            <p>	•警告提示</p>
            <p>	•對話方塊</p>
            <p>	•全域性提示</p>
            <p>	•通知提醒框</p>
            <p>	•進度條</p>
            <p>	•氣泡確認框</p>
            <p>	•載入中</p>
        </div>},
    {
        title: 'Other' ,
        background: '#e1d2ba',
        subTitle:'其它',
        content:<div>
            <p>	•錨點:用於跳轉到頁面指定位置。</p>
            <p>	•回到頂部</p>
            <p>	•國際化:翻譯</p>
        </div>},
];

//遍歷陣列錄入資料
dataArray = dataArray.map(item => ({ ...item, ...textData }));


class ModuleDescribe extends React.Component {
    //限制prop的型別,一般對外部傳來的引數需要做型別驗證
    static propTypes = {
        className: PropTypes.string.isRequired,
    };
    //設定預設props
    /*
    [
      {
        name: 'Zachary He',
        age: 13,
        married: true,
      },
      {
        name: 'Alice Yo',
        name: 17,
      },
      {
        name: 'Jonyu Me',
        age: 20,
        married: false,
      }
    ]
    propTypes: {
        myArray: React.PropTypes.arrayOf(
            React.propTypes.shape({
                name: React.propTypes.string.isRequired,
                age: React.propTypes.number.isRequired,
                married: React.propTypes.bool
            })
        )
    }
   */
    static defaultProps = {
        className: 'pic-details-demo',
    };

    constructor(props) {
        super(props);
        /*
        假如我們是以<ModuleDescribe></ModuleDescribe>呼叫的
            this.props.children拿到的是兩個標籤中間的子節點
         */
        //console.error("this.props.children:"+this.props.children)
        this.state = {
            /*
                使用classnames來動態修改或者新增多個className
                react原生動態新增多個className會報錯:
                import style from './style.css'
                <div className={style.class1 style.class2}</div>
                想要得到最終渲染的效果是:
                <div class='class1 class2'></div>
                引入classnames庫,安裝:
                npm install classnames --save
                使用:
                import classnames from 'classnames'

                <div className=classnames({
                    'class1': true, //這裡的true可以省略
                    'class2': true
                    )>
                </div>

                其他用法:
                    可以傳入陣列物件:
                        var arr = ['b', { c: true, d: false }];
                        classNames('a', arr); // => 'a b c'
                    可以傳入動態class
                        let buttonType = 'primary';
                        classNames({ [`btn-${buttonType}`]: true });
               */

            /*
                區域性state會讓
             */
            picOpen: {},
        };
    }

//元件被點選後的動畫
    onImgClick = (e, i) => {
        const { picOpen } = this.state;
        Object.keys(picOpen).forEach((key) => {
            //檢查是否有其他開啟的狀態的視窗,如果有就關閉
            if (key !== i && picOpen[key]) {
                picOpen[key] = false;
            }
        });
        picOpen[i] = true;
        //更改狀態,重新渲染
        this.setState({
            picOpen,
        });
    };

//元件被關閉的動畫
    onClose = (e, i) => {
        const { picOpen } = this.state;
        picOpen[i] = false;
        //更改狀態,重新渲染
        this.setState({
            picOpen,
        });
    };
    //關閉動畫執行完畢
    onTweenEnd = (i) => {
        const { picOpen } = this.state;
        //刪除picOpen[i]  delete picOpen[i]  結果為: ["a",undefined,"c","d"],在下面會通過判斷型別來推斷它是否被開啟
        delete picOpen[i];
        this.setState({
            picOpen,
        });
    };


    getDelay = (e) => {
        //console.error("getDelay:"+e.index)
        const i = e.index + dataArray.length % 4;
        console.error("e.index:"+e.index);
        return (i % 4) * 100 + Math.floor(i / 4) * 100 + 200;

    };

    getType = (e) => {
        //console.error("getType:"+e.index)
        const animationType=["alpha","left","right","top","bottom","scale","scaleBig","scaleX","scaleY"];
        return animationType[4];
    }
    getChildren = () => {
        const imgWidth = 300;
        const imgHeight = 200;
        const imgBoxWidth = 330;
        const imgBoxHeight = 220;
        //console.error("getLiChildren prepare")
        //map()方法建立一個新陣列,其結果是該陣列中的每個元素都呼叫一個提供的函式後返回的結果。每個元素都是回撥函式的結果。
        return dataArray.map((item, i) => {
            //console.error("getLiChildren begin:"+i)
            const { background,title, subTitle, content } = item;
            const isEnter = typeof this.state.picOpen[i] === 'boolean';
            //讀取該視窗是否被開啟
            const isOpen = this.state.picOpen[i];

            const left = isEnter ? 0 : imgBoxWidth * (i % 4);
            const imgLeft = isEnter ? imgBoxWidth * (i % 4) : 0;
            const isRight = false;
            const isTop = Math.floor(i / 4);
            let top = isTop ? (isTop - 1) * imgBoxHeight : 0;
            top = isEnter ? top : imgBoxHeight * isTop;
            let imgTop = isTop ? imgBoxHeight : 0;
            imgTop = isEnter ? imgTop : 0;
            //這個是點選後彈出的左邊視窗的屬性
            const liStyle = isEnter ? { width: '100%', height: 600, zIndex: 1 } : null;
            //點選後的陰影動畫
            const liAnimation = isOpen ?
                ({ boxShadow: '0 2px 8px rgba(140, 140, 140, .35)',repeat:1}) :
                ({ boxShadow: '0 0px 0px rgba(140, 140, 140, 0)' ,repeat:1});
            let aAnimation = isEnter ?
                ({
                    delay: 400,//新點選的目標移動後就目標多久回去
                    ease: 'easeInOutCubic',
                    width: imgWidth,
                    height: imgHeight,
                    onComplete: this.onTweenEnd.bind(this, i),
                    left: imgBoxWidth * (i % 4),
                    top: isTop ? imgBoxHeight : 0,
                }) : null;
//假如這個圖案是被點選的是什麼效果
            aAnimation = isOpen ?
                ({
                    ease: 'easeInOutCubic',
                    left: isRight ? (imgBoxWidth * 2) - 10 : 0,
                    width: '50%',
                    height: '100%',
                    top: 0,
                }) : aAnimation;

// 位置 js 控制;
            return (
                /*該部分是進場動畫*/
                <TweenOne
                    key={i}
                    style={

                        {

                            left,
                            top,
                            ...liStyle,
                        }
                    }
                    //component是css標籤的意思,比如在這裡用了li的話,在頁面實際上會生成一對<li></li>標籤
                    component="li"
                    className={isOpen ? 'open' : ''}
                    animation={liAnimation}
                >
                    <TweenOne
                        component="a"
                        onClick={e => this.onImgClick(e, i)}
                        style={{
                            left: imgLeft,
                            top: imgTop,
                            width: 300,
                            height: 200
                        }}
                        //aAnimation會經過兩層判斷,先是判斷是否進入的動畫,再判斷是否點選的動畫 {item.title}
                        animation={aAnimation}
                    >
                        {/*我是一段註釋*/}

                        {/*<--圖片會隨著緩慢變大,不是因為給圖片設定了動效,是因為圖片的width為100%,所以它會一直隨著外面的容器變大,實際上TweenOne是一個隱形的容器,假如在這裡用div畫了一個長方形,不會有特效。-->*/}
                        <div className="my-rectangle" style={{background }}>{title}</div>

                    </TweenOne>



                    {/*<控制右邊頁面的動畫-->*/}
                    <TweenOneGroup
                        enter={[
                            {
                                opacity: 0, duration: 0, type: 'from', delay: 400,
                            },
                            { ease: 'easeOutCubic', type: 'from', left: isRight ? '60%' : '0%' },
                        ]}
                        leave={{ ease: 'easeInOutCubic', left: isRight ? '60%' : '0%' }}
                        component=""
                    >
                        {isOpen && (
                            <div
                                className={`${this.props.className}-text-wrapper`}
                                key="text"
                                style={{
                                    left: isRight ? '0%' : '50%',
                                }}
                            >
                                <h1>
                                    {subTitle}
                                </h1>
                                <Icon type="cross" onClick={e => this.onClose(e, i)} />
                                {/*<!--這個標籤表示標題下面的橫線,雖然我也不知道為什麼不是<em></em>-->*/}
                                <em />
                                <p>{content}</p>
                            </div>
                        )}
                    </TweenOneGroup>
                </TweenOne>
            );
        });
    };

    render() {
        return (

            <div>
                {/*<!--this.getLiChildren()每一次返回,QueueAnim標籤都會執行一次,為每一個return分配delay component className....通過列印this.getDelay可以證明-->*/}
                {/*QueueAnim裡面函式是依個全部執行完才執行下一個,例如下面會先執行六次this.getDelay,再執行六次this.getType>,根據實驗,delay會影響後面this.getType的執行順序,
                由於antd是非同步載入,會先執行this.getLiChildren(),再執行QueueAnim裡的函式
                */}
                {/*<!-- QueueAnim會執行括號內的函式*/}
                <QueueAnim
                    delay={this.getDelay}
                    component="ul"
                    className={`${this.props.className}-image-wrapper`}
                    type={this.getType}
                    duration={2000}
                >
                    {/*在這裡我們拿到的是一個數組,但是編譯器會自動拼接陣列的所有元素,驗證的辦法是 這裡可以寫成this.getChildren()[0],那麼螢幕只會顯示一個子元件*/}
                    {this.getChildren()}
                    {console.error(this.getChildren)}
                </QueueAnim>

            </div>

        );
    }
}
export default ModuleDescribe;