| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- <template>
- <view
- class="ripple-container"
- :class="[wlType ? 'ripple-right' : 'ripple-error', { 'ripple-animate': !imageError }]"
- :style="rippleStyle"
- @click="handleClick"
- >
- <view class="custom-content" :style="contentStyle">
- <!-- 图片类型 -->
- <image
- v-if="contentType === 'image'"
- :src="resolvedImageSrc"
- class="custom-image"
- @error="handleImageError"
- />
-
- <!-- 自定义视图类型 -->
- <view v-else-if="contentType === 'view'" class="custom-view">
- <slot name="custom-content"></slot>
- </view>
-
- <!-- 图标类型 -->
- <view v-else class="custom-icon" v-html="customContent"></view>
- </view>
- </view>
- </template>
- <script>
- // 兼容 UniApp 和 UniAppX 的写法
- export default {
- name: "WlRipple",
- props: {
- wlType: {
- type: Boolean,
- default: true
- },
- customContent: {
- type: String,
- default: '<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>'
- },
- contentType: {
- type: String,
- default: 'icon',
- validator: (value) => ['icon', 'image', 'view'].includes(value)
- },
- rippleColor: {
- type: String,
- default: '#FFF'
- },
- iconColor: {
- type: String,
- default: '#008000'
- },
- rippleEffectColor: {
- type: String,
- default: 'rgba(0,128,0,0.1)'
- },
- width: {
- type: [Number, String],
- default: 100
- },
- height: {
- type: [Number, String],
- default: 100
- },
- contentPadding: {
- type: [Number, String],
- default: 0
- },
- maxRippleSize: {
- type: [Number, String],
- default: 80
- }
- },
- data() {
- return {
- imageError: false
- };
- },
- computed: {
- rippleStyle() {
- return {
- backgroundColor: this.rippleColor,
- '--ripple-color': this.rippleEffectColor,
- '--max-ripple-size': this.px(this.maxRippleSize),
- width: this.px(this.width),
- height: this.px(this.height),
- borderRadius: this.px(this.width)
- };
- },
- contentStyle() {
- return {
- color: this.iconColor,
- padding: this.px(this.contentPadding)
- };
- },
- resolvedImageSrc() {
- if (this.contentType !== 'image') return '';
- return this.resolvePath(this.customContent);
- }
- },
- methods: {
- px(value) {
- // 处理 UniAppX 和 UniApp 的单位差异
- if (typeof value === 'string' && value.endsWith('px')) {
- return value;
- }
- return `${value}px`;
- },
- resolvePath(path) {
- if (!path || path === '') return '';
- if (path.startsWith('http')) return path;
- let normalizedPath = path.replace(/^@\/|^\/static\/|^static\//, '');
- normalizedPath = `/static/${normalizedPath}`;
- return normalizedPath;
- },
- handleImageError(e) {
- console.error('Image load error:', e.detail || e);
- this.imageError = true;
- this.$emit('image-error', e);
- },
- handleClick(e) {
- this.$emit('click', e);
- }
- }
- };
- </script>
- <style scoped>
- /* 基础样式 */
- .ripple-container {
- position: relative;
- overflow: hidden;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- }
- /* 水波动画 */
- .ripple-animate {
- animation: ripple 0.6s linear infinite;
- }
- @keyframes ripple {
- 0% {
- box-shadow:
- 0 0 0 0 var(--ripple-color),
- 0 0 0 calc(var(--max-ripple-size) / 4) var(--ripple-color),
- 0 0 0 calc(var(--max-ripple-size) / 2) var(--ripple-color),
- 0 0 0 calc(var(--max-ripple-size) * 3 / 4) var(--ripple-color);
- }
- 100% {
- box-shadow:
- 0 0 0 calc(var(--max-ripple-size) / 4) var(--ripple-color),
- 0 0 0 calc(var(--max-ripple-size) / 2) var(--ripple-color),
- 0 0 0 calc(var(--max-ripple-size) * 3 / 4) var(--ripple-color),
- 0 0 0 var(--max-ripple-size) rgba(0,0,0,0);
- }
- }
- /* 内容区域 */
- .custom-content {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- }
- /* 图标样式 */
- .custom-icon >>> svg {
- width: 100%;
- height: 100%;
- fill: currentColor;
- }
- /* 图片样式 */
- .custom-image {
- width: 100%;
- height: 100%;
- border-radius: 50%;
- object-fit: cover;
- }
- /* 自定义视图样式 */
- .custom-view {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- </style>
|