react card slider实现滑动卡片教程示例

目录
  • 效果
  • 实现
    • card-slider.tsx
    • card-item.tsx
    • App.tsx

效果

实现

通过zIndex控制层级,opacity控制透明度,transform 控制卡片缩放程度,marginLeft控制位置,剩下的就是计算的事了

card-slider.tsx

import React from 'react'
export type CardProps = {
  opacity: number
  scale: number
  loop?: boolean
  width: number
  disablePrev?: boolean
  disableNext?: boolean
  boxWidth: number
  index?: number
  list: any[]
  renderItem(data: any): React.ReactNode
  onChange?: (index: number, data: any) => void
  style?: React.CSSProperties
}
type CardState = {
  activeIndex: number
  moving: boolean
}
export default class CardSlider extends React.Component<CardProps, CardState> {
  static defaultProps: Partial<CardProps> = {
    opacity: 0.9,
    scale: 0.9,
    loop: false,
    disablePrev: false,
    disableNext: false
  }
  constructor(props: CardProps) {
    super(props)
    this.state = {
      activeIndex: props.index || 0,
      moving: false
    }
  }
  componentWillReceiveProps(nextProps: any) {
    if (this.props.index !== nextProps.index) {
      this.setState({
        activeIndex: nextProps.index
      })
    }
  }
  // 卡片总数量
  get totalCount() {
    return this.props.list.length
  }
  // 间隔宽度
  get gridWidth() {
    const isEven = this.totalCount % 2 === 0
    const { width, boxWidth } = this.props
    return (boxWidth - width) / (isEven ? this.totalCount : this.totalCount - 1)
  }
  // 禁用prev
  get disablePrev() {
    const { loop, disablePrev } = this.props
    const { activeIndex } = this.state
    if (disablePrev) return true
    return !loop && activeIndex === 0
  }
  // 禁用prev
  get disableNext() {
    const { loop, disableNext } = this.props
    const { activeIndex } = this.state
    if (disableNext) return true
    return !loop && activeIndex === this.totalCount - 1
  }
  /**
   * offset: 是左或者右的第几个
   * direction: 1:右侧:-1:左侧
   */
  getDirection(index: number) {
    const { activeIndex } = this.state
    let direction = 1
    if (
      index - activeIndex > this.totalCount / 2 ||
      (index - activeIndex < 0 && index - activeIndex > -this.totalCount / 2)
    ) {
      direction = -1
    }
    let offset = Math.abs(index - activeIndex)
    if (offset > this.totalCount / 2) {
      offset = activeIndex + this.totalCount - index
    }
    if (index - activeIndex < -this.totalCount / 2) {
      offset = this.totalCount + index - activeIndex
    }
    return {
      direction,
      offset
    }
  }
  render() {
    const { list, renderItem, opacity, scale, width, boxWidth, style = {} } = this.props
    return (
      <div style={{ ...styles.wrapper, ...style }}>
        <div style={{ ...styles.content, width: boxWidth }}>
          {list.map((data, index) => {
            const { direction, offset } = this.getDirection(index)
            const realScale = Math.pow(scale, offset)
            return renderItem({
              key: index,
              ...data,
              style: {
                position: 'absolute',
                left: '50%',
                marginLeft: this.gridWidth * direction * offset + direction * ((width / 2) * (1 - realScale)),
                zIndex: this.totalCount - offset,
                opacity: Math.pow(opacity, offset),
                transform: `translateX(-50%) translateZ(0) scale(${realScale})`,
                transition: 'all 300ms'
              }
            })
          })}
        </div>
        {!this.disablePrev && (
          <a href="javascript:;" rel="external nofollow"  rel="external nofollow"  style={{ ...styles.btn, left: 35 }} onClick={this.handlePrev}>
            {'<'}
          </a>
        )}
        {!this.disableNext && (
          <a href="javascript:;" rel="external nofollow"  rel="external nofollow"  style={{ ...styles.btn, right: 35 }} onClick={this.handleNext}>
            {'>'}
          </a>
        )}
      </div>
    )
  }
  handlePrev = () => {
    let { activeIndex } = this.state
    if (this.disablePrev) return
    activeIndex = --activeIndex < 0 ? this.totalCount - 1 : activeIndex
    this.setState({ activeIndex })
    this.handleChange(activeIndex)
  }
  handleNext = () => {
    let { activeIndex } = this.state
    if (this.disableNext) return
    activeIndex = ++activeIndex >= this.totalCount ? 0 : activeIndex
    this.setState({ activeIndex })
    this.handleChange(activeIndex)
  }
  handleChange = (index: number) => {
    const { list, onChange } = this.props
    onChange && onChange(index, list[index])
  }
}
const styles: { [name: string]: React.CSSProperties } = {
  wrapper: {
    position: 'relative',
    display: 'flex',
    justifyContent: 'center',
    width: '100%'
  },
  content: {
    height: 210,
    position: 'relative'
  },
  btn: {
    position: 'absolute',
    top: '50%',
    transform: 'translateY(-50%)',
    width: 36,
    height: 36,
    zIndex: 99,
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    fontSize: 24
  }
}

