步驟條的實(shí)現(xiàn)

開(kāi)始之前

今天公司的一個(gè)項(xiàng)目里面有一個(gè)步驟條要實(shí)現(xiàn),這個(gè)項(xiàng)目前端是基于 Vue 和 ElementUI開(kāi)發(fā)。餓了么UI里面是有步驟條(step)這個(gè)組件的,之前的一個(gè)項(xiàng)目是有用過(guò)這個(gè)東西,當(dāng)時(shí)是基于餓了么的那個(gè)組件封裝了一層,做那個(gè)的時(shí)候也挺費(fèi)勁的。今天這個(gè)組件還復(fù)雜一些,再去封裝感覺(jué)還要費(fèi)很長(zhǎng)時(shí)間,衡量了一下還是自己寫(xiě)一個(gè)來(lái)的比較直接,以后自己修改也方便一些。

代碼開(kāi)擼

開(kāi)擼之前先放一張項(xiàng)目里步驟條的圖看看長(zhǎng)什么樣子。


步驟條大致就是這個(gè)樣子里,上面的標(biāo)記大家可以忽略,上面是用一個(gè)前端開(kāi)發(fā)輔助軟件標(biāo)注的,我會(huì)在末尾的時(shí)候告訴大家這個(gè)軟件。

步驟條的功能大家都知到,比如說(shuō)一個(gè)審批流程,創(chuàng)建人發(fā)起,然后到直屬領(lǐng)導(dǎo)審批,直屬領(lǐng)導(dǎo)審批完成了直屬領(lǐng)導(dǎo)的節(jié)點(diǎn)就會(huì)由白色變成一個(gè)藍(lán)色,連接創(chuàng)建人和直屬領(lǐng)導(dǎo)之間的連線(xiàn)也會(huì)由虛線(xiàn)變成實(shí)線(xiàn),后面的節(jié)點(diǎn)依次類(lèi)推,步驟條大致就是這些功能了。

因?yàn)椴煌?xiàng)目對(duì)與生成步驟條的接口的數(shù)據(jù)結(jié)構(gòu)不一樣,所以先不考慮通過(guò)拿到后臺(tái)接口的數(shù)據(jù)去渲染步驟條,咱們先放一張靜態(tài)的頁(yè)面看看怎么去實(shí)現(xiàn)。

<ul class="steps">
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步驟一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步驟一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步驟三</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步驟二</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
    </ul>

通過(guò)這個(gè)DOM樹(shù)的結(jié)構(gòu)相信做過(guò)前端的朋友應(yīng)該大致猜到了要實(shí)現(xiàn)我們項(xiàng)目的要求應(yīng)該如何控制渲染。步驟條節(jié)點(diǎn)數(shù)據(jù)必定要有一個(gè)表示狀態(tài)的標(biāo)志,我們通過(guò)這個(gè)標(biāo)志來(lái)判斷是否該給 li 節(jié)點(diǎn)添加 active 這個(gè)類(lèi),達(dá)到點(diǎn)亮節(jié)點(diǎn)的效果。而節(jié)點(diǎn)數(shù)據(jù)的其他部分就是負(fù)責(zé)渲染步驟條下面的信息。我們的項(xiàng)目中有一個(gè)?,這個(gè)就是觸發(fā)一個(gè)事件,至于什么樣的事件,咱們?cè)诶锩嫣砑右粋€(gè)圖標(biāo),然后再在圖標(biāo)上放上一個(gè) click 事件,根據(jù)業(yè)務(wù)需求執(zhí)行相關(guān)操作就可以了。

那么我們?cè)趺慈?shí)現(xiàn)節(jié)點(diǎn)點(diǎn)亮和連線(xiàn)的效果呢?那當(dāng)然還是靠 CSS 樣式去渲染它。那么我們把 CSS 的代碼放上來(lái)。

