原生js实现React-Native日历功能

韦宏朗
2023-12-01

直接上代码

 

import React, { Component } from 'react';
import {
  Text,
  View,
  StyleSheet,
  TouchableOpacity,
  Image,
} from 'react-native';

const weeks = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
class Calender extends Component {
  constructor(props) {
    super(props);
    this.state = {
      itemsArr: new Array(42),            // 创建一个长度42的数组
      year: new Date().getFullYear(),     // 年
      month: new Date().getMonth() + 1,   // 月
      date: new Date().getDate(),         // 日
      week: new Date().getDay(),          // 周几
      monthStartIndex: 0,                 // 本月第一天是在itemsArr的下标值
      startX: 0,                          // 开始拖拽时的offsetX
    }
  }

  // 本月有多少天
  get monthDays() {
    return new Date(this.state.year, this.state.month, 0).getDate();
  }

  // 上个月有多少天
  get lastMonthDays() {
    return new Date(this.state.year, this.state.month - 1, 0).getDate();
  }

  // 本月第一天是周几
  get weekStart() {
    return new Date(this.state.year, this.state.month - 1, 1).getDay();
  }

  componentDidMount() {
    this.fillDate();
  }

  fillDate = () => {
    const { year, month } = this.state;
    console.log(month);
    // 这个月天数遍历
    let arr = new Array(42);
    for (let i = 1; i <= this.monthDays; i++) {
      arr[this.weekStart + i - 1] = { date: i, month, year };
    }

    // 上个月天数遍历
    let j = 0;
    for (let i = this.weekStart - 1; i >= 0; i--) {
      arr[i] = { date: this.lastMonthDays - j, month: month !== 1 ? month - 1 : 12, year: month !== 1 ? year : year - 1 };
      j++;
    }

    // 计算出本月第一天是在itemsArr的下标值
    let nowMonthStartIndex = 0;
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].date === 1) {
        nowMonthStartIndex = i;
        break;
      }
    }

    // 填充下个月
    if (arr.length % 7 !== 0 || arr.includes(undefined)) {
      let k = 1;
      // 下个月开始的位置
      let nextMonthStartIndex = nowMonthStartIndex + this.monthDays;
      // 下个月的出现的天数
      // let nextMonthLength = itemsArr.length % 7 !== 0
      //     ? 7 - itemsArr.length % 7
      //     : itemsArr.length - nextMonthStartIndex;
      let nextMonthLength = arr.length - nextMonthStartIndex;
      for (let i = 0; i < nextMonthLength; i++) {
        arr[i + nextMonthStartIndex] = { date: k, month: month !== 12 ? month + 1 : 1, year: month !== 12 ? year : year +1 };
        k++;
      }
    }

    this.setState({
      itemsArr: [...arr],
      monthStartIndex: nowMonthStartIndex,
    });
  }

  isToday = item => {
    const isNowYear = new Date().getFullYear() === item.year ? true : false;
    const isNowMonth = new Date().getMonth() + 1 === item.month ? true : false;
    const isNowDay = new Date().getDate() === item.date ? true : false;
    return isNowYear && isNowMonth && isNowDay;
  }

  // 获取样式
  getDayStyle = (item, n) => {
    const { date, monthStartIndex } = this.state;
    const obj = this.isToday(item) ? {
      borderWidth: 1,
      borderStyle: 'solid',
      borderColor: '#6495ed',
    } : {};
    if (date === n - monthStartIndex + 1 && n >= monthStartIndex && n < monthStartIndex + this.monthDays) {
      return this.isToday(item) ? {
        backgroundColor: '#6495ed',
        ...obj,
      } : {
        // backgroundColor: '#6495ed',
        backgroundColor: 'red',
        borderRadius: 8,
      };
    } else if (this.isToday(item)) {
      return obj;
    }
  }

  getTextStyle = n => {
    const { date, monthStartIndex } = this.state;
    if (date === n - monthStartIndex + 1 && n >= monthStartIndex && n < monthStartIndex + this.monthDays) {
      return {
        color: '#ffffff',
      };
    } else if (n < monthStartIndex || n > (this.monthDays + monthStartIndex - 1)) {
      return {
        color: '#a9a9a9',
      };
    }
  }

  onClick = (item, index) => {
    const { year, month, monthStartIndex } = this.state;
    const obj = {};
    if (index < monthStartIndex) {
      if (month > 1) {
        obj.month = month - 1;
      } else {
        obj.year = year - 1;
        obj.month = 12;
      }
    } else if (index >= monthStartIndex + this.monthDays) {
      if (month < 12) {
        obj.month = month +1;
      } else {
        obj.year = year + 1;
        obj.month = 1;
      }
    }
    this.setState({
      ...obj,
      date: item.date,
    }, () => {
      this.fillDate();
    });
  }

  onResponderGrant = e => {
    console.log(e.nativeEvent.locationX)
    this.setState({
      startX: e.nativeEvent.locationX,
    });
  }

  onResponderRelease = e => {
    const { startX, month, year } = this.state;
    const obj = {};
    const diff = e.nativeEvent.locationX - startX;
    if (diff > 60) {
      if (month !== 1) {
        obj.month = month - 1;
      } else {
        obj.month = 12;
        obj.year = year - 1;
      }
    } else if (diff < -60) {
      if (month !== 12) {
        obj.month = month + 1;
      } else {
        obj.month = 1;
        obj.year = year + 1;
      }
    }
    if (obj.month) {
      this.setState({
        ...obj,
        date: 1,
      }, () => {
        this.fillDate();
      });
    }
  }

  onBackToDaY = () => {
    this.setState({
      year: new Date().getFullYear(),
      month: new Date().getMonth() + 1,
      date: new Date().getDate(),
    });
  }

  handleArrowLeft = () => {
    const { month, year } = this.state;
    const obj = {};
    if (month !== 1) {
      obj.month = month - 1;
    } else {
      obj.month = 12;
      obj.year = year - 1;
    }
    this.setState({
      ...obj,
      date: 1,
    }, () => {
      this.fillDate();
    });
  }

  handleArrowright = () => {
    const { month, year } = this.state;
    const obj = {};
    if (month !== 12) {
      obj.month = month + 1;
    } else {
      obj.month = 1;
      obj.year = year + 1;
    }
    this.setState({
      ...obj,
      date: 1,
    }, () => {
      this.fillDate();
    });
  }

  render() {
    const { itemsArr, year, month } = this.state;
    return (
      <View style={styles.calender}>
        <View style={styles.header}>
          <View style={styles.calenderHeader}>
            <TouchableOpacity onPress={this.handleArrowLeft}>
              <Image source={require('./arrow-left.png')} />
            </TouchableOpacity>
            <Text style={{ paddingLeft: 10, paddingRight: 7 }}>{`${year}年${month}月`}</Text>
            <TouchableOpacity onPress={this.handleArrowright}>
              <Image source={require('./arrow-right.png')} />
            </TouchableOpacity>
          </View>
          {/* <TouchableOpacity onPress={this.onBackToDaY}>
            <Text>回今天</Text>
          </TouchableOpacity> */}
        </View>
        <View style={styles.main}>
          <View style={styles.mainHeader}>
            {weeks.map((one, i) => (
              <View style={{ flex: 1 }} key={`${one}_${i}`}>
                <Text style={{ textAlign: 'center' }}>{one}</Text>
              </View>
            ))}
          </View>
  
          <View
            // onStartShouldSetResponder={() => true}
            // onResponderGrant={this.onResponderGrant}
            // onResponderMove={onResponderMove}
            // onResponderReject={onResponderReject}
            // onResponderRelease={this.onResponderRelease}
            accessible={true}
            style={styles.contentWrap}
          >
            <View
              style={styles.mainContent}
              onPress={() => console.log(222)}
            >
              {itemsArr.map((one, i) => (
                <View style={styles.contentDay} key={`${one}_${i}`} onPress={() => console.log(33)}>
                  <TouchableOpacity onPress={() => this.onClick(one, i)}>
                    <View style={[styles.itemStyle, this.getDayStyle(one, i)]}>
                      <Text style={[styles.textStyle, this.getTextStyle(i)]}>{one.date}</Text>
                    </View>
                  </TouchableOpacity>
                </View>
              ))}
            </View>
          </View>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  calender: {
    paddingHorizontal: 16,
    paddingVertical: 10,
    backgroundColor: '#ffffff'
  },
  header: {
    justifyContent: 'center',
    alignItems: 'center',
    paddingBottom: 10,
    fontSize: 16,
    fontWeight: '700',
  },
  calenderHeader: {
    alignItems: 'center',
    flexDirection: 'row',
  },
  mainHeader: {
    height: 40,
    borderTopColor: '#ccc',
    borderTopWidth: 1,
    flexDirection: 'row',
    paddingTop: 10,
  },
  contentWrap: {
    width: '100%',
  },
  mainContent: {
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
  contentDay: {
    width: '14.28%',
    justifyContent: 'center',
    alignItems: 'center',
    marginBottom: 5,
  },
  itemStyle: {
    width: 30,
    height: 30,
    borderRadius: 8,
  },
  textStyle: {
    textAlign: 'center',
    lineHeight: 30,
    color: 'black',
    borderWidth: 0,
  },
});


export default Calender;

 

 类似资料: