必须手写view模拟checkbox,放弃uni-checkbox原生组件;用view+image/text渲染图标,v-model或@click同步状态,class切换视觉态,transition实现缩放动画,并手动处理全选逻辑与无障碍支持。

uni-app里不能直接用uni-checkbox改图标或加动画
原生uni-checkbox组件的样式由平台底层渲染,iOS、Android、小程序各自实现不一致,color属性只能改勾选色,无法替换图标、加圆角、加缩放动画。强行用::before/::after伪元素 hack 会失效或在某些端(尤其是微信小程序)完全不生效。
必须手写<view></view>模拟Checkbox并手动同步状态
核心是放弃<checkbox></checkbox>标签,用<view></view>容器 + <image></image>或<text></text>渲染图标,靠v-model或@click控制checked变量,再用class切换视觉态。示例结构:
<view class="custom-checkbox" :class="{ 'checked': isChecked }"><view class="icon"><image v-if="isChecked" src="/static/checked.svg"></image></view><text class="label">同意协议</text></view>
关键点:
-
isChecked必须是响应式变量(ref或data中定义),不能只靠:class推导 -
@click里要显式触发toggle,不能只靠v-model——因为没真实<checkbox></checkbox>,没有原生change事件 - 如果需要支持
v-model双向绑定,组件内部要用defineModel(Vue 3)或props+$emit('update:modelValue')(Vue 2/3兼容)
选中态动画要用transition配合transform和opacity
直接对image加transition容易卡顿,推荐对整个.icon容器做缩放+淡入:
.custom-checkbox .icon {
transition: transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.2s;
}
.custom-checkbox.checked .icon {
transform: scale(1);
opacity: 1;
}
.custom-checkbox .icon {
transform: scale(0);
opacity: 0;
}
注意:
- 避免用
display: none触发动画,它不可过渡;用opacity+visibility或transform更稳妥 - 动画时长建议≤0.25s,过长在移动端显得迟滞
- 微信小程序里
transform需加-webkit-前缀才稳定
全选/反选逻辑必须独立维护,不能依赖checkbox-group
手写组件后,<checkbox-group></checkbox-group>就失效了,全选按钮的状态和子项状态必须手动同步:
- 全选按钮点击时,遍历所有子项
checked变量批量赋值 - 任一子项变化后,要重新计算:若全部为
true→ 全选按钮true;若全部为false→ 全选按钮false;否则为“半选”(需额外字段indeterminate) - 不要用
Array.every()或Array.some()实时计算,大数据量列表会卡顿,应缓存当前选中数量
真正麻烦的不是写动画,而是让所有端的点击反馈、禁用态、键盘焦点、无障碍读取都跟原生一致——这需要额外处理aria-checked、tabindex和touch-action,否则在部分安卓机或读屏软件下会失焦或误触。