<style lang="scss" scoped>
    .steps{
        position: relative;
        list-style: none;
        text-align: center;
        counter-reset: step;
    }
    .steps li{
        float: left;
        display: inline-block;
        width: 20%;
        text-align: center;
        height: auto;
        position: relative;
    }
        /* 控制節(jié)點(diǎn)那個(gè)圓圈的形成 */
    .steps li:before{
        counter-increment: step;
        content: counter(step);
        display: block;
        height: 20px;
        width: 20px;
        font-size:0;
        border-radius: 20px;
        border:1px solid #6d99ff;
        background-color: #fff;
        margin: 0 auto;
        line-height: 20px;
        text-align: center;
        margin-bottom: 10px;
    }
        /* 控制節(jié)點(diǎn)之間的連線(xiàn) */
    .steps li ~ li:after{
        content: "";
        width: 100%;
        border: 1px dotted #8db0ff;
        position: absolute;
        top:10px;
        left: calc(-40% - 25px);
        z-index: -1;
    }
        /* 控制節(jié)點(diǎn)點(diǎn)亮的效果渲染 */
    .steps li.active:before{
        background-color: #1a5fff;
        box-shadow:0 0 0 2px #bacfff;
    }
    .steps li.active:after{
        border: 1px solid #8db0ff
    }
        /* 控制節(jié)點(diǎn)下面的信息樣式渲染  */
    .steps .text{
        position: relative;
    }
    .steps .text .el-col, .steps .text .el-col div{
        height: 28px !important;
    }
    .steps .text .el-col{
        position: relative;
        left: -14px;
    }
    .steps .text .el-col:first-child{
        text-align: right;
        height: 28px;
        line-height: 28px;
        padding-right: 10px;
    }
    .steps .text .el-col:last-child {

        & > div {
            margin-bottom: 10px;
            & > span {
                float: left;

                &:last-child {
                    height: 28px;
                    line-height: 28px;
                    padding-left: 10px;
                }
            }
        }
    }
</style>

關(guān)于步驟條這塊其實(shí)主要還是樣式的問(wèn)題。寫(xiě)過(guò)前端的一看這些代碼肯定是都了解,但是如果有沒(méi)做過(guò)前端或者對(duì)前端還沒(méi)那么熟悉的不知什么機(jī)緣巧合看到了這篇日記(表示自己很幸運(yùn),感謝)我班門(mén)弄斧的稍微說(shuō)一我當(dāng)時(shí)的思路,ul li 的那一部分是前端常見(jiàn)清除原生樣式,自己手寫(xiě)過(guò)菜單生成代碼的朋友對(duì)這塊應(yīng)該很熟悉。這一部分就是通過(guò) list-style清除無(wú)序列表前面的那個(gè)小黑點(diǎn),然后將 li 做成行內(nèi)塊級(jí)元素,加一個(gè)浮動(dòng),使原來(lái)的豎排列變成行排列。

