這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
本篇文章記錄仿寫一個
el-button組件細節,從而有助于大家更好理解餓了么ui對應組件具體作業細節,本文是elementui原始碼學習仿寫系列的又一篇文章,后續空閑了會不斷更新并仿寫其他組件,原始碼在github上,大家可以拉下來,npm start運行跑起來,結合注釋有助于更好的理解
網站效果演示:ashuai.work:8888/#/myButton
GitHub倉庫地址:github.com/shuirongshu…
什么是Button組件
按鈕用于點擊,一般是做事件的回應,
按鈕封裝效果圖
按鈕分類
- 單一按鈕
- 默認按鈕
- 主題按鈕(primary、success、warning、error)
- 按鈕大小(small、middle、big)
- 按鈕禁用(disabled)
- 按鈕加載(loading)
- 按鈕的圖示位置(默認圖示在按鈕文字左側)
- 圖示按鈕(沒有按鈕文字)
- 單一文字按鈕
- 按鈕組(按鈕組中有多個按鈕)
默認按鈕
默認按鈕很簡單,只是寫一個最普通的樣式即可
<button : />
.myButton {
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
box-sizing: border-box;
padding: 12px 16px;
background-color: rgba(0, 0, 0, 0.1);
color: #222;
border: none;
cursor: pointer;
user-select: none; // 不讓選中文字
transition: all 0.3s;
font-size: 14px;
}
// 懸浮效果
.myButton:hover {
background-color: rgba(0, 0, 0, 0.2);
}
// 按中效果
.myButton:active {
background-color: rgba(0, 0, 0, 0.3);
}
筆者這里是將懸浮的效果和按中的效果,設定背景色越來越深,這樣的話,看著效果比較明顯
主題按鈕
所謂按鈕的主題,就是添加不同的類名,比如primary主題的按鈕,就加上.primary類名、success主題的按鈕,就加上.success類名,然后使用動態class去添加即可(這里使用動態class的陣列用法),如:
<button : />
變數type的值源自于使用按鈕組件時,傳遞進來的type引數
const typeArr = [
"",
"primary",
"success",
"warning",
"error",
"text",
"dangerText",
];
props:{
type: { // 按鈕主題型別
type: String,
validator(val) {
return typeArr.includes(val); // 這里可以加一個校驗函式,其實不加也行
},
},
}
然后給不同type值加上對應的樣式即可,如下:
// primary樣式
.primary {
background-color: #1867c0;
color: #fff;
}
.primary:hover {
background-color: #0854ac;
}
.primary:active {
background-color: #033d7f;
}
// success樣式
.success {
background-color: #19be6b;
color: #fff;
}
.success:hover {
background-color: #0ea459;
}
.success:active {
background-color: #008140;
}
// warning樣式
.warning {
background-color: #ffc163;
color: #fff;
}
.warning:hover {
background-color: #db952d;
}
.warning:active {
background-color: #b97b1d;
}
// 等等type值樣式...
按鈕大小
按鈕大小可以使用padding值的大小去控制,也可以直接使用zoom縮放做控制
這里使用動態style搭配計算屬性的方式去控制,如下代碼:
// 不同的大小指定不同的縮放程度
const sizeObj = {
small: 0.85,
middle: 1,
big: 1.2,
};
props:{ size: String }
<button :style="styleCal" />
computed: {
styleCal() {
return {
zoom: sizeObj[this.size] // zoom縮放的值大小取決于傳遞進來的size值
}
}
}
按鈕禁用
按鈕禁用disable沒啥好說的,主要是要注意loading的時候,也要禁用掉,loading加載的時候,不允許用戶再點擊,
<button :disabled="disabled || loading" />
props:{
loading:Boolean
}
這里注意一下,按鈕禁用的樣式也是通過動態class加上的,請往下看
按鈕加載
注意加載時樣式和加載按鈕圖示出來的時候,將其他的圖示給隱藏起來,(同一時刻,只能有一個按鈕圖示,這樣保證按鈕加載時簡潔一些)
<button
:
:disabled="disabled || loading" // 禁用時禁用,加載時也禁用
>
<i v-if="loading"></i>
<!-- 使用傳進來的圖示,通過動態style控制圖示和文字見的間隔,同一時刻下,
只能有一個圖示出現,所以有loading圖示了,就不能有別的圖示了 -->
<i : :style="styleGap" v-if="icon && !loading"></i>
<slot></slot>
</button>
按鈕的圖示位置
默認從左往右排列(圖示在左側、文字在右側),這里我們可以使用彈性盒的方向flexDirection屬性,來控制從左往右還是從右往左排列
<button :style="styleCal"/>
styleCal() {
// 控制縮放和指定默認圓角以及設定圖示在文字左側還是右側
let styleObj = {
zoom: sizeObj[this.size],
borderRadius: "5px",
flexDirection: this.rightIcon ? "row-reverse" : "row",
};
return styleObj;
},
圖示按鈕和單一文字按鈕
這兩個也很簡單,
- 圖示按鈕注意加圓角的時機
- 單一文字按鈕的樣式要預留設定一份即可
然后動態控制一下即可
按鈕組
按鈕組注意事項:
- 首先將所有的按鈕的圓角全部去掉(這樣的話,所有的按鈕都是方方正正的按鈕了)
- 然后單獨給第一個按鈕
:first-of-type的左上角和左下角的圓角設定一下 - 然后再給最后一個按鈕
last-of-type的右上角和右下角的圓角設定一下 - 最后,按鈕組之間需要有間隔,這里使用
border-right做分割線 - 最最后,再把最后一個按鈕的右邊框去掉即可,如下css代碼
// 附上按鈕組樣式
.myButtonGroup > .myButton {
border-radius: unset !important; // 給所有的按鈕都去掉圓角
border-right: 1px solid #fff; // 給按鈕加上分隔線條
}
// 第一個按鈕左側圓角
.myButtonGroup > .myButton:first-of-type {
border-top-left-radius: 5px !important;
border-bottom-left-radius: 5px !important;
}
// 最后一個按鈕的右側圓角
.myButtonGroup > .myButton:last-of-type {
border-top-right-radius: 5px !important;
border-bottom-right-radius: 5px !important;
border-right: none; // 同時,清除最后一個按鈕的右側邊框
}
代碼
復制粘貼即可使用,如果道友覺得代碼幫忙到了您,歡迎給咱github倉庫一個star哈??
myButton組件
<template>
<button
:style="styleCal"
:
:disabled="disabled || loading"
@click="clickButton"
>
<i v-if="loading"></i>
<!-- 使用傳進來的圖示,通過動態style控制圖示和文字見的間隔,同一時刻下,
只能有一個圖示出現,所以有loading圖示了,就不能有別的圖示了 -->
<i : :style="styleGap" v-if="icon && !loading"></i>
<!-- 普通插槽有東西才去渲染 -->
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
<script>
// 型別校驗
const typeArr = [
"",
"primary",
"success",
"warning",
"error",
"text",
"dangerText",
];
const sizeArr = ["", "small", "middle", "big"]; // 大小檢驗
const sizeObj = {
// 不同的大小指定不同的縮放程度
small: 0.85,
middle: 1,
big: 1.2,
};
export default {
name: "myButton",
props: {
disabled: Boolean,
loading: Boolean, // loading時,不可繼續點擊(繼續點擊不生效)
rightIcon: Boolean, // 通過彈性盒的方向控制圖示的位置
type: {
type: String,
validator(val) {
return typeArr.includes(val);
},
},
size: {
type: String,
validator(val) {
return sizeArr.includes(val);
},
},
icon: String,
},
computed: {
styleCal() {
// 控制縮放和指定默認圓角以及設定圖示在文字左側還是右側
let styleObj = {
zoom: sizeObj[this.size],
borderRadius: "5px",
flexDirection: this.rightIcon ? "row-reverse" : "row",
};
// 當有圖示,且沒有文字的時候(或默認插槽沒傳),就讓按鈕變成圓形按鈕
if ((this.icon && !this.$slots.default) || !this.$slots.default[0].text) {
styleObj["borderRadius"] = "50%";
styleObj["padding"] = "12px";
}
return styleObj;
},
styleGap() {
// 有圖示,有文字,圖示在左側
if (
(this.icon && !this.$slots.default) ||
(this.$slots.default[0].text && !this.rightIcon)
) {
return {
paddingRight: "1px",
};
}
// 有圖示,有文字,圖示在右側
if (
(this.icon && !this.$slots.default) ||
(this.$slots.default[0].text && this.rightIcon)
) {
return {
paddingLeft: "1px",
};
}
},
},
methods: {
clickButton(e) {
if (this.disabled) return;
this.$emit("click", e); // 傳出去,便于使用
},
},
};
</script>
<style lang='less' scoped>
/* 關于按鈕的樣式即寫好幾套樣式,然后通過型別等各種引數去控制樣式,最終實作對應效果 */
// 基礎樣式
.myButton {
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
box-sizing: border-box;
padding: 12px 16px;
background-color: rgba(0, 0, 0, 0.1);
color: #222;
border: none;
cursor: pointer;
user-select: none;
transition: all 0.3s;
font-size: 14px;
.iii {
margin-right: 4px;
}
}
.myButton:hover {
background-color: rgba(0, 0, 0, 0.2);
}
.myButton:active {
background-color: rgba(0, 0, 0, 0.3);
}
// primary樣式
.primary {
background-color: #1867c0;
color: #fff;
}
.primary:hover {
background-color: #0854ac;
}
.primary:active {
background-color: #033d7f;
}
// success樣式
.success {
background-color: #19be6b;
color: #fff;
}
.success:hover {
background-color: #0ea459;
}
.success:active {
background-color: #008140;
}
// warning樣式
.warning {
background-color: #ffc163;
color: #fff;
}
.warning:hover {
background-color: #db952d;
}
.warning:active {
background-color: #b97b1d;
}
// error樣式
.error {
background-color: #ff5252;
color: #fff;
}
.error:hover {
background-color: #fd3030;
}
.error:active {
background-color: #d50000;
}
// text樣式
.text {
background-color: unset;
color: #409eff;
padding: 2px 4px;
}
.text:hover {
background-color: unset;
opacity: 0.9;
}
.text:active {
background-color: unset;
opacity: 1;
color: #1a7ada;
}
// dangerText樣式
.dangerText {
background-color: unset;
color: #ff5252;
padding: 2px 4px;
}
.dangerText:hover {
background-color: unset;
opacity: 0.9;
}
.dangerText:active {
background-color: unset;
opacity: 1;
color: #d50000;
}
// 加載按鈕樣式
.loadingBtn {
opacity: 0.6;
pointer-events: none; // 值為none就沒有hover和active效果了
}
// disabled樣式(注意樣式的順序)
.disabledBtn {
background-color: rgba(0, 0, 0, 0.12);
color: #bbb;
}
.disabledBtn:hover {
opacity: 1;
cursor: not-allowed;
background-color: rgba(0, 0, 0, 0.12);
}
.disabledBtn:active {
color: #bbb;
opacity: 1;
background-color: rgba(0, 0, 0, 0.12);
}
// 附上按鈕組樣式
.myButtonGroup > .myButton {
border-radius: unset !important;
border-right: 1px solid #fff;
}
.myButtonGroup > .myButton:first-of-type {
border-top-left-radius: 5px !important;
border-bottom-left-radius: 5px !important;
}
.myButtonGroup > .myButton:last-of-type {
border-top-right-radius: 5px !important;
border-bottom-right-radius: 5px !important;
border-right: none;
}
</style>
myButtonGroup組件
<template>
<div >
<slot></slot>
</div>
</template>
<script>
export default {
name: "myButtonGroup",
};
</script>
<style>
.myButtonGroup {
display: inline-flex !important;
align-items: center;
}
</style>
使用的時候
<template>
<div>
<h5>單個按鈕</h5>
<br />
<button @click="clickLoad">加載切換</button>
<div >
<span v-for="(item, index) of btnArr">
<my-button
style="margin-right: 16px"
:key="index"
:type="item.type"
:size="item.size"
:disabled="item.disabled"
:loading="item.loading"
:icon="item.icon"
:rightIcon="item.rightIcon"
@click="
(e) => {
clickBtn(item, e);
}
"
>{{ item.name }}</my-button
>
</span>
</div>
<br />
<h5>按鈕組</h5>
<br />
<my-button-group>
<my-button type="success" icon="el-icon-arrow-left">上一頁</my-button>
<my-button type="success" icon="el-icon-arrow-right" :rightIcon="true"
>下一頁</my-button
>
</my-button-group>
<br />
<br />
<my-button-group>
<my-button type="primary" icon="el-icon-user"></my-button>
<my-button type="primary" icon="el-icon-view"></my-button>
<my-button type="primary" icon="el-icon-star-off"></my-button>
<my-button type="primary" icon="el-icon-chat-dot-square"></my-button>
<my-button type="primary" icon="el-icon-share"></my-button>
</my-button-group>
</div>
</template>
<script>
export default {
name: "myButtonName",
data() {
return {
loadingF: false,
btnArr: [
{
type: "",
name: "默認按鈕",
},
{
type: "primary",
name: "primary",
},
{
type: "success",
name: "success",
},
{
type: "warning",
name: "warning",
},
{
type: "error",
name: "error",
},
{
type: "primary",
name: "size=small",
size: "small",
},
{
type: "primary",
name: "size=middle",
size: "middle",
},
{
type: "primary",
name: "size=big",
size: "big",
},
{
type: "success", // 不管type什么型別,只要禁用全部置灰
name: "disabled",
disabled: true,
},
{
type: "primary",
name: "等待加載",
loading: false,
},
{
type: "success",
name: "等待加載",
loading: false,
},
{
type: "success",
name: "icon",
icon: "el-icon-star-on",
},
{
type: "success",
name: "icon",
icon: "el-icon-star-on",
rightIcon: true,
},
{
type: "success",
name: "",
icon: "el-icon-edit",
},
{
type: "error",
name: "",
icon: "el-icon-delete",
},
{
type: "text",
name: "純text按鈕",
// loading: true,
},
{
type: "dangerText",
name: "dangerText按鈕",
icon: "el-icon-delete-solid",
},
{
type: "text",
name: "text禁用",
disabled: true,
},
],
};
},
methods: {
clickLoad() {
let lebel = this.btnArr[9].name;
let newItem9 = {
type: "primary",
name: lebel == "等待加載" ? "加載中" : "等待加載",
loading: lebel == "等待加載" ? true : false,
};
this.$set(this.btnArr, 9, newItem9);
let newItem10 = {
type: "success",
name: lebel == "等待加載" ? "加載中" : "等待加載",
loading: lebel == "等待加載" ? true : false,
};
this.$set(this.btnArr, 10, newItem10);
},
// 注意這種寫法,可接收多個引數
clickBtn(item, e) {
console.log("clickBtn", item, e);
},
},
};
</script>
<style>
.btnBox {
width: 100%;
box-sizing: border-box;
padding: 24px 0;
display: flex;
align-items: flex-end;
flex-wrap: wrap;
}
.btn {
margin-bottom: 24px;
}
</style>
本文轉載于:
https://juejin.cn/post/7182113902539309112
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/546121.html
標籤:其他
上一篇:react組件中方法呼叫
下一篇:跨域

