改造微信小程序Swiper组件,自定义切换动画

index.tsx

TypeScript 复制代码
import { FC, memo, PropsWithChildren, useEffect, useState, useRef, useMemo } from 'react'
import { Swiper as BaseSwiper, SwiperItem, View } from '@tarojs/components'
import { Button } from '@wmeimob/taro-design'
import classNames from 'classnames'
import styles from './index.module.less'
import Taro from '@tarojs/taro'
import { Flex } from '~/components'
import { vibrateShort } from '~/utils/util'
import { SwiperProps } from './const'
// import { getBarHeight } from '~/pages/service/util'

/**
 * Swiper
 * @param props
 * @returns
 */
const Component: FC<PropsWithChildren<SwiperProps>> = ({ current, modes, isDot, onMode, children }) => {
  const [currentIndex, setCurrentIndex] = useState(current ?? 0)
  // const [dx, setDx] = useState(0)
  const currentRef = useRef<number>(0)

  useEffect(() => {
    setCurrentIndex(current)
    return () => {
      Taro.setStorageSync('U1_LASTTIME_MODE_CURRENT', currentRef.current)
    }
  }, [current])
 
  // const getScale = useMemo(() => {
  //   const { windowWidth } = Taro.getSystemInfoSync()
  //   return Math.min(Math.max(dx / (windowWidth - 20), 0.88), 1)
  // }, [dx])
  // console.log('Taro', Taro.getSystemInfoSync().windowWidth, getScale)

  return (
    <View className={styles.container}>
      <BaseSwiper
        // autoplay
        // interval={2500}
        easingFunction='easeOutCubic'
        circular
        duration={1000}
        previousMargin='8px'
        nextMargin='12px'
        current={currentIndex}
        className={styles.swiperBox}
        onChange={(event) => {
          setCurrentIndex(event.detail.current)
          currentRef.current = event.detail.current
          vibrateShort()
        }}
        // style={{
        //   height: Taro.getSystemInfoSync().windowHeight - getBarHeight() - 44 - 34
        // }}
        // onTransition={(event) => {
        //   console.log('onTransition', event.detail.dx)
        //   setDx(event.detail.dx)
        // }}
      >
        {modes.map((mode, index) => {
          return (
            <SwiperItem key={`key-${index}`}>
              <Flex
                column
                style={{
                  backgroundImage: `url(${mode?.bgImg})`
                  // transform: index === currentIndex ? ` scale(${getScale})` : undefined
                }}
                className={classNames(styles.swiperItem,{
                  [styles.swiperItemActive]: index === currentIndex,
                  [styles.swiperItemOutRight]: index > currentIndex,
                  [styles.swiperItemOutLeft]: index < currentIndex
                })}
              >
                <Button
                    className={styles.button}
                    type="main"
                    text='开始护肤'
                    onClick={(event) => {
                      event.stopPropagation()
                      onMode?.(mode)
                    }}
                   />
              </Flex>
            </SwiperItem>
          )
        })}
      </BaseSwiper>
      {isDot && (
        <Flex alignCenter className={styles.dots}>
          {modes.map((_, index) => {
            return (
              <View
                key={`key-${index}`}
                className={classNames(styles.dot, {
                  [styles.dotActive]: index === currentIndex
                })}
              />
            )
          })}
        </Flex>
      )}
      {children}
    </View>
  )
}

const Swiper = memo(Component)
export default Swiper

index.less

css 复制代码
.container {
  position: relative;
}

.swiperBox {
  position: relative;
  width: 100vw;
  height: calc(100vw * 638 / 375);

  .swiperItem {
    position: relative;
    width: calc(100% - 5px);
    height: 100%;
    background-size: cover;
    background-repeat: no-repeat;
    background-position: center;
    border-radius: 8px;
    margin-left: 5px;
    padding: 15px;

    .button {
      position: absolute;
      top: calc(100vw * 175 / 375);
      
      width: 130px;
      height: 42px;
      
      border-radius: 42px;
      background: #EFD7B1;
      font-weight: 500;
    }
  }

  .swiperItemActive {
    transform: scale(1);
    transition: all 0.8s ease 0s;
  }

  .swiperItemOutLeft {
    transition: all 0.8s ease-out 0.1s;
    transform: translate(20px, 38px) scale(0.88);
  }

  .swiperItemOutRight {
    transition: all 0.8s ease-out 0.1s;
    transform: translate(-20px, 38px) scale(0.88);
  }
}

.dots {
  position: absolute;
  bottom: -15px;
  // gap: 5px; // 有些手机型号无效
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;

  .dot {
    width: 12px;
    height: 2px;
    background-color: #5B5C5C;
    transition: all 0.5s;
    margin-right: 5px;
  }

  .dotActive {
    background-color: #fff;
  }
}

conts.ts

TypeScript 复制代码
export interface SwiperProps {
  current: number
  modes: xxxx[] // xxx是需要定义的inteface结构
  isDot?: boolean
  onMode?: (plan: CareModelDTO) => void
}

改造Swiper组件,符合业务的设计和动画效果

相关推荐
wangfpp11 分钟前
产品:这个文字颜色能不能根据背景图自动换?
前端·面试·产品
LJianK118 分钟前
vxe-table 的 checkbox复选框
前端·html
字节高级特工18 分钟前
C++从入门到熟悉:深入剖析const和constexpr
前端·c++·人工智能·后端·算法
Alan Lu Pop26 分钟前
个人精选 Skills 清单
前端·react.js·cursor
木斯佳34 分钟前
前端八股文面经大全:bilibili前端一面(2026-03-26)·面经深度解析
前端·面试·笔试·校招·promise
早點睡39034 分钟前
ReactNative项目OpenHarmony三方库集成实战:react-native-date-picker
javascript·react native·react.js
吴声子夜歌35 分钟前
TypeScript——BigInt、展开运算符、解构和可选链运算符
前端·javascript·typescript
Muen38 分钟前
Swift多线程方案-Concurrency
前端
floret. 小花1 小时前
Vue3 知识点总结 · 2026-03-27
前端·面试·electron·学习笔记·vue3
网络点点滴1 小时前
Vue 3 中的 readonly 和 shallowReadonly:保护数据不被修改
javascript·vue.js·ecmascript