然后生成節(jié)點(diǎn)的時(shí)候我們使用 :before 偽元素,這個(gè)屬性的作用是在目標(biāo)元素的內(nèi)容前面插入一個(gè)虛擬的元素,它其實(shí)不是真實(shí)存在的,不會(huì)出現(xiàn)DOM中,不會(huì)改變文檔內(nèi)容,不可復(fù)制,僅僅是在CSS渲染出加入。所以它經(jīng)常用做修飾行的內(nèi)容,比如說(shuō)添加個(gè)圖標(biāo)什么的。它必須通過(guò)content屬性來(lái)添加內(nèi)容,然后根據(jù)設(shè)置不同元素形式(塊級(jí),行內(nèi)元素等),來(lái)設(shè)置其它的屬性。這個(gè)地方我設(shè)置了 content ,然后將這個(gè)偽類(lèi)設(shè)置成一個(gè)塊級(jí)元素,設(shè)置寬高。對(duì)于怎么成一個(gè)圓形哪就是 border-radius。我這里設(shè)置的是相同的寬高,是一個(gè)正方形,我們做成一個(gè)圓只要將 broder-radius 設(shè)置成超過(guò)正方形邊長(zhǎng)的一半即可?,F(xiàn)在說(shuō)說(shuō)這個(gè)content值的設(shè)置,其實(shí)相關(guān)的counter-reset 和 counter-increment 這兩個(gè)屬性我平時(shí)很少用到,上次做步驟條看 element-ui 樣式表的時(shí)候發(fā)現(xiàn)了這兩個(gè)屬性。這兩個(gè)屬性的作用創(chuàng)建計(jì)數(shù)器和遞增計(jì)數(shù)器,常用來(lái)排序替代用有序表來(lái)排序。咱們這里其實(shí)是用來(lái)生成節(jié)點(diǎn)的 1 2 3 4 數(shù)字,這樣如果要顯示這些數(shù)字的我們就不用程序去控制了。最后面我會(huì)放上頁(yè)面的最終渲染效果,發(fā)現(xiàn)沒(méi)有顯示這些數(shù)字是因?yàn)槲以谏蓤A圈那段CSS代碼中把font-size設(shè)置成了0,大家注掉會(huì)發(fā)現(xiàn)里面的數(shù)字。

其他地方需要需要注意的屬性是 box-shadow,生成盒子的陰影,我們的很多項(xiàng)目都會(huì)帶有陰影效果(我也不知道為什么??),我會(huì)經(jīng)常用到,要在周?chē)纬梢蝗﹃幱?,大家只要把這個(gè)屬性前三個(gè)屬性都設(shè)置為0 ,第四個(gè)屬性設(shè)置一下想要陰影的大小,然后給個(gè)顏色就可以了。后面的連線(xiàn)就是使用 :after 位元素,生成一個(gè)沒(méi)有寬高,只有邊框的。寬度為整個(gè)父級(jí)寬度一跟線(xiàn),它其實(shí)是連接兩個(gè)圓心的。連線(xiàn)這里有一個(gè)算是比較巧妙的地方,就是你看到我的代碼里設(shè)置 :after 偽類(lèi)的時(shí)候我是從第二個(gè) li 元素開(kāi)始的, 就是 li ~ li:after這個(gè)代碼,如果我們不這樣設(shè)置,當(dāng)我們給第二個(gè) li元素添加 active 類(lèi)名時(shí),節(jié)點(diǎn)后面的線(xiàn)也會(huì)點(diǎn)亮,而且最后一個(gè)節(jié)點(diǎn)還會(huì)有一個(gè)尾巴,那就不是一個(gè)步驟條正確的效果。既然從第二條開(kāi)始那怎么連接第一個(gè)節(jié)點(diǎn)呢,就是改變定位方式,然后加一個(gè)偏移量就搞定了。因?yàn)楦讣?jí)li元素是一個(gè)相對(duì)定位,我們這個(gè)偽元素設(shè)置成絕對(duì)定位時(shí)參照的是相對(duì)父級(jí)的絕對(duì)定位。

關(guān)于下面信息的展示,就沒(méi)啥可以說(shuō)的了,借助element的柵格,然后計(jì)算一下位置的偏移量,最終到達(dá)我們想要的效果就可以了。下面例子靜態(tài)頁(yè)面想象了最多有5個(gè)節(jié)點(diǎn),所以每個(gè)li元素寬度是20%,如果需要更多的節(jié)點(diǎn)就需要?jiǎng)討B(tài)調(diào)整這個(gè)寬度。下面放上最終的效果圖:

image

寫(xiě)在最后

這篇日記寫(xiě)完,發(fā)現(xiàn)跟我開(kāi)發(fā)這個(gè)組件用的時(shí)間幾乎差不多。再寫(xiě)一遍這個(gè)思路的時(shí)候腦子會(huì)重現(xiàn)出當(dāng)時(shí)寫(xiě)出來(lái)的BUG,然后對(duì)自己不了解的一些屬性會(huì)重新鞏固一遍。上面寫(xiě)的不一定很好,朋友們?nèi)绻l(fā)現(xiàn)不合適的地方還請(qǐng)?jiān)谙旅媪粞裕戎x謝大家的批評(píng)指正了。

