elementui原始碼學習之仿寫一個el-switch
本篇文章記錄仿寫一個 el-switch
元件細節,從而有助於大家更好理解餓了麼ui對應元件具體工作細節。本文是elementui原始碼學習仿寫系列的又一篇文章,後續空閒了會不斷更新並仿寫其他元件。原始碼在github上,大家可以拉下來,npm start執行跑起來,結合註釋有助於更好的理解。github倉庫地址如下: https://github.com/shuirongsh...
switch元件思考
元件功能作用
switch元件
一般是表示開關狀態或者兩種狀態之間的切換,如點選開啟網站的夜間模式,或關閉夜間模式。如下圖vue官網首頁就有這樣的操作功能:
vue官網連結地址: https://cn.vuejs.org/
元件的結構
switch元件
的結構還是比較簡單的,主要分為兩部分:
switch元件切換小圓點按鈕 switch元件切換容器
元件的實現思路
基本的 switch
切換佈局結構
在實現 switch元件
的時候, switch元件切換容器
可以直接畫一個 div
去表示,那麼 switch元件切換小圓點按鈕
我們也需要畫一個 div
嗎?其實不用的。
- 我們可以使用
偽元素
先畫出一個切換小圓點按鈕
(結合定位) - 然後需要定義一個
標識布林值
,用於更改切換元件開啟關閉狀態
- 當狀態變化的時候,去更改
切換小圓點按鈕
在左側或在右側的位置(通過class),即實現了切換功能 - 再加上過渡效果,這樣的話,
switch元件
的切換(開啟關閉)就會很絲滑了
開啟關閉 switch元件
的說明文字功能注意事項
如下圖:
- 關於開啟時候文字在左側,關閉時候文字在右側,也開始可以通過彈性盒樣式控制
justifyContent:flex-start / flex-end;
,當然動態padding
也要加上,詳情見程式碼 - 若將文字加入切換框內部,那麼就需要讓切換框背景容器dom的寬度自適應,即根據內容文字的多少來控制,所以要提到
width: fit-content;屬性
(使用fit-content屬性,讓寬度隨著內容文字多少自適應)
關於 fit-content 詳情見官方文件: https://developer.mozilla.org...
給偽元素加上hover效果的寫法
給偽元素加懸浮效果是先hover再::after(不要搞反了)
正確寫法: .target:hover::after { background-color: red; }
錯誤寫法!!!: .target::after:hover { background-color: red; }
這裡舉一個例子程式碼效果圖,複製貼上即可使用,如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { padding: 120px; } .target { display: inline-block; width: 60px; height: 18px; background-color: #c4c4c4; border-radius: 10px; cursor: pointer; transition: all 0.3s; position: relative; } /* 使用偽元素畫一個小圓點 */ .target::after { content: ""; position: absolute; top: -4px; left: -2px; border-radius: 50%; width: 24px; height: 24px; border: 1px solid #e9e9e9; box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.3); background-color: #fff; transition: all 0.3s; } /* 給自己加懸浮效果直接寫即可 */ .target:hover { background-color: green; } /* 給偽元素加懸浮效果是先hover再::after(不要搞反了) */ .target:hover::after { background-color: red; } </style> </head> <body> <div class="target"></div> </body> </html>
關於封裝的 mySwitch元件
的其他的東西,結合筆者的註釋,就可以清晰的理解了。這個元件主要還是樣式的動態控制。
另,筆者封裝的元件暫不搭配 el-form
的校驗使用,後續寫到表單校驗時,會補上並更新github上的程式碼倉庫中
當然部分寫法效果,筆者的方案和官方的還是略有不同,畢竟思路略有不同,也建議讀者自己嘗試仿寫封裝哦
封裝的元件
效果圖
筆者的gif錄屏軟體不太好,道友朋友們有沒有不錯的gif錄製軟體推薦一下 ^_^
複製貼上即可使用哦
使用程式碼
<template> <div> <my-divider lineType="dotted" content-position="left">普通使用</my-divider> <my-switch @change="change" v-model="flag1"></my-switch> <my-switch v-model="flag2"></my-switch> <my-divider lineType="dotted" content-position="left" >開啟關閉文字</my-divider > <my-switch v-model="flag3" openText="開啟啦開啟啦" closeText="關閉了"></my-switch> <my-switch v-model="flag3" openText="ON" closeText="OFF"></my-switch> <my-switch v-model="flag3" openText="✔" closeText="✘"></my-switch> <my-divider lineType="dotted" content-position="left" >自定義開啟關閉背景色</my-divider > <my-switch v-model="flag4" active-color="#19be6b" inactive-color="#ed4014" ></my-switch> <my-divider lineType="dotted" content-position="left">禁用</my-divider> <my-switch v-model="flag5" disabled></my-switch> <my-switch v-model="flag6" disabled></my-switch> <br /> <my-divider lineType="dotted" content-position="left" >small切換框</my-divider > <my-switch v-model="flag7" active-color="#006CFF" inactive-color="#DD6DA6" openText="small" closeText="switch" size="small" ></my-switch> <my-divider lineType="dotted" content-position="left">big切換框</my-divider> <my-switch v-model="flag8" active-color="#2F2F2F" inactive-color="#ddd" openText="☾" closeText="☼" size="big" ></my-switch> </div> </template> <script> export default { data() { return { flag1: true, flag2: false, flag3: true, flag4: true, flag5: false, flag6: true, flag7: true, flag8: true, }; }, methods: { change(val) { console.log("切換後的狀態", val); }, }, }; </script>
封裝程式碼
參考註釋,建議自己封裝適合自己公司業務的 switch元件
<template> <div class="mySwitchWrap" :class="[disabled ? 'disabledSwitch' : '', size]" @click="changeStatus" > <!-- input標籤 --> <input class="switchInput" type="checkbox" @change="changeStatus" ref="input" :true-value="activeValue" :false-value="inactiveValue" :disabled="disabled" @keydown.enter="changeStatus" /> <!-- 主要內容 --> <span :class="[ 'switchCentre', 'circleDotLeft', isOpen ? 'changeCircleDotRight' : '', ]" :style="{ background: computedBackground, borderColor: computedBackground, }" > <span class="text" :style="{ justifyContent: isOpen ? 'flex-start' : 'flex-end', padding: isOpen ? '0 28px 0 8px' : '0 8px 0 28px', }" >{{ isOpen ? openText : closeText }}</span > </span> </div> </template> <script> export default { name: "mySwitch", props: { openText: String, closeText: String, // v-model搭配value接收資料,this.$emit("input", val)更新資料 value: { type: Boolean, default: false, // 預設false }, // 是否禁用,預設不禁用 disabled: { type: Boolean, default: false, }, // switch開啟時為true activeValue: { type: Boolean, default: true, }, // switch關閉時為false inactiveValue: { type: Boolean, default: false, }, // 自定義switch開啟時背景色 activeColor: { type: String, default: "", }, // 自定義switch關閉時背景色 inactiveColor: { type: String, default: "", }, // switch切換框的大小 size: { type: String, default: "", }, }, computed: { // 是否開啟切換框取決於外層傳遞的v-model的值是否為true isOpen() { return this.value === this.activeValue; }, computedBackground() { // 若傳遞了啟用顏色和未啟用顏色,就根據是否開啟狀態使用傳遞的顏色 if ((this.activeColor.length > 0) & (this.inactiveColor.length > 0)) { return this.isOpen ? this.activeColor : this.inactiveColor; } // 沒傳遞就根據開啟使用預設的背景色 else { return this.isOpen ? "#409EFF" : "#C0CCDA"; } }, }, methods: { changeStatus() { // 禁用情況下,不做狀態更改切換 if (this.disabled) { return; } // 首先看是否開啟,若開啟,就傳遞不開啟;若不開啟,就傳遞開啟(因為狀態切換,取反) const val = this.isOpen ? this.inactiveValue : this.activeValue; this.$emit("input", val); // 更新外層v-model繫結的值 this.$emit("change", val); // 丟擲一個change事件以供使用者使用 }, }, }; </script> <style scoped lang="less"> .mySwitchWrap { display: inline-block; cursor: pointer; font-size: 14px; margin: 2px; /* 將input標籤隱藏起來,寬高都為0,透明度也為0,看不到 */ .switchInput { position: absolute; width: 0; height: 0; opacity: 0; margin: 0; } .switchCentre { display: inline-block; width: auto; height: 20px; color: #fff; background-color: #c4c4c4; border: 1px solid; outline: 0; border-radius: 10px; box-sizing: border-box; transition: all 0.3s; // 加上過渡效果 position: relative; .text { min-width: 54px; // 設定最小寬度 width: fit-content; // 使用fit-content屬性,讓寬度隨著內容多少自適應 height: 100%; font-size: 12px; display: flex; // justify-content: justifyContent; // 注意,這裡也是通過:style控制文字靠左還是靠右 align-items: center; transition: all 0.3s; // 加上過渡效果 } } // 預設小圓點在左側的(使用偽元素建立一個小圓點) .circleDotLeft::after { content: ""; position: absolute; top: -4px; left: -2px; border-radius: 100%; width: 24px; height: 24px; border: 1px solid #e9e9e9; box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.3); // 原來小圓點有一點陰影 background-color: #fff; transition: all 0.3s; // 加上過渡效果 } // 當switch開啟時,加上類名~通過更改定位left值控制圓點在右側 .changeCircleDotRight::after { left: 100%; margin-left: -24px; } // 懸浮加重小圓點陰影 .circleDotLeft:hover::after { box-shadow: 0 1px 18px 0 rgba(0, 0, 0, 0.5); } } // 除了cursor樣式的not-allowed還要搭配js判斷才禁用到位 .disabledSwitch { cursor: not-allowed; opacity: 0.48; } // 禁用情況下,保持小圓點原有陰影 .disabledSwitch .circleDotLeft:hover::after { box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.3); } // 小型switch元件做一個縮放 .small { zoom: 0.8; } // 大型switch元件做一個縮放 .big { zoom: 1.6; } </style>
注意 true-value和false-value
是官方自帶的搭配v-model屬性,其實這裡不用也行,大家參考一下antd的元件便可明瞭。詳見: https://cn.vuejs.org/guide/es...
- 天翼雲全場景業務無縫替換至國產原生作業系統CTyunOS!
- 以羊了個羊為例,淺談小程式抓包與響應報文修改
- 這幾種常見的 JVM 調優場景,你知道嗎?
- 如此狂妄,自稱高效能佇列的Disruptor有啥來頭?
- 為什麼要學習GoF設計模式?
- 827. 最大人工島 : 簡單「並查集 列舉」運用題
- 手把手教你如何使用 Timestream 實現物聯網時序資料儲存和分析
- 850. 矩形面積 II : 掃描線模板題
- Java 併發程式設計解析 | 基於JDK原始碼解析Java領域中的併發鎖,我們可以從中學習到什麼內容?
- 【手把手】光說不練假把式,這篇全鏈路壓測實踐探索
- 大廠鍾愛的全鏈路壓測有什麼意義?四種壓測方案詳細對比分析
- 寫個續集,填坑來了!關於“Thread.sleep(0)這一行‘看似無用’的程式碼”裡面留下的坑。
- 857. 僱傭 K 名工人的最低成本 : 列舉 優先佇列(堆)運用題
- Vue3 實現一個自定義toast(小彈窗)
- 669. 修剪二叉搜尋樹 : 常規樹的遍歷與二叉樹性質
- 讀完 RocketMQ 原始碼,我學會了如何優雅的建立執行緒
- 效能調優——小小的log大大的坑
- 1582. 二進位制矩陣中的特殊位置 : 簡單模擬題
- elementui原始碼學習之仿寫一個el-switch
- 646. 最長數對鏈 : 常規貪心 DP 運用題