card-item.tsx

import React from 'react'
const CardItem = ({ style, url }) => {
  return (
    <div
      style={{
        width: 375,
        height: 208,
        background: '#000',
        color: '#fff',
        borderRadius: 5,
        textAlign: 'center',
        ...style
      }}
    >
      <img src={url} width="100%" height="100%" />
    </div>
  )
}
export default CardItem

App.tsx

import CardSlider from './card-slider'
import CardItem from './card-item'
import './App.scss'
const list = [
  {
    name: '1',
    url: 'https://m15.360buyimg.com/mobilecms/jfs/t1/218369/27/14203/132191/6226a702E5a0b9236/a11294e884bc7635.jpg!cr_1053x420_4_0!q70.jpg'
  },
  {
    name: '2',
    url: 'https://m15.360buyimg.com/mobilecms/jfs/t1/158791/25/27003/106834/620c4bc2Efb15fc57/7c89841a597ce41b.jpg!cr_1053x420_4_0!q70.jpg'
  },
  {
    name: '3',
    url: 'https://m15.360buyimg.com/mobilecms/jfs/t1/117358/2/22877/138901/6228342eE68ae2c88/f8a9adb2642c1313.jpg!cr_1053x420_4_0!q70.jpg'
  },
  {
    name: '4',
    url: 'https://m15.360buyimg.com/mobilecms/jfs/t1/121592/2/24818/138081/622ccc8fEdf840f95/cd229433d699c70c.jpg!cr_1053x420_4_0!q70.jpg'
  },
  {
    name: '5',
    url: 'https://imgcps.jd.com/ling-cubic/danpin/lab/amZzL3QxLzE2Mjc4Mi8zNi85MTM4LzQ0NjQ1MS82MDQwN2Q4MUVkMDlmMWM5OC9jZWVmOWU0OWVkNzlkNjZkLnBuZw/6Zi_6L-q6L6-5pav6LeR5q2l6Z6L/5qmh6IO25aSW5bqV/60586f6fa1b18f3314204f2d/cr_1125x449_0_166/s/q70.jpg'
  },
  {
    name: '6',
    url: 'https://imgcps.jd.com/img-cubic/creative_server_cid/v2/2000755/10041170380456/FocusActivity/CkNqZnMvdDEvMjExMDQ2LzIyLzExMTc3Lzc0NTA0LzYxYTU4MzAwRWU1YjQ0OTcxL2Q5YjE5NzlmOGJkMjAzNzIuanBnEgs4NDIteGNfMF81MjABOPOOekIWChLkuprnkZ_lo6vot5HmraXpnosQAEIQCgzpkpzmg6DnmbvlnLoQAUIQCgznq4vljbPmiqLotK0QAkIKCgbkvJjpgIkQBw/cr_1053x420_4_0/s/q70.jpg'
  },
  {
    name: '7',
    url: 'https://m15.360buyimg.com/mobilecms/jfs/t1/117358/2/22877/138901/6228342eE68ae2c88/f8a9adb2642c1313.jpg!cr_1053x420_4_0!q70.jpg'
  }
]
export default function App() {
  return (
    <div style={{ paddingTop: '20%'}}>
      <CardSlider
        list={list}
        renderItem={CardItem}
        width={375}
        boxWidth={500}
        opacity={0.75}
        scale={0.9}
        disableNext={false}
        disablePrev={false}
        onChange={(index: number, data: any) => {
          console.log(index, data)
        }}
      />
    </div>
  )
}