最后說(shuō)一下開(kāi)始項(xiàng)目截圖里面用的軟件,是一個(gè)叫 PxCook 的軟件,它分為設(shè)計(jì)和開(kāi)發(fā)模式,只要我們把高保真圖放進(jìn)去就可以測(cè)量出每個(gè)區(qū)域的寬度,取色也很方便,會(huì)生成一些代碼輔助開(kāi)發(fā)。最后的最后附上這個(gè)實(shí)例的全部代碼。

<template>
    <ul class="steps">
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步驟一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步驟一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步驟三</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步驟二</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部門(mén)經(jīng)理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
    </ul>
</template>

<script>
    export default {
        name: "Steps",
        data(){
            return {
                imgUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png'
            }
        }
    }
</script>

<style lang="scss" scoped>
    .steps{
        position: relative;
        list-style: none;
        text-align: center;
        counter-reset: step;
    }
    .steps li{
        float: left;
        display: inline-block;
        width: 20%;
        text-align: center;
        height: auto;
        position: relative;
    }
    .steps li:before{
        counter-increment: step;
        content: counter(step);
        display: block;
        height: 20px;
        width: 20px;
        font-size:0;
        border-radius: 20px;
        border:1px solid #6d99ff;
        background-color: #fff;
        margin: 0 auto;
        line-height: 20px;
        text-align: center;
        margin-bottom: 10px;
    }
    .steps li ~ li:after{
        content: "";
        width: 100%;
        border: 1px dotted #8db0ff;
        position: absolute;
        top:10px;
        left: calc(-40% - 25px);
        z-index: -1;
    }
    .steps li.active:before{
        background-color: #1a5fff;
        box-shadow:0 0 0 2px #bacfff;
    }
    .steps li.active:after{
        border: 1px solid #8db0ff
    }
    .steps .text{
        position: relative;
    }
    .steps .text .el-col, .steps .text .el-col div{
        height: 28px !important;
    }
    .steps .text .el-col{
        position: relative;
        left: -14px;
    }
    .steps .text .el-col:first-child{
        text-align: right;
        height: 28px;
        line-height: 28px;
        padding-right: 10px;
    }
    .steps .text .el-col:last-child {

        & > div {
            margin-bottom: 10px;
            & > span {
                float: left;

                &:last-child {
                    height: 28px;
                    line-height: 28px;
                    padding-left: 10px;
                }
            }
        }
    }
</style>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??DOM 1 級(jí)主要定義的是 HTML 和 XML 文檔的底層結(jié)構(gòu)。 ??DOM2 和 DOM3 級(jí)則在這個(gè)結(jié)構(gòu)...
    霜天曉閱讀 1,599評(píng)論 1 3
  • feisky云計(jì)算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 4,355評(píng)論 0 5
  • 前端開(kāi)發(fā)面試題 面試題目: 根據(jù)你的等級(jí)和職位的變化,入門(mén)級(jí)到專(zhuān)家級(jí),廣度和深度都會(huì)有所增加。 題目類(lèi)型: 理論知...
    怡寶丶閱讀 2,686評(píng)論 0 7
  • 一:在制作一個(gè)Web應(yīng)用或Web站點(diǎn)的過(guò)程中,你是如何考慮他的UI、安全性、高性能、SEO、可維護(hù)性以及技術(shù)因素的...
    Arno_z閱讀 1,365評(píng)論 0 1
  • ??DOM(文檔對(duì)象模型)是針對(duì) HTML 和 XML 文檔的一個(gè) API(應(yīng)用程序編程接口)。 ??DOM 描繪...
    霜天曉閱讀 3,866評(píng)論 0 7

友情鏈接更多精彩內(nèi)容