-webkit-mask-box-image 适配小程序

本文介绍微信小程序中由于底层规则变化导致mask属性失效的问题,提出使用-webkit-mask-box-image属性替代,确保显示效果正常的技术应对方法。

微信小程序经常修改底层规则,导致很多时候mask属性失效,此时使用-webkit-mask-box-image 这个属性可以有效的替换mask属性,使显示正常起来。

.coupon-wrap {
    mask: url($IMG_URL+'/qz_score_bg.png');
    -webkit-mask-box-image: url($IMG_URL+'/qz_score_bg.png');

<template> <!-- 蒙层 --> <view v-if="visible" class="zy-popup-select-mask-layer" @click="handleMaskClick"> <view :class="{ 'zy-popup-select-mask-false': !mask, 'zy-popup-select-mask': mask }" style="height: 100%" /> </view> <!-- 弹窗 --> <view v-if="visible" class="zy-popup-select-popup-wrapper"> <view class="zy-popup-select-popup" :style="{ 'min-height': optionsHeight + optionsHeightPx }"> <!-- 滚动区域 --> <scroll-view class="zy-popup-select-options" :class="{ 'zy-popup-select-options-scrollable': options.length > maxVisibleItems }" scroll-y :style="{ '--max-visible-height': maxVisibleHeight }" @scroll="handleScroll" > <!-- 渲染选项 --> <view v-for="(item, index) in options" :key="getValue(item)" class="zy-popup-select-option-item" :style="{ height: optionsHeight + optionsHeightPx }" @click="toggleOption(item)" > <text class="zy-popup-select-option-text"> {{ getLabel(item) }} </text> <uni-icons v-if="isSelected(item)" class="zy-popup-select-checkmark" type="checkmarkempty" color="#0f56d5" /> </view> </scroll-view> </view> </view> </template> <script> export default { name: 'ZyPopupSelect', props: { modelValue: { type: Boolean, default: false }, options: { type: Array, required: true }, labelKey: { type: String, default: 'label' }, valueKey: { type: String, default: 'value' }, maxSelected: { type: Number, default: 1 }, defaultSelected: { type: Array, default: () => [] }, mask: { type: Boolean, default: true }, optionsHeight: { type: Number, default: 57 }, optionsHeightPx: { type: String, default: 'px' }, maxVisibleItems: { type: Number, default: 3 }, isNeedSelected: { type: Boolean, default: true } }, emits: ['update:modelValue', 'change', 'confirm'], data() { return { selectedValues: [...this.defaultSelected] } }, computed: { visible() { return this.modelValue }, maxVisibleHeight() { return `${this.maxVisibleItems * this.optionsHeight}${this.optionsHeightPx}` } }, methods: { // 清空所有选择 clearSelection() { this.selectedValues = [] this.$emit('change', { item: null, selected: false }) }, // 全选功能(受 maxSelected 限制) selectAll() { if (this.maxSelected === 1) return const restCount = this.maxSelected - this.selectedValues.length if (restCount <= 0) { uni.showToast({ title: `已达上限${this.maxSelected}项`, icon: 'none' }) return } const newValues = this.options .map(this.getValue) .filter(val => !this.selectedValues.includes(val)) .slice(0, restCount) this.selectedValues.push(...newValues) this.$emit('change', { item: null, selected: true }) }, getLabel(item) { return item[this.labelKey] }, getValue(item) { return item[this.valueKey] }, isSelected(item) { return this.selectedValues.includes(this.getValue(item)) && this.isNeedSelected }, toggleOption(item) { const value = this.getValue(item) const index = this.selectedValues.indexOf(value) if (index > -1 || !this.isNeedSelected) { this.selectedValues.splice(index, 1) this.$emit('change', { item, selected: false }) } else { if (this.selectedValues.length >= this.maxSelected) { uni.showToast({ title: `最多选中 ${this.maxSelected} 项`, icon: 'none', duration: 1500 }) return } this.selectedValues.push(value) this.$emit('change', { item, selected: true }) } }, handleMaskClick() { this.close(false) // 点击蒙层视为非取消关闭 }, close(fromCancel) { this.$emit('update:modelValue', false) this.$emit('confirm', { selected: this.selectedValues.map(val => this.options.find(opt => this.getValue(opt) === val)).filter(Boolean), fromCancel }) } } } </script> <style lang="scss"> /* 蒙层外层 */ .zy-popup-select-mask-layer { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 998; pointer-events: auto; } .zy-popup-select-mask { width: 100%; background-color: #000000; opacity: 0.5; } .zy-popup-select-mask-false { opacity: 0; width: 100%; } /* 弹窗外层 */ .zy-popup-select-popup-wrapper { position: fixed; bottom: 0; left: 0; width: 100%; z-index: 999; background-color: #fff; padding-bottom: env(safe-area-inset-bottom); border-radius: 8px 8px 0 0; overflow: hidden; .zy-popup-select-popup { position: relative; } } /* 弹窗整体 */ .zy-popup-select-popup { background-color: #ffffff; width: 100%; box-sizing: border-box; display: flex; flex-direction: column; } /* 滚动区域 */ .zy-popup-select-options { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; max-height: var(--max-visible-height); } .zy-popup-select-options-scrollable { overflow-y: auto; } /* 单个选项 */ .zy-popup-select-option-item { position: relative; display: flex; align-items: center; justify-content: center; padding: 0 60rpx; box-sizing: border-box; } .zy-popup-select-option-item::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background-color: #eeeeee; transform: scaleY(0.5); transform-origin: bottom; pointer-events: none; } .zy-popup-select-option-item:last-child::after { display: none; } /* 文本样式 */ .zy-popup-select-option-text { font-size: $uni-font-size-18; color: $uni-text-color; font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc(100% - 100rpx); text-align: center; z-index: 1; } /* 对号图标 */ .zy-popup-select-checkmark { transform: translate(22px, 0px); color: $uni-primary; font-weight: bold; font-size: $uni-font-size-18; width: auto; height: auto; z-index: 2; } /* 头部样式 */ .zy-popup-select-slot-header { width: 100%; padding: 12px 0; display: flex; justify-content: space-between; align-items: center; background-color: #f9f9f9; position: relative; } .zy-popup-select-slot-header::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background-color: #eee; transform: scaleY(0.5); transform-origin: top; } .zy-popup-select-header-text { font-size: $uni-font-size-14; color: #666; } .zy-popup-select-header-actions { display: flex; gap: 16px; } .zy-popup-select-action { font-size: $uni-font-size-14; color: $uni-primary; padding: 4px 8px; border: 1px solid $uni-primary; border-radius: 4px; } </style> 根据当前组件修改成一个新的组件,要求组件传入options,options中每项左右排列并且超出可视区域的时候可以左右滑动,每项可通过options中不同的value展示不同的图片,并在图片下方展示options每项的label,点击每项可调用方法,传入当前点击项的value和每项中的options
最新发布
09-26
<template> <!-- 蒙层(全屏层级,仅覆盖非操作区) --> <view v-if="visible" class="zy-select-mask-layer" @click="handleMaskClick"> <view :class="{ 'zy-select-mask-false': !mask, 'zy-select-mask': mask }" :style="{ height: '100%' }" /> </view> <!-- 弹窗(独立层级,浮在蒙层之上) --> <view v-if="visible" class="zy-select-popup-wrapper"> <view class="zy-select-popup"> <!-- 可选项滚动区域 --> <scroll-view class="zy-select-options" :class="{ 'zy-select-options-scrollable': options.length > maxVisibleItems }" scroll-y :style="{ '--max-visible-height': maxVisibleHeight }" > <view v-for="(item, index) in options" :key="getValue(item)" class="zy-select-option-item" :class="{ 'zy-select-option-selected': isSelected(item) }" @click="toggleOption(item)" > <text class="zy-select-option-text" :class="{ 'zy-select-option-text-selected': isSelected(item) }"> {{ getLabel(item) }} </text> <uni-icons v-if="isSelected(item)" class="zy-select-checkmark" type="checkmarkempty" color="#0f56d5" /> </view> </scroll-view> <!-- 分割线 --> <view class="zy-select-divider" /> <!-- 取消按钮 --> <view class="zy-select-cancel" @click="handleCancel"> <text class="zy-select-cancel-text">取消</text> </view> </view> </view> </template> <script> export default { name: 'ZySelect', props: { modelValue: { type: Boolean, default: false }, options: { type: Array, required: true }, labelKey: { type: String, default: 'label' }, valueKey: { type: String, default: 'value' }, maxSelected: { type: Number, default: 1 }, defaultSelected: { type: Array, default: () => [] }, mask: { type: Boolean, default: true }, maxVisibleItems: { type: Number, default: 3 } }, emits: ['update:modelValue', 'change', 'confirm'], data() { return { selectedValues: [...this.defaultSelected] } }, computed: { visible() { return this.modelValue }, maxVisibleHeight() { return `${this.maxVisibleItems * 57}px` } }, mounted() {}, methods: { getLabel(item) { return item[this.labelKey] }, getValue(item) { return item[this.valueKey] }, isSelected(item) { return this.selectedValues.includes(this.getValue(item)) }, toggleOption(item) { const value = this.getValue(item) const index = this.selectedValues.indexOf(value) if (index > -1) { this.selectedValues.splice(index, 1) this.$emit('change', { item, selected: false }) } else { if (this.selectedValues.length >= this.maxSelected) { uni.showToast({ title: `最多选中 ${this.maxSelected} 项`, icon: 'none', duration: 1500 }) return } this.selectedValues.push(value) this.$emit('change', { item, selected: true }) } }, handleCancel() { this.selectedValues = [...this.defaultSelected] // 恢复默认 this.close(true) }, handleMaskClick() { this.close(false) // 点击蒙层视为非取消关闭 }, close(fromCancel) { this.$emit('update:modelValue', false) this.$emit('confirm', { selected: this.selectedValues.map(val => this.options.find(opt => this.getValue(opt) === val)).filter(Boolean), fromCancel }) } } } </script> <style lang="scss"> /* 蒙层外层:覆盖全屏,但只显示上半部分 */ .zy-select-mask-layer { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 998; /* 蒙层层级 */ pointer-events: auto; } .zy-select-mask { width: 100%; background-color: #000000; opacity: 0.5; } .zy-select-mask-false { opacity: 0; width: 100%; } /* 弹窗外层:固定在底部,浮在蒙层之上 */ .zy-select-popup-wrapper { position: fixed; bottom: 0; left: 0; width: 100%; z-index: 999; /* 高于蒙层 */ background-color: #fff; /* 确保不侵入底部安全区 */ padding-bottom: env(safe-area-inset-bottom); /* ✅ 关键:顶部圆角 */ border-radius: 8px 8px 0 0; /* ✅ 确保子元素不溢出圆角边界 */ overflow: hidden; } /* 弹窗整体布局改为 flex 列布局 */ .zy-select-popup { background-color: #ffffff; width: 100%; box-sizing: border-box; display: flex; flex-direction: column; min-height: 57px; /* 防止空内容塌陷 */ } /* 滚动区域:允许纵向滚动,限制最大高度 */ .zy-select-options { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; max-height: var(--max-visible-height); /* 使用 CSS 变量 */ } .zy-select-options-scrollable { overflow-y: auto; } /* 单个选项保持居中等样式不变 */ .zy-select-option-item { position: relative; height: 57px; display: flex; align-items: center; justify-content: center; /* 主轴居中:X 轴居中 */ padding: 0 60rpx; box-sizing: border-box; } .zy-select-option-item::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; /* 先设成 1px */ background-color: #eeeeee; transform: scaleY(0.5); /* 缩小一半 → 视觉上就是 0.5px */ transform-origin: bottom; pointer-events: none; } .zy-select-option-item:last-child::after { display: none; } /* 文本:完全居中,不受对号影响 */ .zy-select-option-text { font-size: $uni-font-size-18; color: $uni-text-color; font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc(100% - 100rpx); /* 防止太长碰到右边 */ text-align: center; z-index: 1; /* 确保在上层 */ } /* 选中状态文字颜色 */ .zy-select-option-selected .zy-select-option-text { color: $uni-primary; } .zy-select-option-text-selected { transform: translate(8px, 0px); } /* ✅ 对号:绝对定位,脱离文档流 */ .zy-select-checkmark { transform: translate(22px, 0px); color: $uni-primary; font-weight: bold; font-size: $uni-font-size-18; width: auto; height: auto; z-index: 2; /* 确保显示在上面 */ } /* 分割线:固定在取消按钮上方 */ .zy-select-divider { height: 8px; background-color: $uni-bg-color; /* 不再随内容滚动 */ } /* 取消按钮 */ .zy-select-cancel { height: 57px; display: flex; align-items: center; justify-content: center; } .zy-select-cancel-text { font-size: $uni-font-size-18; color: $uni-text-color; text-align: center; font-weight: 400; } </style> 请你为当前组件设计插槽,注意这个组件是vue3小程序的项目使用的
09-19
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangxtqd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值