選是由多個單選項組成的選擇控件,獲取用戶針對某個事情的唯一結(jié)果。
常用場景有“微信APP-我-個人信息-設(shè)置性別”、“iOS設(shè)置-通知-顯示預(yù)覽”等。
單選功能通常包含標題和多個單選項,每個單選項由選項名稱和單選按鈕。
(1)先畫標題。從默認元件庫拖動“文本標簽”到工作區(qū),或者直接把“頁面名稱”作為標題。
(2)再畫選項名稱。從默認元件庫拖動“矩形1”到工作區(qū),修改尺寸為375*40px,雙擊輸入文字“選項名稱”,設(shè)置左填充為15px這樣展示效果更妥當。
(3)再畫單選按鈕。從默認元件庫拖動“文本標簽”到“選項名稱”的右邊,雙擊輸入“√”,修改尺寸為40*40px,設(shè)置對齊方式為左右居中和垂直居中。
(4)復(fù)制單選名稱2份,依次排列。
(5)生成原型HTML并在瀏覽器中查看效果。
單選功能的常見交互效果:點擊任一單選按鈕自動變?yōu)檫x中狀態(tài),同時其他單選按鈕變成未選中狀態(tài)。選中狀態(tài)和未選中狀態(tài)的樣式是不一樣的。
(6)繼承上面的“無交互”步驟,從第四步開始繼續(xù)畫“有交互”。設(shè)置按鈕的未選中狀態(tài),設(shè)置文本顏色為白色。
(7)設(shè)置按鈕的選中狀態(tài),點擊“交互樣式-選中”,選中字體顏色設(shè)置為#333333。
(8)設(shè)置按鈕為選項組。選擇按鈕設(shè)置選項組名稱為“單選”或者其他名稱,這樣代表該組的按鈕同時只能有一個選中狀態(tài)。
(9)選中標題和按鈕,然后右鍵點擊“組合”,然后命名為“單選項”。
(10)設(shè)置單選項的交互。點擊該組合,設(shè)置“鼠標單擊時”事件,添加動作“選中”,組織動作“當前元件”。
(11)復(fù)制單選項2份,依次排列。
(12)選中第一個按鈕,打勾選中,這樣生成原型之后默認選中它。
(13)生成原型HTML并在瀏覽器中查看效果。
不同場景下的單選功能,標題不一樣,樣式相對固定。
根據(jù)多年P(guān)M經(jīng)驗,總結(jié)出3種常用的“單選”,添加到APP功能庫。
單選(打勾)
單選(主流)
單選(傳統(tǒng))
單選功能同一時間只有一個選中狀態(tài)。
單選功能可以設(shè)計多種樣式,另外選擇器也可充當單選功能。
我們百度搜索我們想要的文章時,好不容易找到了我們想要的文章,點開復(fù)制,卻發(fā)現(xiàn)文章被鎖定,像下面這種情況,復(fù)制需要收費,面對這樣的情況,教你一招解決,免費復(fù)制自己想要的文章。
首先我們需要找到我們所要復(fù)制的文章,按電腦上的F12按鈕,打開如同所示的頁面
然后按F1按鈕
再往下翻,找到Debugger下面的禁用JavaScript,進行打勾
千萬不要將旁邊的框框刪去,這樣就可以免費復(fù)制了
Zag 和 PandaCSS 都是出自 chakra 團隊之手,Zag 聚焦于處理組件的邏輯,而 PandaCSS 聚焦于通過 ts 來維護樣式,將兩者進行搭配會有怎么樣的使用體驗?zāi)兀窟@篇文章將繼續(xù)以 vuesax 中 checkbox 組件的樣式作為參考,結(jié)合 Zag 和 PandaCSS 進行 vue3 版本的重構(gòu),實現(xiàn)一個超絲滑的勾選框組件。
Zag 中將 Checkbox 分為三個組成部分:
我們首先在 Zag Checkbox 文檔中復(fù)制 JSX 的實例代碼:
import * as checkbox from "@zag-js/checkbox"
import { normalizeProps, useMachine } from "@zag-js/vue"
import { defineComponent, h, Fragment, computed } from "vue"
export default defineComponent({
name: "Checkbox",
setup() {
const [state, send]=useMachine(checkbox.machine({ id: "checkbox" }))
const apiRef=computed(()=>
checkbox.connect(state.value, send, normalizeProps),
)
return ()=> {
const api=apiRef.value
return (
<label {...api.rootProps}>
<span {...api.labelProps}>
Input is {api.isChecked ? "checked" : "unchecked"}
</span>
<div {...api.controlProps} />
<input {...api.hiddenInputProps} />
</label>
)
}
},
})
這段代碼使用了 useMachine 函數(shù)創(chuàng)建了一個狀態(tài)機,并且寫了一個無樣式的基礎(chǔ) checkbox 結(jié)構(gòu):
接下來我們?yōu)?checkbox 組件補充樣式.
想要實現(xiàn)絲滑的勾選框效果,最直觀的是打勾的動畫。這個效果可以通過 SVG 或者純 css 實現(xiàn),這里我使用的是 css 來實現(xiàn)的。:
首先我們要想想如何實現(xiàn)一個勾勾的效果,??由一長一短兩個斜邊組成,那么我們只需要放置一個矩形,設(shè)置一定的旋轉(zhuǎn)角度,并設(shè)置其中的兩個邊框,就能實現(xiàn)一個??的形狀:
代碼實現(xiàn)如下:
import { defineComponent } from "vue";
import { css, cx } from "@/styled-system/css";
const IconCheck=defineComponent({
props: {
color: {
type: String,
default: css({ colorPalette: "gray" }),
},
size: {
type: String,
default: css({ colorPalette: "gray" }),
},
customCSS: {
type: String,
},
},
setup(props) {
return ()=> (
<i
class={cx(
css({
display: "flex",
alignItems: "center",
justifyContent: "center",
}),
props.customCSS,
)}
>
<div
class={css({
position: "relative",
width: "80%",
height: "40%",
transform: "rotate(-45deg)",
})}
>
<div
class={css({
position: "absolute",
left: "0",
width: "full",
height: "full",
borderLeft: "2px solid white",
animation: "checkShort 0.15s",
})}
/>
<div
class={css({
position: "absolute",
left: "0",
width: "full",
height: "full",
borderBottom: "2px solid white",
animation: "checkLong 0.5s",
})}
/>
</div>
</i>
);
},
});
export default IconCheck;
上面這段代碼中,定義了一個矩形,寬高分別為最外層容器的 80% 和 40%,transform: "rotate(-45deg)", 則設(shè)置了矩形的旋轉(zhuǎn)角度,內(nèi)部放置兩個與矩形寬高一致的容器,分別設(shè)置 borderLeft 和 borderBottom ,這樣就組成了一長一短兩條線。
這里之所以需要在內(nèi)部放兩個容器單獨設(shè)置邊框,而不是直接在矩形設(shè)置邊框,是為了更好的實現(xiàn)動畫效果,長短邊分別設(shè)置了兩個持續(xù)時間不同的動畫 checkShort 0.15s 和 checkLong 0.5s:
checkShort: {
"0%": {
height:0,
},
"100%":{
height: "full",
}
},
checkLong: {
"0%": {
width: 0,
},
"30%":{
width: 0,
},
"100%": {
width: "full",
}
}
短邊從最開始就執(zhí)行動畫,持續(xù)時間為長邊動畫的 30%,長邊則在 0-30% 時不執(zhí)行,30% 之后開始執(zhí)行,這樣就能實現(xiàn)短邊動畫執(zhí)行完成后,長邊動畫再執(zhí)行的效果。
之所以不使用 animation-delay 去延遲執(zhí)行長邊動畫,是因為這種方式會導(dǎo)致動畫延遲執(zhí)行前,長邊會先展示出來,效果就不對了。如果要使用這種方式還得單獨做一些動畫延遲前的隱藏處理,會比較麻煩:
為了讓用戶更容易感知勾選框是可以交互的,我們?yōu)楣催x框增加未勾選和關(guān)狀態(tài)的 hover 效果。
未勾選狀態(tài)的 hover 效果,默認只有灰色邊框,鼠標懸浮后變?yōu)榛疑尘埃?/p>
這里有個注意點,我們鼠標懸浮在勾選框的最外層,也可以觸發(fā)內(nèi)層的 hover 樣式,如果直接使用 hover 效果是沒法做到的,這樣只能鼠標懸浮在邊框內(nèi)才能觸發(fā)。
如果我們沒有使用任何樣式庫,實現(xiàn)這個效果可以通過維護一個 鼠標是否 hover 的狀態(tài),并通過 onMouseEnter 和 onMouseLeave 來更新這個狀態(tài),再在內(nèi)層根據(jù)這個狀態(tài)動態(tài)渲染樣式。
但這里我們可以使用 pandaCSS 的 group 選擇器來實現(xiàn)。
首先在勾選框最外層元素增加 group 類名:
<label
{...api.rootProps}
class={[
css({
display: "flex",
alignItems: "center",
cursor: "pointer",
}),
+ "group",
]}
>
然后在內(nèi)層的 control 元素增加基礎(chǔ)樣式:
<div
{...api.controlProps}
class={css({
width: "24px",
height: "24px",
borderRadius: "8px",
border: api.isChecked
? "none"
: "token(colors.gray.200) solid 2px",
transition: "all 0.3s",
marginRight: "4px",
flexShrink: "0",
_groupHover: {
background: "gray.200",
},
})}
>
// ...
</div>
這里的 _groupHover 即為 group 選擇器,當帶有 group 類名的元素觸發(fā) hover 時,內(nèi)部的元素都可以使用 _groupHover 設(shè)置特定樣式。這樣我們就實現(xiàn)了前文圖中的效果。
在勾選時,會有一個藍色色塊放大漸出的效果,我們先來實現(xiàn)這個樣式。
<Transition
enterFromClass={css({
transition: "all 0.2s",
transform: "scale(0.5)",
opacity: "0",
})}
enterToClass={css({
transition: "all 0.2s",
transform: "scale(1)",
opacity: "1",
})}
leaveToClass={css({
transition: "all 0.2s",
transform: "scale(0.5)",
opacity: "0",
})}
>
{api.isChecked && (
<div
class={cx(
props.color,
css({
width: "full",
height: "full",
background: "colorPalette.600",
borderRadius: "inherit",
display: "flex",
alignItems: "center",
justifyContent: "center",
transition: "all 0.3s",
}),
)}
>
<IconCheck
customCSS={css({
width: "18px",
height: "18px",
})}
/>
</div>
)}
</Transition>
這里我們使用 vue 中的 Transition 組件來實現(xiàn)動畫效果,通過改變scale 和 opacity 實現(xiàn)元素大小和透明度的過渡動畫,內(nèi)部包裹著勾選的圖標。
實現(xiàn)了勾選的效果,繼續(xù)實現(xiàn)勾選后的 hover 樣式。勾選后在 hover 時,勾選框的外層有一個與主題色相同的外層陰影效果:
這里我們依然使用 group 選擇器來設(shè)置 hover 樣式:
<div
class={cx(
props.color,
css({
width: "full",
height: "full",
background: "colorPalette.600",
borderRadius: "inherit",
display: "flex",
alignItems: "center",
justifyContent: "center",
transition: "all 0.3s",
+ _groupHover: {
+ boxShadow:
+ "0px 3px 15px 0px color-mix(in srgb, token(colors.colorPalette.600) 40%, transparent)",
+ },
}),
)}
>
<IconCheck
customCSS={css({
width: "18px",
height: "18px",
})}
></IconCheck>
</div>
在陰影效果的代碼中 0px 3px 15px 0px color-mix(in srgb, token(colors.colorPalette.600) 40%, transparent) ,前面幾個設(shè)置陰影大小的參數(shù)很容易理解,但是后面陰影顏色的實現(xiàn)大家可能比較陌生,單獨解釋一下:
我這里用法的含義是在 srgb 的色彩模式下,將主題色(token(colors.colorPalette.600)) 與透明色(transparent),以 40% 的比例進行混合,最終合成的顏色就是 40% 透明度的主題色。 color-mix() 函數(shù)是 pandaCSS 中推薦用戶用于為內(nèi)置顏色設(shè)置透明度的方法,除此以外并沒有發(fā)現(xiàn)其他更簡便的方式。
最后我們完善一下勾選框的雙向綁定邏輯邏輯,
實現(xiàn)的代碼如下:
const [state, send]=useMachine(
checkbox.machine({
id: useId("checkbox"),
onCheckedChange(detail) {
emit("update:modelValue", detail.checked);
},
}),
);
const apiRef=computed(()=>
checkbox.connect(state.value, send, normalizeProps),
);
watch(
()=> props.modelValue,
()=> {
apiRef.value.setChecked(props.modelValue);
},
);
? 使用 Vue+Zag+PandaCSS 實現(xiàn)一個超絲滑的勾選框組件
原文鏈接:https://juejin.cn/post/7295954109404463155
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。