以上就是react card slider实现滑动卡片教程的详细内容,更多关于react card slider卡片滑动的资料请关注我们其它相关文章!

(0)

相关推荐

  • react-native滑动吸顶效果的实现过程

    前言 最近公司开发方向偏向移动端,于是就被调去做RN(react-native),体验还不错,当前有个需求是首页中间吸顶的效果,虽然已经很久没写样式了,不过这种常见样式应该是so-easy,没成想翻车了,网上搜索换了几个方案都不行,最后去github上复制封装好的库来实现,现在把翻车过程记录下来. 需求效果 翻车过程 第一种方案 失败 一开始的思路是这样的,大众思路,我们需要监听页面的滚动状态,当页面滚动到要吸顶元素所处的位置的时候,我们设置它为固定定位,不过很遗憾,RN对于position属性

  • React实现双滑块交叉滑动

    本文实例为大家分享了React实现双滑块交叉滑动的具体代码,供大家参考,具体内容如下 html代码: <body> <div id="root"></div> </body> script代码: <script type="text/babel"> const root = document.querySelector('#root') class Comp extends React.Component

  • react的滑动图片验证码组件的示例代码

    业务需求,需要在系统登陆的时候,使用"滑动图片验证码",来验证操作的不是机器人. 效果图 使用方式 在一般的页面组件引用即可.onReload这个函数一般是用来请求后台图片的. class App extends Component { state = { url: "" } componentDidMount() { this.setState({ url: getImage() }) } onReload = () => { this.setState({

  • react-native 实现购物车滑动删除效果的示例代码

    购物车的功能基本上电商项目都会有的,这是一篇关于react-native的,原生android的已经好久没写了.记得以前写原生购物车的时候,遇到过产品的灵魂质问,为啥iOS的滑动删除可以,android却那么难实现的.这个时候,我就打开微信说,android的微信版也是长按进行操作,iOS的是滑动操作的,两个平台自带的系统交互操作是不一样的.当然,最后还是默默的找各种三方库去进行滑动删除. rn的项目也是找的网上的一个三方库进行列表滑动操作的,github地址react-native-swipe

  • react card slider实现滑动卡片教程示例

    目录 效果 实现 card-slider.tsx card-item.tsx App.tsx 效果 实现 通过zIndex控制层级,opacity控制透明度,transform 控制卡片缩放程度,marginLeft控制位置,剩下的就是计算的事了 card-slider.tsx import React from 'react' export type CardProps = { opacity: number scale: number loop?: boolean width: number

  • React大屏可视化脚手架教程示例

    目录 使用 create-react-app 初始化 引入 antd UI库 使用 craco 插件来自定义配置 自定义 antd 主题,使用 less 作为 css 预处理器 修改 craco.config.js 文件 craco-less .module.less 模拟vue组件中style的scope 功能 配置 craco.config.js 文件 以上操作版本记录 使用 create-react-app 初始化 # 使用了 create-react-app 的 typescript 模

  • react context优化四重奏教程示例

    目录 一.前言 二.用法 三.缺点 四.context优化 一重奏--使用PureComponent 二重奏--使用shouldComponentUpdate 三重奏--使用React.memo 四重奏--Provider再封装+props.children 总结 一.前言 我们在使用react的过程中,经常会遇到需要跨层级传递数据的情况.props传递数据应用在这种场景下会极度繁琐,且不利于维护,于是context应运而生 官方解释: Context 提供了一种在组件之间共享此类值的方式,而不

  • React+Antd 实现可增删改表格的示例

    最近写了一个小东西,模仿自己原先用vue写的项目改成react语法.写了一个可编辑的表格,期间磕磕碰碰的,打算把bug记录下.先把效果图和代码贴上去,主要用的是react+antd table表格,点击编辑,打开弹窗,弹窗内是tab切换显示不同的form表单+可编辑表格,表格内操作栏"+",表格内新增一行可编辑的数据,编辑,保存,删除这些操作就不细说也不贴效果图了 Table/index.js import React, { useState }from 'react' import

  • 基于C#实现图片滑动验证码的示例代码

    目录 图片准备 合成目标 实现 1.创建项目 2.Nuget添加ImageSharp 3.vscode打开 4.引入图片 5.生成out_bg.jpg 6.生成out_slider.png 全部代码 最后 图片准备 hole.png和slider.png为png是因为图片带有透明度. 合成目标 最终为前端生成两张图片: out_slider.png高度为344与背景图等高. 也可以打开滑动验证Demo页面,F12来观察图片. 实现 本机环境为.net 6.0.300-preview.22204.

  • vue实例成员 插值表达式 过滤器基础教程示例详解

    目录 一. 什么是Vue 二.为什么学Vue 三.如何使用Vue 下载安装? 插值表达式 四.vue特点 1.虚拟DOM 2.数据的双向绑定 3.单页面应用 4.数据驱动 五.Vue实例 六.实例成员 - 挂载点 | el - 自定义插值表达式括号| delimiters - 数据 | data - 过滤器 | filters - 方法 | methods - js对象(即字典)补充 - 插值表达式转义 | delimters - 计算属性 | computed - 监听属性 | watch 一

  • 手写vite插件教程示例

    目录 前言 1. 什么是 vite 插件 2. 为什么要写 vite 插件 创建  vite 插件通用模板 1. 初始化 2. 配置 eslint 和 prettier(可选) 3. 新增 src/index.ts 入口 4. 创建 examples 目录 5. 配置 examples/vite-vue3 项目 6. 安装 tsup 配置运行命令 7. 开发环境运行 8. 发布 vite 的插件钩子 hooks 们 1. vite 独有的钩子 2. vite 与 rollup 的通用钩子之构建阶

  • vue3中的透传attributes教程示例详解

    目录 引言 绑定样式 对象 数组 透传的attributes 透传 attributes 之样式绑定 透传 attributes 之事件绑定 特殊1:组件嵌套 特殊2:禁用透传attributes 特殊3:在 javascript 中访问透传的attributes 总结 引言 最近两年都是在使用 react 进行项目开发,看技术博客都是针对 react 和 javaScript 高级方面的,对 vue 的知识基本上遗忘的差不多了.最近开始慢慢回顾 vue 的知识以及对新的语法进行学习,为后面的计

  • webpack 5.68.0版本教程示例详解

    目录 起步 1. 基本安装 2. 配置出入口 plugin 1. html-webpack-plugin 2. progress-bar-webpack-plugin loader 1. css-loader与style-loader 2. url-loader与file-loader 3. sass-loader 4. postcss-loader 5. babel-loader 搭建环境 1. 开发环境与生产环境 2. 配置别名 代码分离 1. webpack-bundle-analyzer

  • .NET 6实现滑动验证码的示例详解

    目录 CaptchaData.cs CaptchaValidateData.cs ImageCaptchaInfo.cs Resource.cs SliderImageCaptchaInfo.cs SlideTrack.cs TemplatePair.cs Track.cs 本节创建的类全部在工程的Model目录下: CaptchaData.cs CaptchaData.cs:验证码的数据类实体 namespace SlideCaptcha.Model { public class Captch

随机推荐