| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import { View, Image as TaroImage, ImageProps as TaroImageProps } from '@tarojs/components'
- import { cn } from '@/utils/cn'
- import { useState } from 'react'
- export interface ImageProps extends Omit<TaroImageProps, 'onError'> {
- /**
- * 图片地址
- */
- src: string
- /**
- * 替代文本
- */
- alt?: string
- /**
- * 图片模式
- * @default "aspectFill"
- */
- mode?: TaroImageProps['mode']
- /**
- * 是否懒加载
- * @default true
- */
- lazyLoad?: boolean
- /**
- * 是否显示加载占位
- * @default true
- */
- showLoading?: boolean
- /**
- * 是否显示错误占位
- * @default true
- */
- showError?: boolean
- /**
- * 圆角大小
- */
- rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
- /**
- * 自定义样式类
- */
- className?: string
- /**
- * 图片加载失败时的回调
- */
- onError?: () => void
- /**
- * 图片加载成功的回调
- */
- onLoad?: () => void
- }
- const roundedMap = {
- none: '',
- sm: 'rounded-sm',
- md: 'rounded-md',
- lg: 'rounded-lg',
- xl: 'rounded-xl',
- full: 'rounded-full'
- }
- export function Image({
- src,
- alt = '图片',
- mode = 'aspectFill',
- lazyLoad = true,
- showLoading = true,
- showError = true,
- rounded = 'none',
- className,
- onError,
- onLoad,
- ...props
- }: ImageProps) {
- const [loading, setLoading] = useState(true)
- const [error, setError] = useState(false)
- const handleLoad = () => {
- setLoading(false)
- setError(false)
- onLoad?.()
- }
- const handleError = () => {
- setLoading(false)
- setError(true)
- onError?.()
- }
- const renderPlaceholder = () => {
- if (loading && showLoading) {
- return (
- <View className="absolute inset-0 flex items-center justify-center bg-gray-100">
- <View className="i-heroicons-photo-20-solid w-8 h-8 text-gray-400 animate-pulse" />
- </View>
- )
- }
- if (error && showError) {
- return (
- <View className="absolute inset-0 flex items-center justify-center bg-gray-100">
- <View className="i-heroicons-exclamation-triangle-20-solid w-8 h-8 text-gray-400" />
- </View>
- )
- }
- return null
- }
- return (
- <View className={cn('relative overflow-hidden', roundedMap[rounded], className)}>
- <TaroImage
- src={src}
- mode={mode}
- lazyLoad={lazyLoad}
- onLoad={handleLoad}
- onError={handleError}
- className={cn(
- 'w-full h-full',
- loading && 'opacity-0',
- !loading && !error && 'opacity-100 transition-opacity duration-300'
- )}
- {...props}
- />
- {renderPlaceholder()}
- </View>
- )
- }
|