提早須要準備好react腳手架開發環境,因爲react已經不支持在頁面內部經過jsx.transform來轉義,咱們就本身大了個簡易的開發環境css
建立一個文件夾,命名爲react-canlendarhtml
cd ./react-canlendar
運行react
npm init
一路enter咱們獲得一個package.json的文件webpack
安裝幾個咱們須要的腳手架依賴包程序員
npm install awesome-typescript-loader typescript webpack webpack-cli -D
安裝幾個咱們須要的類庫web
npm install @types/react react react-dom --save
基礎類庫安裝完畢,開始構建webpack配置typescript
新建一個目錄config,config下面新增一個文件,名字叫作webpack.jsnpm
var path = require('path') module.exports = { entry: { main: path.resolve(__dirname, '../src/index.tsx') }, output: { filename: '[name].js' }, resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }, module: { rules: [ {test: /\.tsx?$/, use: ['awesome-typescript-loader']} ] } }
還須要建立一個index.html文件,這是咱們的入口文件json
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="root"></div> <script src="./dist/main.js"></script> </body> </html>
以上環境只是一個極簡單的環境,真實環境要比這個複雜的多。數組
好了,言歸正傳,咱們仍是聚焦到日曆組件的開發中來吧
建立一個src文件夾,內部建立一個index.tsx文件。
這個入口文件很簡單就是一個掛載
import * as React from 'react' import * as ReactDOM from 'react-dom' ReactDOM.render(( <div> test </div> ), document.getElementById('root'))
ok,打開頁面能夠看到頁面正常顯示了test字樣。
咱們須要建立Calendar組件了。
建立一個components文件夾,內部建立一個Calendar.tsx文件。
import * as React from 'react' export default class Calendar extends React.Component { render() { return (<div> 日曆 </div>) } }
在index.tsx中把Calendar.tsx引入,並使用起來。因而index.tsx變成這個樣子。
import * as React from 'react' import * as ReactDOM from 'react-dom' import Calendar from './components/Calendar' ReactDOM.render(( <div> <Calendar/> </div> ), document.getElementById('root'))
能夠看到頁面顯示了日曆字樣。
要顯示日曆,首先須要顯示日曆這個大框以及內部的一個個小框。實現這種佈局最簡單的佈局就是table了
因此咱們首先建立的是這種日曆table小框框,以及表頭的星期排列。
import * as React from 'react' const WEEK_NAMES = ['日', '一', '二', '三', '四', '五', '六'] const LINES = [1,2,3,4,5,6] export default class Calendar extends React.Component { render() { return (<div> <table cellPadding={0} cellSpacing={0} className="table"> <thead> <tr> { WEEK_NAMES.map((week, key) => { return <td key={key}>{week}</td> }) } </tr> </thead> <tbody> { LINES.map((l, key) => { return <tr key={key}> { WEEK_NAMES.map((week, index) => { return <td key={index}>{index}</td> }) } </tr> }) } </tbody> </table> </div>) } }
能夠看到咱們使用了一個星期數組做爲表頭,咱們按照慣例是從週日開始的。你也能夠從其餘星期開始,不過會對下面的日期顯示有影響,由於每月的第一天是周幾決定第一天顯示在第幾個格子裏。
那爲何行數要6行呢?由於咱們是按照最大行數來肯定表格的行數的,若是一個月有31天,而這個月的第一天恰好是週六。就確定會顯示6行了。
爲了顯示好看,我直接寫好了樣式放置在index.html中了,這個不重要,不講解。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { margin: 0; padding: 0; } .table { border-collapse:collapse; border-spacing:0; } .table td{ border: 1px solid #ddd; padding: 10px; } .table caption .caption-header{ border-top: 1px solid #ddd; border-right: 1px solid #ddd; border-left: 1px solid #ddd; padding: 10px; display: flex; justify-content: space-between; } .table caption .caption-header .arrow { cursor: pointer; font-family: "宋體"; transition: all 0.3s; } .table caption .caption-header .arrow:hover { opacity:0.7; } </style> </head> <body> <div id="root"></div> <script src="./dist/main.js"></script> </body> </html>
下面就要開始顯示日期了,首先要把當前月份的日期顯示出來,咱們先在組件的state中定義當前組件的狀態
state = { month: 0, year: 0, currentDate: new Date() }
咱們定義一個方法獲取當前年月,爲何不須要獲取日,由於日曆都是按月顯示的。獲取日如今看來對咱們沒有意義,因而新增一個方法,設置當前組件的年月
setCurrentYearMonth(date) { var month = Calendar.getMonth(date) var year = Calendar.getFullYear(date) this.setState({ month, year }) } static getMonth(date: Date): number{ return date.getMonth() } static getFullYear(date: Date): number{ return date.getFullYear() }
建立兩個靜態方法獲取年月,爲何是靜態方法,由於與組件的實例無關,最好放到靜態方法上去。
要想繪製一個月還須要知道一個月的天數吧,纔好繪製吧
因此咱們建立一個數組來表示月份的天數
const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] // 暫定2月份28天吧
組件上建立一個函數,根據月份獲取天數,也是靜態的
static getCurrentMonthDays(month: number): number { return MONTH_DAYS[month] }
下面還有一個重要的事情,就是獲取當前月份第一天是周幾,這樣子就能夠決定把第一天繪製在哪裏了。首先要根據年月的第一天得到date,根據這個date獲取周幾。
static getDateByYearMonth(year: number, month: number, day: number=1): Date { var date = new Date() date.setFullYear(year) date.setMonth(month, day) return date }
這裏得到每月的第一天是周幾了。
static getWeeksByFirstDay(year: number, month: number): number { var date = Calendar.getDateByYearMonth(year, month) return date.getDay() }
好了,開始在框子插入日期數字了。由於每一個日期都是不同的,這個二維數組能夠先計算好,或者經過函數直接插入到jsx中間。
static getDayText(line: number, weekIndex: number, weekDay: number, monthDays: number): any { var number = line * 7 + weekIndex - weekDay + 1 if ( number <= 0 || number > monthDays ) { return <span> </span> } return number }
看一下這個函數須要幾個參數哈,第一個行數,第二個列數(周幾),本月第一天是周幾,本月天數。line * 7 + weekIndex
表示當前格子原本是幾,減去本月第一天星期數字。爲何+1,由於索引是從0開始的,而天數則是從1開始。那麼<0 || >本月最大天數的
則過濾掉,返回一個空span,只是爲了撐開td。其餘則直接返回數字。
import * as React from 'react' const WEEK_NAMES = ['日', '一', '二', '三', '四', '五', '六'] const LINES = [1,2,3,4,5,6] const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] export default class Calendar extends React.Component { state = { month: 0, year: 0, currentDate: new Date() } componentWillMount() { this.setCurrentYearMonth(this.state.currentDate) } setCurrentYearMonth(date) { var month = Calendar.getMonth(date) var year = Calendar.getFullYear(date) this.setState({ month, year }) } static getMonth(date: Date): number{ return date.getMonth() } static getFullYear(date: Date): number{ return date.getFullYear() } static getCurrentMonthDays(month: number): number { return MONTH_DAYS[month] } static getWeeksByFirstDay(year: number, month: number): number { var date = Calendar.getDateByYearMonth(year, month) return date.getDay() } static getDayText(line: number, weekIndex: number, weekDay: number, monthDays: number): any { var number = line * 7 + weekIndex - weekDay + 1 if ( number <= 0 || number > monthDays ) { return <span> </span> } return number } static formatNumber(num: number): string { var _num = num + 1 return _num < 10 ? `0${_num}` : `${_num}` } static getDateByYearMonth(year: number, month: number, day: number=1): Date { var date = new Date() date.setFullYear(year) date.setMonth(month, day) return date } checkToday(line: number, weekIndex: number, weekDay: number, monthDays: number): Boolean { var { year, month } = this.state var day = Calendar.getDayText(line, weekIndex, weekDay, monthDays) var date = new Date() var todayYear = date.getFullYear() var todayMonth = date.getMonth() var todayDay = date.getDate() return year === todayYear && month === todayMonth && day === todayDay } monthChange(monthChanged: number) { var { month, year } = this.state var monthAfter = month + monthChanged var date = Calendar.getDateByYearMonth(year, monthAfter) this.setCurrentYearMonth(date) } render() { var { year, month } = this.state console.log(this.state) var monthDays = Calendar.getCurrentMonthDays(month) var weekDay = Calendar.getWeeksByFirstDay(year, month) return (<div> {this.state.month} <table cellPadding={0} cellSpacing={0} className="table"> <caption> <div className="caption-header"> <span className="arrow" onClick={this.monthChange.bind(this, -1)}><</span> <span>{year} - {Calendar.formatNumber(month)}</span> <span className="arrow" onClick={this.monthChange.bind(this, 1)}>></span> </div> </caption> <thead> <tr> { WEEK_NAMES.map((week, key) => { return <td key={key}>{week}</td> }) } </tr> </thead> <tbody> { LINES.map((l, key) => { return <tr key={key}> { WEEK_NAMES.map((week, index) => { return <td key={index} style={{color: this.checkToday(key, index, weekDay, monthDays) ? 'red' : '#000'}}> {Calendar.getDayText(key, index, weekDay, monthDays)} </td> }) } </tr> }) } </tbody> </table> </div>) } }
能夠看到最終的代碼多了一些東西,由於我加了月份的切換。
還記的上文咱們把二月份天數寫28天嘛?要不大家本身改改,判斷一下閏年。
建立了一個程序員交流微信羣,你們進羣交流IT技術
若是已過時,能夠添加博主微信號15706211347,拉你進羣