Zag 和 PandaCSS 都是出自 chakra 團(tuán)隊(duì)之手,Zag 聚焦于處理組件的邏輯,而 PandaCSS 聚焦于通過 ts 來維護(hù)樣式,將兩者進(jìn)行搭配會(huì)有怎么樣的使用體驗(yàn)?zāi)兀窟@篇文章將繼續(xù)以 vuesax 中 checkbox 組件的樣式作為參考,結(jié)合 Zag 和 PandaCSS 進(jìn)行 vue3 版本的重構(gòu),實(shí)現(xiàn)一個(gè)超絲滑的勾選框組件。
Zag 中將 Checkbox 分為三個(gè)組成部分:
我們首先在 Zag Checkbox 文檔中復(fù)制 JSX 的實(shí)例代碼:
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)建了一個(gè)狀態(tài)機(jī),并且寫了一個(gè)無樣式的基礎(chǔ) checkbox 結(jié)構(gòu):
接下來我們?yōu)?checkbox 組件補(bǔ)充樣式.
想要實(shí)現(xiàn)絲滑的勾選框效果,最直觀的是打勾的動(dòng)畫。這個(gè)效果可以通過 SVG 或者純 css 實(shí)現(xiàn),這里我使用的是 css 來實(shí)現(xiàn)的。:
首先我們要想想如何實(shí)現(xiàn)一個(gè)勾勾的效果,??由一長一短兩個(gè)斜邊組成,那么我們只需要放置一個(gè)矩形,設(shè)置一定的旋轉(zhuǎn)角度,并設(shè)置其中的兩個(gè)邊框,就能實(shí)現(xiàn)一個(gè)??的形狀:
代碼實(shí)現(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;
上面這段代碼中,定義了一個(gè)矩形,寬高分別為最外層容器的 80% 和 40%,transform: "rotate(-45deg)", 則設(shè)置了矩形的旋轉(zhuǎn)角度,內(nèi)部放置兩個(gè)與矩形寬高一致的容器,分別設(shè)置 borderLeft 和 borderBottom ,這樣就組成了一長一短兩條線。
這里之所以需要在內(nèi)部放兩個(gè)容器單獨(dú)設(shè)置邊框,而不是直接在矩形設(shè)置邊框,是為了更好的實(shí)現(xiàn)動(dòng)畫效果,長短邊分別設(shè)置了兩個(gè)持續(xù)時(shí)間不同的動(dòng)畫 checkShort 0.15s 和 checkLong 0.5s:
checkShort: {
"0%": {
height:0,
},
"100%":{
height: "full",
}
},
checkLong: {
"0%": {
width: 0,
},
"30%":{
width: 0,
},
"100%": {
width: "full",
}
}
短邊從最開始就執(zhí)行動(dòng)畫,持續(xù)時(shí)間為長邊動(dòng)畫的 30%,長邊則在 0-30% 時(shí)不執(zhí)行,30% 之后開始執(zhí)行,這樣就能實(shí)現(xiàn)短邊動(dòng)畫執(zhí)行完成后,長邊動(dòng)畫再執(zhí)行的效果。
之所以不使用 animation-delay 去延遲執(zhí)行長邊動(dòng)畫,是因?yàn)檫@種方式會(huì)導(dǎo)致動(dòng)畫延遲執(zhí)行前,長邊會(huì)先展示出來,效果就不對了。如果要使用這種方式還得單獨(dú)做一些動(dòng)畫延遲前的隱藏處理,會(huì)比較麻煩:
為了讓用戶更容易感知勾選框是可以交互的,我們?yōu)楣催x框增加未勾選和關(guān)狀態(tài)的 hover 效果。
未勾選狀態(tài)的 hover 效果,默認(rèn)只有灰色邊框,鼠標(biāo)懸浮后變?yōu)榛疑尘埃?/p>
這里有個(gè)注意點(diǎn),我們鼠標(biāo)懸浮在勾選框的最外層,也可以觸發(fā)內(nèi)層的 hover 樣式,如果直接使用 hover 效果是沒法做到的,這樣只能鼠標(biāo)懸浮在邊框內(nèi)才能觸發(fā)。
如果我們沒有使用任何樣式庫,實(shí)現(xiàn)這個(gè)效果可以通過維護(hù)一個(gè) 鼠標(biāo)是否 hover 的狀態(tài),并通過 onMouseEnter 和 onMouseLeave 來更新這個(gè)狀態(tài),再在內(nèi)層根據(jù)這個(gè)狀態(tài)動(dòng)態(tài)渲染樣式。
但這里我們可以使用 pandaCSS 的 group 選擇器來實(shí)現(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 選擇器,當(dāng)帶有 group 類名的元素觸發(fā) hover 時(shí),內(nèi)部的元素都可以使用 _groupHover 設(shè)置特定樣式。這樣我們就實(shí)現(xiàn)了前文圖中的效果。
在勾選時(shí),會(huì)有一個(gè)藍(lán)色色塊放大漸出的效果,我們先來實(shí)現(xiàn)這個(gè)樣式。
<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 組件來實(shí)現(xiàn)動(dòng)畫效果,通過改變scale 和 opacity 實(shí)現(xiàn)元素大小和透明度的過渡動(dòng)畫,內(nèi)部包裹著勾選的圖標(biāo)。
實(shí)現(xiàn)了勾選的效果,繼續(xù)實(shí)現(xiàn)勾選后的 hover 樣式。勾選后在 hover 時(shí),勾選框的外層有一個(gè)與主題色相同的外層陰影效果:
這里我們依然使用 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) ,前面幾個(gè)設(shè)置陰影大小的參數(shù)很容易理解,但是后面陰影顏色的實(shí)現(xiàn)大家可能比較陌生,單獨(dú)解釋一下:
我這里用法的含義是在 srgb 的色彩模式下,將主題色(token(colors.colorPalette.600)) 與透明色(transparent),以 40% 的比例進(jìn)行混合,最終合成的顏色就是 40% 透明度的主題色。 color-mix() 函數(shù)是 pandaCSS 中推薦用戶用于為內(nèi)置顏色設(shè)置透明度的方法,除此以外并沒有發(fā)現(xiàn)其他更簡便的方式。
最后我們完善一下勾選框的雙向綁定邏輯邏輯,
實(shí)現(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 實(shí)現(xiàn)一個(gè)超絲滑的勾選框組件
原文鏈接:https://juejin.cn/post/7295954109404463155
html表單:HTML表單用于收集用戶輸入的數(shù)據(jù),并將數(shù)據(jù)發(fā)送到服務(wù)器進(jìn)行處理,創(chuàng)建了一個(gè)包含多種表單元素的表單。其中,<label> 標(biāo)簽用來描述表單元素,<input> 標(biāo)簽用來創(chuàng)建表單輸入框,<textarea> 標(biāo)簽用來創(chuàng)建多行文本輸入框,<button> 標(biāo)簽用來創(chuàng)建提交按鈕等等。下面我們一起來學(xué)習(xí)了解吧
HTML提供了幾個(gè)用于創(chuàng)建表單的標(biāo)簽,包括<form>、<input>、<label>、<select>、<option>、<textarea>和<button>。
<form>標(biāo)記用于創(chuàng)建表單元素,該元素可以包含一個(gè)或多個(gè)表單控件,如文本輸入、復(fù)選框、單選按鈕等。
action屬性指定表單數(shù)據(jù)應(yīng)提交到的URL,method屬性指定用于提交的HTTP方法(通常為“GET”或“POST”)。
下面是一個(gè)帶有文本輸入和提交按鈕的簡單表單示例:
<form action="submit-form.php" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<button type="submit">Submit</button>
</form>
在本例中,<label>標(biāo)記用于使用for屬性和id屬性將文本輸入與其標(biāo)簽相關(guān)聯(lián)。輸入的name屬性指定將與表單數(shù)據(jù)一起提交的表單字段的名稱。
<select>標(biāo)記用于創(chuàng)建選項(xiàng)的下拉列表,<option>標(biāo)記用于定義每個(gè)選項(xiàng)。下面是一個(gè)例子:
<label for="color">Favorite color:</label>
<select id="color" name="color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
在本例中,每個(gè)選項(xiàng)的value屬性指定將與表單數(shù)據(jù)一起提交的值。
<textarea>標(biāo)記用于創(chuàng)建多行文本輸入。下面是一個(gè)例子:
<label for="message">Message:</label>
<textarea id="message" name="message"></textarea>
<button>標(biāo)記用于創(chuàng)建一個(gè)按鈕,該按鈕可用于提交表單或執(zhí)行其他操作。 下面是一個(gè)例子:
<button type="submit">Submit</button>
HTML中,文本框、密碼框和多行文本框分別使用input元素的不同type屬性值來實(shí)現(xiàn)。
文本框、密碼框和多行文本框是用來接收用戶輸入的常見表單元素。它們分別使用不同的標(biāo)簽和屬性來實(shí)現(xiàn)。
文本框(text box)是用于接受單行文本輸入的表單元素。可以通過設(shè)置<input>標(biāo)簽的type屬性為"text"來創(chuàng)建文本框。例如:
<label for="username">用戶名:</label>
<input type="text" id="username" name="username">
上面的示例代碼中,id屬性定義了文本框的唯一標(biāo)識(shí)符,name屬性定義了提交表單時(shí)該文本框的名稱。
密碼框(password field)是用于接受密碼等敏感信息的表單元素,并且會(huì)將用戶輸入的字符隱藏起來。可以通過設(shè)置<input>標(biāo)簽的type屬性為"password"來創(chuàng)建密碼框。例如:
<label for="password">密碼:</label>
<input type="password" id="password" name="password">
多行文本框(text area)可用于接收包含多個(gè)行的文本輸入,例如評(píng)論或留言等。可以通過使用<textarea>標(biāo)簽來創(chuàng)建多行文本框,然后使用它的rows和cols屬性來指定它的尺寸。例如:
<label for="message">留言:</label>
<textarea id="message" name="message" rows="5" cols="30"></textarea>
上面的示例代碼中,rows屬性定義了多行文本框的行數(shù),cols屬性定義了多行文本框每一行的字符數(shù)。
HTML中,單選框和復(fù)選框是常見的表單元素,用于讓用戶選擇不同選項(xiàng)。它們分別使用<input>標(biāo)簽的不同type屬性值來實(shí)現(xiàn)。
單選框(radio button)允許用戶在一組選項(xiàng)中選擇一個(gè)。可以使用相同的name屬性來將多個(gè)單選框關(guān)聯(lián)起來。當(dāng)用戶勾選其中一個(gè)單選框時(shí),其他單選框?qū)⒆詣?dòng)取消選中狀態(tài)。可以通過設(shè)置<input>標(biāo)簽的type屬性為"radio"來創(chuàng)建單選框。例如:
<form>
<p>請選擇性別:</p>
<label><input type="radio" name="gender" value="M">男士</label>
<label><input type="radio" name="gender" value="F">女士</label>
<label><input type="radio" name="gender" value="O">其他</label>
</form>
上面的示例代碼中,三個(gè)單選框使用相同的name屬性,以便將它們關(guān)聯(lián)起來。當(dāng)用戶勾選其中一個(gè)單選框時(shí),另外兩個(gè)單選框?qū)⒆詣?dòng)取消選中狀態(tài)。
復(fù)選框(checkbox)允許用戶選擇多個(gè)選項(xiàng)。可以使用相同的name屬性來將多個(gè)復(fù)選框關(guān)聯(lián)起來。當(dāng)用戶勾選其中一個(gè)復(fù)選框時(shí),其他復(fù)選框保持不變。可以通過設(shè)置<input>標(biāo)簽的type屬性為"checkbox"來創(chuàng)建復(fù)選框。例如:
<form>
<p>請選擇語言:</p>
<label><input type="checkbox" name="lang" value="js">JavaScript</label>
<label><input type="checkbox" name="lang" value="py">Python</label>
<label><input type="checkbox" name="lang" value="java">Java</label>
</form>
上面的示例代碼中,三個(gè)復(fù)選框使用相同的name屬性,以便將它們關(guān)聯(lián)起來。用戶可以勾選多個(gè)復(fù)選框。
單選框和復(fù)選框的值可以通過value屬性進(jìn)行指定,當(dāng)表單提交時(shí),瀏覽器會(huì)將被選中的單選框或復(fù)選框的值作為表單數(shù)據(jù)提交到服務(wù)器并進(jìn)行處理。
HTML中,下拉列表框和列表框是用來顯示多個(gè)選項(xiàng)的表單元素。它們分別使用不同的標(biāo)簽和屬性來實(shí)現(xiàn)。
下拉列表框(dropdown list)可以讓用戶從多個(gè)選項(xiàng)中選擇一個(gè)選項(xiàng)。可以使用<select>標(biāo)簽來創(chuàng)建下拉列表框,然后使用<option>標(biāo)簽定義每一個(gè)選項(xiàng)。例如:
<form>
<p>請選擇城市:</p>
<select name="city">
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="gz">廣州</option>
<option value="sz">深圳</option>
</select>
</form>
上面的示例代碼中,四個(gè)<option>標(biāo)簽定義了四個(gè)選項(xiàng)。其中"value"屬性指定每個(gè)選項(xiàng)的值,"text"節(jié)點(diǎn)指定每個(gè)選項(xiàng)的文本內(nèi)容。當(dāng)用戶點(diǎn)擊下拉框時(shí),會(huì)出現(xiàn)一個(gè)下拉菜單列出所有可用選項(xiàng),用戶可以從中選擇一個(gè)選項(xiàng)。
列表框(list box)是另一種顯示多個(gè)選項(xiàng)的表單元素,與下拉列表框不同的是,列表框可以同時(shí)顯示多個(gè)選項(xiàng)。可以使用<select>標(biāo)簽來創(chuàng)建列表框,然后使用<option>標(biāo)簽定義每一個(gè)選項(xiàng)并設(shè)置multiple屬性為"multiple"來啟用多選模式。例如:
<form>
<p>請選擇興趣愛好:</p>
<select name="interests" multiple>
<option value="reading">閱讀</option>
<option value="music">音樂</option>
<option value="painting">繪畫</option>
<option value="sports">運(yùn)動(dòng)</option>
</select>
</form>
上面的示例代碼中,multiple屬性指定該列表框啟用多選模式。當(dāng)用戶選擇一個(gè)或多個(gè)選項(xiàng)時(shí),這些選項(xiàng)將被高亮顯示。
總之,在HTML中可以使用下拉列表框和列表框來為用戶提供多個(gè)選項(xiàng)讓用戶選擇,并在表單提交時(shí)將選擇的結(jié)果作為表單數(shù)據(jù)提交到服務(wù)器。
HTML中,按鈕和圖像按鈕是用來觸發(fā)事件或提交表單的表單元素。它們分別使用<button>標(biāo)簽和<input>標(biāo)簽的不同type屬性值來實(shí)現(xiàn)。
普通按鈕(button)通常用于觸發(fā)一些事件,例如刷新頁面、清空表單等。可以使用<button>標(biāo)簽來創(chuàng)建普通按鈕。例如:
<button>點(diǎn)擊我</button>
上面的示例代碼中,創(chuàng)建了一個(gè)簡單的按鈕,點(diǎn)擊按鈕會(huì)觸發(fā)一些事件。
提交按鈕(submit button)用于將表單數(shù)據(jù)提交到服務(wù)器進(jìn)行處理。可以使用<input>標(biāo)簽,并將其type屬性設(shè)置為"submit"來創(chuàng)建提交按鈕,例如:
<input type="submit" value="提交">
上面的示例代碼中,設(shè)置了一個(gè)提交按鈕,當(dāng)用戶點(diǎn)擊該按鈕時(shí),瀏覽器會(huì)將表單數(shù)據(jù)提交到服務(wù)器并進(jìn)行處理。
圖像按鈕(image button)與普通按鈕類似,但它使用圖片而不是文本作為按鈕的標(biāo)識(shí)。可以使用<input>標(biāo)簽,并將其type屬性設(shè)置為"image"來創(chuàng)建圖像按鈕,例如:
<input type="image" src="submit.png" alt="提交">
上面的示例代碼中,創(chuàng)建了一個(gè)帶有圖片的圖像按鈕。當(dāng)用戶單擊圖像按鈕時(shí),瀏覽器會(huì)將表單數(shù)據(jù)提交到服務(wù)器并進(jìn)行處理。
以上就是今天要講的內(nèi)容,本文簡單介紹了HTML的表單的知識(shí)點(diǎn),更多內(nèi)容可以查看THTML官方文檔
如果覺得有用,歡迎明天再來,繼續(xù)合集的下一篇文章學(xué)習(xí)
遇到問題,私信我!!~~
次講Qt Style Sheet(QSS),QSS是一種與CSS類似的語言,實(shí)際上這兩者幾乎完全一樣。既然談到CSS我們就有必要說一下盒模型。
在樣式中,每一個(gè)UI控件都可以抽象成一個(gè)盒模型:
盒模型由4個(gè)同心矩形組成,從外到內(nèi)分別是外邊距、邊界、內(nèi)襯和內(nèi)容,大部分UI視覺內(nèi)容都包含在內(nèi)容矩形中。默認(rèn)情況下,前三個(gè)矩形的大小屬性都是0,因此這四個(gè)矩形都重疊成一個(gè)了。這里與前三個(gè)大小相關(guān)的屬性有:
QSS與CSS相似,因此這里只大概介紹QSS的特點(diǎn):
這里我們通過一個(gè)示例程序來接觸QSS,因?yàn)閷?shí)例程序涉及多個(gè)控件但我們篇幅有限,我們只介紹菜單相關(guān)的樣式設(shè)置。菜單分為兩部分:菜單欄(QMenuBar)、菜單(QMenu)。
我們把菜單欄寬度設(shè)置成大于等于菜單item的寬度60px(這里的width不指代整個(gè)菜單欄的寬度,比較奇怪),防止item的寬度被菜單欄的寬度限制,并且設(shè)置灰色點(diǎn)邊框。
QT開發(fā)交流+貲料君羊:714620761
QMenuBar {
width:60px;
background-color:white;
border:1px dotted gray;
}
item這里代指菜單欄上的每一項(xiàng)菜單,我們設(shè)置邊框位groove以使其看起來有凹凸效果;設(shè)置margin-right,這樣兩個(gè)item之間會(huì)有合適的間隔;我們還在不同的狀態(tài)下設(shè)置不同的背景顏色,這樣交互上比較友好。
QMenuBar::item {
height:15px;
width:60px;
background:transparent;
border:2px groove gray;
margin-right:1px;
}
QMenuBar::item:selected {
color:green;
background-color:rgb(236, 242, 245);
}
QMenuBar::item:pressed {
color:green;
background-color:rgb(233, 227, 227);
}
菜單我們設(shè)置成與菜單欄一樣;菜單的item指代每一個(gè)菜單選項(xiàng),我們對其設(shè)置合適的margin和padding大小,border、item的selected和pressed狀態(tài)設(shè)置成菜單欄的樣式,使其看起來布局空間合理且與菜單欄一致:
QMenu {
background-color:white;
border:1px solid rgb(0, 171, 255);
padding:1px;
}
QMenu::item {
height:15px;
width:60px;
background-color:white;
margin:1px;
padding:2px 2px 2px 20px;
border:2px groove gray;
}
QMenu::item:selected {
color:green;
background-color:rgb(236, 242, 245);
}
QMenu::item:pressed {
color:green;
background-color:rgb(233, 227, 227);
}
我們重繪菜單之間的間隔條(separator),簡單的用淺藍(lán)色填充1像素高的背景:
QMenu::separator {
height:1px;
margin:1px;
background-color:lightblue;
}
菜單的選中指示框和子菜單我們用圖片來顯示,圖片選用png格式,保持透明通道,使其與背景一致:
QMenu::indicator:checked {
width:10px;
height:10px;
margin-left:3px;
border:0px solid blue;
image:url(:/checked-icon.png);
}
QMenu::right-arrow {
width:15px;
height:15px;
image:url(:/right-arrow.png);
}
程序的完整運(yùn)行結(jié)果如下:
優(yōu)點(diǎn):
缺點(diǎn):
*請認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。