如何实现一个超丝滑的勾选框组件?
yuyutoo 2024-10-11 21:37 9 浏览 0 评论
前言
Zag 和 PandaCSS 都是出自 chakra 团队之手,Zag 聚焦于处理组件的逻辑,而 PandaCSS 聚焦于通过 ts 来维护样式,将两者进行搭配会有怎么样的使用体验呢?这篇文章将继续以 vuesax 中 checkbox 组件的样式作为参考,结合 Zag 和 PandaCSS 进行 vue3 版本的重构,实现一个超丝滑的勾选框组件。
使用 Zag 实现核心逻辑
Zag 中将 Checkbox 分为三个组成部分:
- root:根元素,用于包裹内部元素
- control: 控制器
- label: 标签内容
我们首先在 Zag Checkbox 文档中复制 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 函数创建了一个状态机,并且写了一个无样式的基础 checkbox 结构:
接下来我们为 checkbox 组件补充样式.
实现打勾动画图标
想要实现丝滑的勾选框效果,最直观的是打勾的动画。这个效果可以通过 SVG 或者纯 css 实现,这里我使用的是 css 来实现的。:
首先我们要想想如何实现一个勾勾的效果,??由一长一短两个斜边组成,那么我们只需要放置一个矩形,设置一定的旋转角度,并设置其中的两个边框,就能实现一个??的形状:
代码实现如下:
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)", 则设置了矩形的旋转角度,内部放置两个与矩形宽高一致的容器,分别设置 borderLeft 和 borderBottom ,这样就组成了一长一短两条线。
这里之所以需要在内部放两个容器单独设置边框,而不是直接在矩形设置边框,是为了更好的实现动画效果,长短边分别设置了两个持续时间不同的动画 checkShort 0.15s 和 checkLong 0.5s:
checkShort: {
"0%": {
height:0,
},
"100%":{
height: "full",
}
},
checkLong: {
"0%": {
width: 0,
},
"30%":{
width: 0,
},
"100%": {
width: "full",
}
}
短边从最开始就执行动画,持续时间为长边动画的 30%,长边则在 0-30% 时不执行,30% 之后开始执行,这样就能实现短边动画执行完成后,长边动画再执行的效果。
之所以不使用 animation-delay 去延迟执行长边动画,是因为这种方式会导致动画延迟执行前,长边会先展示出来,效果就不对了。如果要使用这种方式还得单独做一些动画延迟前的隐藏处理,会比较麻烦:
实现 Hover 效果
为了让用户更容易感知勾选框是可以交互的,我们为勾选框增加未勾选和关状态的 hover 效果。
未勾选状态
未勾选状态的 hover 效果,默认只有灰色边框,鼠标悬浮后变为灰色背景:
这里有个注意点,我们鼠标悬浮在勾选框的最外层,也可以触发内层的 hover 样式,如果直接使用 hover 效果是没法做到的,这样只能鼠标悬浮在边框内才能触发。
如果我们没有使用任何样式库,实现这个效果可以通过维护一个 鼠标是否 hover 的状态,并通过 onMouseEnter 和 onMouseLeave 来更新这个状态,再在内层根据这个状态动态渲染样式。
但这里我们可以使用 pandaCSS 的 group 选择器来实现。
首先在勾选框最外层元素增加 group 类名:
<label
{...api.rootProps}
class={[
css({
display: "flex",
alignItems: "center",
cursor: "pointer",
}),
+ "group",
]}
>
然后在内层的 control 元素增加基础样式:
<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 类名的元素触发 hover 时,内部的元素都可以使用 _groupHover 设置特定样式。这样我们就实现了前文图中的效果。
勾选状态
在勾选时,会有一个蓝色色块放大渐出的效果,我们先来实现这个样式。
<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 组件来实现动画效果,通过改变scale 和 opacity 实现元素大小和透明度的过渡动画,内部包裹着勾选的图标。
实现了勾选的效果,继续实现勾选后的 hover 样式。勾选后在 hover 时,勾选框的外层有一个与主题色相同的外层阴影效果:
这里我们依然使用 group 选择器来设置 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) ,前面几个设置阴影大小的参数很容易理解,但是后面阴影颜色的实现大家可能比较陌生,单独解释一下:
- token(colors.colorPalette.600):token 是 pandaCSS 中提供的一种 token 使用方法,用于在 border,boxSahdow 这种组合多个参数的样式中引用 token 值。相关文档:panda-css.com/docs/themin…
- color-mix() : color-mix 则是原生 css 的函数,用于接收两个颜色值,并返回在指定色彩模式、以指定比例混合后的颜色。相关文档:developer.mozilla.org/zh-CN/docs/…
我这里用法的含义是在 srgb 的色彩模式下,将主题色(token(colors.colorPalette.600)) 与透明色(transparent),以 40% 的比例进行混合,最终合成的颜色就是 40% 透明度的主题色。 color-mix() 函数是 pandaCSS 中推荐用户用于为内置颜色设置透明度的方法,除此以外并没有发现其他更简便的方式。
完成双向绑定
最后我们完善一下勾选框的双向绑定逻辑逻辑,
- 通过 Zag 中的 onCheckedChange 配合 emit 可以实现 Zag 内部状态向外传递
- 通过监听 propmodelValue 的改变,执行 apiRef.value.setChecked 可以实现外部状态向 Zag 内传递
实现的代码如下:
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 实现一个超丝滑的勾选框组件
原文链接:https://juejin.cn/post/7295954109404463155
相关推荐
- 深度解读Spring框架的核心原理
-
深度解读Spring框架的核心原理在Java开发的世界里,提到Spring框架,就像提起一位久经沙场的老将,它几乎成了企业级应用开发的代名词。那么,这个被无数开发者膜拜的框架究竟有何独特之处?今天,我...
- 「Spring认证」Spring 框架概述
-
Spring是最流行的企业Java应用程序开发框架。全球数以百万计的开发人员使用SpringFramework来创建高性能、易于测试和可重用的代码。Spring框架是一个开源的Java...
- 学习Spring框架 这一篇就够了
-
1.spring概述1.1Spring是什么(理解)...
- Spring框架双核解析:IOC与AOP的本质与实战
-
#Spring核心#IOC容器#AOP编程#Java框架设计...
- Spring Boot与传统Spring框架的对比:探索Java开发的新境界
-
SpringBoot与传统Spring框架的对比:探索Java开发的新境界在Java生态系统中,Spring框架无疑是一个里程碑式的存在。从最初的简单依赖注入容器,到如今覆盖企业级开发方方面面的庞大...
- Spring MVC框架源码深度剖析:从入门到精通
-
SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...
- Spring框架入门
-
一.spring是什么?Spring是分层...
- 程序员必知必会技能之Spring框架基础——面向切面编程!
-
面向切面编程AOP(AspectOrientedProgramming)与OOP(ObjectOrientedProgramming,面向对象编程)相辅相成。AOP提供了与OOP不同的抽象软件结...
- Spring Security安全框架深度解读:为你的应用穿上“钢铁铠甲”
-
SpringSecurity安全框架深度解读:为你的应用穿上“钢铁铠甲”在现代网络世界里,保护我们的应用程序免受各种威胁攻击至关重要。而在这个过程中,SpringSecurity框架无疑是我们最可...
- Spring框架的设计哲学与实现:打造轻量级的企业级Java应用
-
Spring框架的设计哲学与实现:打造轻量级的企业级Java应用Spring框架自2003年诞生以来,已成为企业级Java应用开发的代名词。它不仅仅是一个框架,更是一种设计理念和哲学的体现。本文将带你...
- Spring框架深度解析:从核心原理到底层实现的全方位避坑指南
-
一、Spring框架核心概念解析1.控制反转(IoC)与依赖注入(DI)Spring的核心思想是通过IoC容器管理对象的生命周期和依赖关系。传统开发中,对象通过new主动创建依赖对象,导致高耦合;而S...
- Java框架 —— Spring简介
-
简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入...
- Spring 框架概述,模块划分
-
Spring框架以控制反转(InversionofControl,IoC)和面向切面编程(Aspect-OrientedProgramming,AOP)为核心,旨在简化企业级应用开发,使开发者...
- spring框架怎么实现依赖注入?
-
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入。具体介绍如下:●属性set...
- Spring框架详解
-
Spring是一种开放源码框架,旨在解决企业应用程序开发的复杂性。一个主要优点就是它的分层体系结构,层次结构让你可以选择要用的组件,同时也为J2EE应用程序开发提供了集成框架。 Spring特征...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)