Gallery By React
根据慕课网教程写的React练手小项目:
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import Data from './data/imgData.json' //创建图片组件 class ImgFigure extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this);//如果在调用handleClick方法后面加(),应该为其绑定this } handleClick(e) {//如果图片居中了就翻转,未居中则居中 if (this.props.arrange.isCenter) { this.props.inverse(); } else { this.props.center(); } e.stopPropagation();//终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播 e.preventDefault();//阻止点击事件的默认行为 } render() { var styleObj = {}; //如果props属性中指定了这张图片的位置,则使用 if (this.props.arrange.pos) { styleObj.left = this.props.arrange.pos.left; styleObj.top = this.props.arrange.pos.top; } //如果图片的旋转角度有值并且不为0,添加旋转角度 if (this.props.arrange.rotate) { (['Moz', 'Ms', 'Webkit', '']).forEach((value) => { styleObj[value + 'Transform'] = 'rotate(' + this.props.arrange.rotate + 'deg)'; }); } //如果图片居中,则设置zIndex使其在最上层 if (this.props.arrange.isCenter) { styleObj.zIndex = 11; } //如果图片翻转,则添加“is-inverse”类 let imgFigureClassName = 'img-figure'; imgFigureClassName += this.props.arrange.isInverse ? ' is-inverse ' : ''; return ( <figure style={styleObj} className={imgFigureClassName} onClick={this.handleClick}> <img src={this.props.data.imgUrl} alt={this.props.data.title}/> <figcaption> <h2 className='img-title'>{this.props.data.title}</h2> </figcaption> <div className="img-back" onClick={this.handleClick}> <p> {this.props.data.desc} </p> </div> </figure> ); } } //控制结构组件(思路基本与imgFigure相同) class ControllerUnit extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(e) { if (this.props.arrange.isCenter) { this.props.inverse(); } else { this.props.center(); } e.preventDefault(); e.stopPropagation(); } render() { var ControllerUnitClassName = 'controllerUnit'; if (this.props.arrange.isCenter) { ControllerUnitClassName += ' is-center'; } if (this.props.arrange.isInverse) { ControllerUnitClassName += ' is-inverse'; } return ( <span className={ControllerUnitClassName} onClick={this.handleClick}></span> ); } } //背景舞台组件 class Stage extends React.Component { constructor(props) { super(props); this.state = { //构建图片信息数组包括位置,角度,是否翻转和居中 imgInfoArr: [ { pos: { left: '0', top: '0' }, rotate:0, isInverse:false, isCenter:false } ] }; //内部变量:记录中心位置、水平垂直方向取值范围 this.Constant = { centerPos: { left: 0, top: 0 }, hPosRange: { //水平方向取值范围 leftSecX: [0, 0], rightSecX: [0, 0], y: [0, 0] }, vPosRange: { //垂直方向取值范围 x: [0, 0], topY: [0, 0] } }; } //组件加载以后,为每张图片加载位置的范围 componentDidMount() { //分别获取舞台和图片组件的宽高 var stageDOM = ReactDOM.findDOMNode(this.refs.stage), stageH = stageDOM.scrollHeight, stageW = stageDOM.scrollWidth, halfStageH = Math.ceil(stageH / 2), halfStageW = Math.ceil(stageW / 2); var imgFigureDOM = ReactDOM.findDOMNode(this.refs.imgFigure0), imgH = imgFigureDOM.scrollHeight, imgW = imgFigureDOM.scrollWidth, halfImgH = Math.ceil(imgH / 2), halfImgW = Math.ceil(imgW / 2); this.Constant = { //计算取值范围并赋值给Constant centerPos: { left: halfStageW - halfImgW, top: halfStageH - halfImgH + 50 }, hPosRange: { //水平方向取值范围 leftSecX: [-halfImgW, halfStageW - halfImgW * 3], rightSecX: [halfStageW + halfImgW, stageW - halfImgW], y: [-halfImgH, stageH - halfImgH] }, vPosRange: { //垂直方向取值范围 x: [halfStageW - imgW, halfStageW], topY: [-halfImgH, halfStageH - halfImgH * 3] } }; //加载完成后进行布局 this.setPosition(0); } //获取low、high之间的随机值 getRandomValue(low, high) { return Math.ceil(Math.random() * (high - low) + low); } //获取正负30之间的随机值 get30DeRandom() { return (Math.random() > 0.5 ? "" : '-') + Math.ceil(Math.random() * 30); } //翻转 inverse(index) { return function () { var imgInfoArr = this.state.imgInfoArr; imgInfoArr[index].isInverse = !imgInfoArr[index].isInverse; this.setState({ imgInfoArr:imgInfoArr }); }.bind(this); } //居中 center(index) { return () => { this.setPosition(index); } } //设置图片位置,centerIndex是居中图片的索引值 setPosition(cneterIndex) { var centerPos = this.Constant.centerPos, hLeftXMin = this.Constant.hPosRange.leftSecX[0], hLeftXMax = this.Constant.hPosRange.leftSecX[1], hRightXMin = this.Constant.hPosRange.rightSecX[0], hRightXMax = this.Constant.hPosRange.rightSecX[1], hYMin = this.Constant.hPosRange.y[0], hYMax = this.Constant.hPosRange.y[1], vXMin = this.Constant.vPosRange.x[0], vXMax = this.Constant.vPosRange.x[1], vYMin = this.Constant.vPosRange.topY[0], vYMax = this.Constant.vPosRange.topY[1]; var imgInfoArr = this.state.imgInfoArr; //获取居中图片并设置其值 var cneterArr = imgInfoArr.splice(cneterIndex, 1); cneterArr[0].pos = centerPos; cneterArr[0].rotate = 0; cneterArr[0].isCenter = true; //获取在上方的图片(0或1个) var topImgNum = Math.floor(Math.random() * 2); var topIndex = Math.ceil(Math.random() * (imgInfoArr.length - topImgNum)); var topArr = imgInfoArr.splice(topIndex, topImgNum); topArr.forEach(function(element, index) { topArr[index] = { pos: { left: this.getRandomValue(vXMin, vXMax), top: this.getRandomValue(vYMin, vYMax) }, rotate:this.get30DeRandom(), isCenter:false, } }.bind(this)); //将剩下的图片对半分放于左右两边 for (let i = 0, j = imgInfoArr.length, k = j / 2; i < j; i++) { var hPosLOrR = null; if (i < k) { hPosLOrR = this.getRandomValue(hLeftXMin, hLeftXMax); } else { hPosLOrR = this.getRandomValue(hRightXMin, hRightXMax); } imgInfoArr[i] = { pos: { left:hPosLOrR, top:this.getRandomValue(hYMin, hYMax) }, rotate: this.get30DeRandom(), isCenter:false, }; } //按顺序将顶部与中心图片数据传回数组 if (topArr && topArr[0]) { imgInfoArr.splice(topIndex, 0, topArr[0]); } imgInfoArr.splice(cneterIndex, 0, cneterArr[0]); this.setState({ imgInfoArr:imgInfoArr }); } render() { var imgs = []; var controllers = []; Data.forEach( function(value, index) { //初始化图片数据数组,不能用setState方法,否则每次都会触发渲染 if (!this.state.imgInfoArr[index]) { this.state.imgInfoArr[index] = { pos: { left: 0, top: 0 }, rotate: 0, isInverse: false, isCenter:false } } value.imgUrl = require('./images/' + value.fileName); imgs.push(<ImgFigure arrange={this.state.imgInfoArr[index]} key={index} ref={'imgFigure' + index} data={value} inverse={this.inverse(index)} center={this.center(index)}/>); controllers.push(<ControllerUnit key={index} arrange={this.state.imgInfoArr[index]} inverse={this.inverse(index)} center={this.center(index)} />) }.bind(this)); return ( <section className="stage" ref='stage'> <section className="imgStage">{imgs}</section> <nav className="controller">{controllers}</nav> </section> ); } } ReactDOM.render(<Stage />, document.getElementById('root'));
index.css
@font-face {font-family: "iconfont"; src: url("./fonts/icons/iconfont.eot") format("embedded-opentype"), url("./fonts/icons/iconfont.woff") format("woff"), url("./fonts/icons/iconfont.ttf") format("truetype"), url("./fonts/icons/iconfont.svg") format("svg"); } html, body { margin: 0; padding: 0; font-family: sans-serif; background-color: #ddd; } #root { width: 100%; height: 100%; } /*Stage start*/ .stage { position: relative; width: 100%; height: 828px; } .imgStage { position: relative; width: 100%; height: 100%; overflow: auto; background-color: #ddd; perspective: 1800px; overflow: hidden; } /*Stage end*/ /*imgFigure start*/ .img-figure { position: absolute; height: 347px; width: 480px; margin: 0; padding: 40px; box-sizing: border-box; background-color: #FFF; cursor: pointer; transform-style: preserve-3d; transition: left .6s ease-in-out, top .6s ease-in-out, transform .6s ease-in-out; transform-origin: 0 50% 0; } .img-figure.is-inverse { transform: translate(480px) rotateY(180deg); } .img-back { position: absolute; left: 0; top: 0; width: 100%; height: 100%; padding: 40px; overflow: auto; color: #a7a0a2; font-size: 22px; line-height: 1.25; text-align: left; background-color: #FFF; box-sizing: border-box; backface-visibility: hidden; transform: rotateY(180deg); } .img-back p { margin: 0; } figcaption { text-align: center; } .img-title { margin-top: 10px; color: #a7a0a2; font-size: 16px; } /*imgFigure end*/ /*controllerUnit start*/ .controller { position: absolute; left: 0; bottom: 30px; z-index: 101; width: 100%; text-align: center; } .controllerUnit { display: inline-block; width: 30px; height: 30px; margin: 0 5px; text-align: center; cursor: pointer; background-color: #aaa; border-radius: 50%; transform: scale(.5); vertical-align: middle; transition: transform .6s ease-in-out, background-color .3s; } .controllerUnit.is-center { background-color: #888; transform: scale(1); } .controllerUnit.is-center::after { font-family: 'iconfont'; content: "\e515"; color: #fff; line-height: 30px; font-size: 80%; } .controllerUnit.is-center.is-inverse { background-color: #555; transform: rotateY(180deg); } /*controllerUnit end*/