页面设计 项目 GitHub 地址:group_website
方法+重难点 CSS脱离文档流后同级元素上移问题 参考链接:css固定定位脱离文档流怎么解决? 某元素脱离文档流后,同级元素位置上移的原因是因为没有给父级层设置相应的高度。当子级元素没有脱离文档流时,父DIV会因为子DIV的高度而被撑起来,然而当子DIV设置绝对定位或固定定位时,父DIV不会再根据子DIV的高度撑起来,也就是我们常说的脱离文档流。 解决方法1:强行给父级DIV设置相应的高度,这个方案的缺点是不够灵活,需要自己计算要设置的高度
1 2 3 4 5 6 7 8 9 10 11 .parent { height : 200px ; ... }.son { position :fixed; width : 200px ; height : 200px ; }
解决方法2:在合适的位置设置占位元素。没有在父元素设置元素高度直观,不建议采用。解决方法3:使用JS设置父级DIV的高度等于子DIV (推荐)
1 2 3 4 5 6 window .onload = ()=>( let son = document .getElementById('son' ); let parent = document .getElementById('parent' ); parent .style.height = window .getComputedStyle('son' ).height; )
window.getComputedStyle() : 以对象形式返回目标的所有CSS样式值window.onload() : MDN文档中说明,onload 事件触发发生在所有 DOM 资源加载完成后,此时所有元素在DOM中均已完成挂载,不用担心 onload 事件触发后调用函数无作用对象的问题。在 React 中用 ref 属性来获取 dom 元素进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 //在组件完成挂载时,添加 window.onload 监听事件,它在所有 DOM 资源加载完成后调用函数执行。 componentDidMount() { window.onload = ()=>{ this.parent.style.height = window.getComputedStyle(this.son).height } } ... <div style= {{ position: "relative" }} //获取DOM元素 ref={(parent) => { this.parent = parent }} > <img style= {{ position: "absolute" , zIndex: -1, width: "100vw" , height: "1080px" }} src={headerPng} alt="" //获取DOM元素 ref={(son) => { this.son = son }} /> <Titles /> <DropdownMenu isActive ={this.state.isActive} /> </div > <div style = {{ position: "relative" }} > //去除了占位元素: - <div className ="titlePlaceHolder" > </div > <PageOne /> </div >
渲染结果:
this 指向问题 参考链接:this 指向问题 this 指向详细解析 这里只对实战中遇到的 this 做简要分析,具体的需要详细学习。DOM事件处理函数 (1)当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件) (2)代码被内联处理函数调用时,它的this指向监听器所在的DOM元素,当代码被包括在函数内部执行时,其this指向等同于函数直接调用的情况
setTimeout & setInterval 对于延时函数内部的回调函数的this指向全局对象window
箭头函数中的 this (1)由于箭头函数不绑定this, 它会捕获其所在上下文的this值, 作为自己的this值。因此常被用于调用函数,改变函数的this指向,不容易出错。 (2)方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象,箭头函数没有this
举例:
1 setTimeout (() => { dispatch({ type: "close_isActive_delay" }) }, 300 )
上述例子中,不能直接使用dispatch函数,因为在setTimeout中this指向全局对象window,使用箭头函数后自动关联上下文,this最终指向监听事件的DOM元素。(正确性有待商榷,对this不是很了解)
项目结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ├─ group_website │ └─ src │ ├─ App . css │ ├─ components │ │ ├─ DropdownMenu . js │ │ ├─ DropdownSubTitle . js │ │ ├─ DropdownTitle . js │ │ ├─ page1 │ │ │ ├─ PageOne . js │ │ │ └─ PageOneText . js │ │ ├─ SubTitle . js │ │ └─ Titles . js │ ├─ images │ │ ├─ header.png │ │ ├─ pgoneBackground.png │ │ └─ testImg.jpg │ ├─ index.js │ ├─ store │ │ ├─ index.js │ │ └─ reducer.js │ ├─ style │ │ ├─ DropdownMenu . css │ │ ├─ DropdownTitle . css │ │ ├─ page1 │ │ │ └─ PageOne . css │ │ ├─ SubTitle . css │ │ └─ Titles . css │ └─ Website . js └─ package-lock.json
项目入口 js 文件为 Website.js
,CSS样式存储于 /store
文件夹下,页面组件存储于/components
文件夹中。
下拉菜单 Website.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import React, { Component } from 'react' ;import DropdownMenu from './components/DropdownMenu' import Titles from './components/Titles' import "./App.css" import store from './store/index' import headerPng from './images/header.png' import PageOne from './components/page1/PageOne' class Website extends Component { constructor (props ) { super (props); this .state = store.getState() this .storeChange = this .storeChange.bind(this ) store.subscribe(this .storeChange) } storeChange ( ) { this .setState(store.getState()) } render ( ) { return ( <div> <div> <div style={{ position : "relative" }}> <img style={{position :"absolute" ,zIndex :-1 ,width :"100vw" ,height :"1080px" }} src={headerPng} alt="" /> <Titles /> <DropdownMenu isActive={this .state.isActive} /> </div> <div style={{position :"relative" }}> <div className="titlePlaceHolder" ></div> <PageOne /> </div> </div> </div> ); } }export default Website;
重难点分析 页面布局
如何让组件脱离文档流?方法一:设置position:("fixed"/"absolute")
,方法二:设置display:float
,不过目前常用的是 flex 布局,一般用不到 float 浮动布局。
脱离文档流的好处和坏处?好处:脱离文档流可以释放元素占据的空间,让同级以及子级元素浮动到顶部,在设置图片背景的时候常用,如实战代码中的<img style={{position:"absolute",zIndex:-1,width:"100vw",height:"1080px"}} src={headerPng} alt="" />
。坏处:脱离文档流后后续的布局会被打乱,很难调整。
如何克服脱离文档流后后续元素上移问题:用<div>
创建一个具有原同级元素大小的元素充当占位元素,或者给父级元素设置相应的高度。实战中代码使用如下:1 2 3 4 5 6 7 8 9 10 11 //设置position:"absolute"后脱离文档流 <div style = {{ position: "relative" }} > <img style = {{position:"absolute" ,zIndex:-1,width:"100vw",height:"1080px"}} src ={headerPng} alt ="" /> <Titles /> <DropdownMenu isActive ={this.state.isActive} /> </div > <div style = {{position:"relative" }} > //设置一个div元素,大小与上述div最大尺寸相同,占据原有位置 <div className ="titlePlaceHolder" > </div > <PageOne /> </div >
脱离文档流的元素位置设置:position:("fixed"/"absolute")
属性都是相对于父级的position:"relative"
进行定位的。若要进行位置定位,在父级元素单独设置position:"relative"
即可。
关于 z-index 属性设置:在设置背景的时候,往往会遇到背景遮盖了优先需要显示的部分,这就需要调整 z-index 的属性,z-index 越大,显示的优先级越高。1 2 3 4 5 z-index 设置: 设置z-index 的(每一个)元素必须设置position :(relative/fixed/absolute),若不对位置有调整或特殊要求,可以单设一个relative。 将要在z轴排序的元素用<div>包裹,同时在父级元素上也要设position 。 根据需要对不同的子元素设置z-index ,排序。 ***关键:所有参与的元素(包括父元素)都要设置position
Title.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 import React, { Component } from 'react' ;import "../style/Titles.css" import SubTitle from './SubTitle' ;class Titles extends Component { constructor (props ) { super (props); this .state = {} } render ( ) { const titleInfo = [ { titleText: "Title1" , id:1 , subTitleProps: [ { name : "百度" , href : "https://www.baidu.com/" , }, { name : "搜狗" , href : "https://www.sogou.com/" , }, { name : "360搜索" , href : "https://www.so.com/" , }, { name : "火狐" , href : "https://start.firefoxchina.cn/" , }, ], }, { titleText: "Title2" , id:2 , subTitleProps: [ { name : "百度" , href : "https://www.baidu.com/" , }, { name : "搜狗" , href : "https://www.sogou.com/" , }, { name : "360搜索" , href : "https://www.so.com/" , }, { name : "火狐" , href : "https://start.firefoxchina.cn/" , }, ], }, { titleText: "Title3" , id:3 , subTitleProps: [ { name : "百度" , href : "https://www.baidu.com/" , }, { name : "搜狗" , href : "https://www.sogou.com/" , }, { name : "360搜索" , href : "https://www.so.com/" , }, { name : "火狐" , href : "https://start.firefoxchina.cn/" , }, ], }, { titleText: "Title4" , id:4 , subTitleProps: [ { name : "百度" , href : "https://www.baidu.com/" , }, { name : "搜狗" , href : "https://www.sogou.com/" , }, { name : "360搜索" , href : "https://www.so.com/" , }, { name : "火狐" , href : "https://start.firefoxchina.cn/" , }, ], }, ] return ( <div> <div className="titleContainer" > <div className="leftPlaceHolder" ></div> <div className="rightPlaceHolder" > { titleInfo.map((item, index ) => { return ( <SubTitle key={index} titleText={item.titleText} params={item.subTitleProps} index={item.id} />) }) } </div> </div> </div> ); } }export default Titles;
重难点分析 组件使用思想 React 尽量将具有相同功能的组件单独编写,一些内容变化可以通过传参的方式,事先声明存储内容的参数数组进行遍历,如上述代码所示。若功能完全一致的组件可以声明为高阶组件进行复用。
1 2 3 4 5 6 7 8 9 titleInfo.map((item, index ) => { return ( <SubTitle key={index} titleText={item.titleText} params={item.subTitleProps} index={item.id} />) })
注意 {}
是需要用return()
返回内容的,这里也可以用()=>()
的ES6写法,默认省略return
,自动返回括号内的内容。
通过传递props
的方式修改通用组件<SubTitle />
中的部分内容,实现组件的高度复用。
页面布局的小技巧:1 2 <div className ="leftPlaceHolder" ></div> <div className ="rightPlaceHolder" >
布局前实现用空<div>
进行空间分配,再在相应空间内进行布局。
SubTitle.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import React, { Component } from 'react' ;import DropdownTitle from './DropdownTitle' import "../style/SubTitle.css" import store from '../store/index' ;class SubTitle extends Component { constructor (props) { super (props); this .state = store.getState() this .storeChange = this .storeChange.bind(this ) store.subscribe(this .storeChange) this .handleMouseOver = this .handleMouseOver.bind(this ) this .handleMouseOut = this .handleMouseOut.bind(this ) } handleMouseOver() { const action = { type: "open_isActive_delay" , id: this .props.index, } store.dispatch(action) } handleMouseOut() { const delayAction = () => (dispatch, getState) => { let timer = setTimeout(() => { dispatch({ type: "close_isActive_delay" }) }, 300 ) console.log(this .state.isActive) dispatch({ type: "add_timer" , timer }) } store.dispatch(delayAction()) } storeChange() { this .setState(store.getState()) } render() { return ( <div className="subTitleContainer" > <div> <a className="textFont" onMouseOver={(e) => { this .handleMouseOver() }} onMouseOut={(e) => { this .handleMouseOut() }} style={ this .state.isActive ? ((this .state.id === this .props.index) ?{ color: "#44DAFF" } : { color: "#666666" }) : { color: "white" } } >{this .props.titleText}</a> </div> <DropdownTitle params={this .props.params} index={this .props.index} /> </div> ); } } export default SubTitle;
重难点分析 Redux-thunk 在导航栏设计时需要考虑用户拖动鼠标的时间,假设不设置 setTimeout 延时,会导致用户鼠标离开标题后,直接触发 onMouseOut 事件,将导航栏关闭,无法点击导航栏的子选项。因此我们要给用户一定的时间从主标题移动到子标题,当用户移动到子标题时,触发子标题上的 onMouseOver 事件,清除 setTimeout 延时并维持 isActive 状态为 true。 由于涉及到多级的状态管理,因此使用了 Redux ,但是在 Redux 中const action = {type:"...",...}
只能传递对象,无法加入 setTimeout()
异步函数,因此我们又需要引入中间件Redux-thunk。thunk 中间件主要处理一些状态的异步操作问题:例如 setTimeout 延时, axios 请求等。thunk 的基础和原理在这里不过多的介绍,可以参考: redux-thunk最简单的讲解,7行代码用就完事了 精炼:执行同步action:
1 2 3 4 5 6 7 8 9 function decrement ( ) { return { type : 'INCREMENT' , payload : 1 } } const mapDispatchToProps = (dispatch ) => { return { increment: () => dispatch({ type : 'INCREMENT' , payload : 1 }), decrement: () => dispatch( decrement() ) } }
执行异步action:
1 2 3 4 5 const delayAction = () => (dispatch , getState ) => { let timer = setTimeout(() => { dispatch({ type : "close_isActive_delay" }) }, 300 ) dispatch({ type : "add_timer" , timer }) } store.dispatch(delayAction() )
注意事项 dispatch内必须是一个扁平化的object,或者是能直接返回一个{type:’REDUCERS’,payload:data}的函数
在什么场景下需要用到redux的middleware,thunk? 精炼: thunk 函数接收两个参数,其都是 Redux 的方法名
1 (dispatch, getState) => {}
thunk 可以通过 getState 方法获取 store 仓库中的所有状态,并可以在执行完自定义函数操作后将结果 dispatch 到 store 仓库中。
理解redux-thunk 技术胖-Redux-thunk thunk 的使用方法:
安装 redux-thunk
配置 redux-thunk1 2 3 4 5 6 import { createStore , applyMiddleware } from 'redux' import thunk from 'redux-thunk' const store = createStore( reducer, applyMiddleware(thunk))
使用 thunk1 2 3 4 5 const func1 = () => (dispatch, getState) => { let timer = setTimeout (() => { dispatch({ type: "close_isActive_delay" }) }, 300 ) dispatch({ type: "add_timer" , timer }) } store.dispatch(func1())
这里创建一个func1的函数,其返回一个 thunk 函数 ,thunk 函数接收两个参数,并在内部写业务逻辑,得到的 action 对象通过 dispatch 注入,并最后将 thunk 函数通过 dispatch 注入到 store 中。这里有一个点需要注意:即为什么要在 thunk 函数外嵌套一层函数,而不是直接将 thunk 函数注入到 store 中?这是因为外层嵌套函数后可以传入一些自定义的参数,由于 thunk 函数接收的两个参数是固定的,因此就有必要嵌套一层函数使得 thunk 函数可以使用用户自定义的一些业务逻辑参数,例如const func1 = (myname) => (dispatch, getState) => {...}
注意store.dispatch(func1())
中注入的是函数的调用而非函数名。 当然上述函数定义也可以写为:
1 2 3 4 5 6 7 8 9 const func1 = (xxx) => { return ( (dispatch, getState) => { let timer = setTimeout (() => { dispatch({ type: "close_isActive_delay" }) }, 300 ) dispatch({ type: "add_timer" , timer }) } ) } store.dispatch(func1())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import React, { Component } from 'react' ;import { createPortal } from 'react-dom' import "../style/DropdownMenu.css" import { CSSTransition } from 'react-transition-group' import store from '../store/index' class DropdownMenu extends Component { constructor (props ) { super (props); this .state = {} const dom = window .document this .node = dom.createElement("div" ) dom.body.appendChild(this .node) } componentWillUnmount ( ) { window .document.body.removeChild(this .node) } render ( ) { return createPortal(( <div className="menuContainer" > <CSSTransition in ={this .props.isActive} timeout={200 } classNames={{ enter: "menuEnter" , enterActive: "menuEnterActive" , enterDone: "menuEnterDone" , exit: "menuExit" , exitActive: "menuExitActive" , exitDone: "menuExitDone" , }} > <div className="dropDownMenu" ></div> </CSSTransition> </div> ), this .node); } }export default DropdownMenu;
重难点分析 主要实践了 React-Portal,其实这里可以用脱离文档流加定位的方式实现,只是单纯的为练习而使用,React-Portal 具体使用可以参考先前的文章。
页面滚动动画 PageOneText.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import React, { Component } from 'react' import "../../style/page1/PageOne.css" import { Typography } from 'antd' ;import store from '../../store' ; import "../../style/page1/PageOne.css" import testImg from '../../images/testImg.jpg' const { Title } = Typography;const { Paragraph } = Typography;class PageOneText extends Component { constructor (props ) { super (props); this .state = store.getState() this .storeChange = this .storeChange.bind(this ) store.subscribe(this .storeChange) } handleScroll ( ) { if (document .documentElement.scrollTop > 900 ) { const action = { type: "handle_scroll" , } store.dispatch(action) } if (document .documentElement.scrollTop < 550 ) { const action = { type: "handle_noscroll" , } store.dispatch(action) } } storeChange ( ) { this .setState(store.getState()) } componentDidMount ( ) { window .onscroll = () => { this .handleScroll() } } render ( ) { return ( <div className="textContainer" > <img className={`${this .state.pgoneClassNames.img} ` } src={testImg} alt="" /> <Title className={`title ${this .state.pgoneClassNames.title} ` }>{this .props.title}</Title> <Paragraph ellipsis={{ rows : 5 , expandable : true , symbol : 'more' }} className={`texts paragraph ${this .state.pgoneClassNames.text} ` } > {this .props.text} </Paragraph> </div> ); } }export default PageOneText;
重难点分析 页面滚动事件
处理页面滚动的函数1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 handleScroll () { if (document.documentElement .scrollTop > 900 ) { const action = { type: "handle_scroll" , } store.dispatch(action) } if (document.documentElement .scrollTop < 550 ) { const action = { type: "handle_noscroll" , } store.dispatch(action) } }
document.documentElement 返回document文档的根节点元素,例如HTML文档则返回<html>
元素节点。 通过document.documentElement.scrollTop
可以获得HTML文档滚动条距离其顶部的高度,由于显示分辨率的不同,scrollTop也不同,具体调试滚动阈值的时候可以通过console.log(document.documentElement.scrollTop)
来实时打印确定。
题外话:document.documentElement和document.body区别 body是DOM对象里的body子节点,即body标签 documentElement 是整个节点树的根节点root
挂载监听事件1 2 3 4 componentDidMount ( ) { window .onscroll = () => { this .handleScroll() } }
上述handleScroll()
只定义了监听事件的处理函数,我们还需要在合适的位置将监听函数挂载。通常用window.onScroll 为当前页面的页面滚动事件添加事件处理函数。 我们将页面滚动监听放在componentDidMount
中,在页面组件渲染完成后挂载监听事件,当用户滚动页面时会不断触发监听事件所添加的事件处理函数handleScroll
,当滚动距离超过阈值时,执行动画操作。
PageOne.js 用于包裹<PageOneText />
组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import React, { Component } from 'react' ;import "../../style/page1/PageOne.css" import PageOneText from './PageOneText' import testImg from '../../images/testImg.jpg' import pgoneBackground from '../../images/pgoneBackground.png' class PageOne extends Component { constructor (props ) { super (props); this .state = {} } render ( ) { const itemList = [ { title: "h1. Title1" , text: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ XXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxx\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" , imgSrc: testImg, }, { title: "h2. Title2" , text: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ XXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxx\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" , imgSrc: testImg, }, { title: "h3. Title3" , text: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\ XXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxx\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" , imgSrc: testImg, }, ] return ( <div className="pgOnePlaceholder" > <img src={pgoneBackground} style={{ opacity: 1 , width: "100%" , height: "100%" , position: "absolute" , }} alt="" /> { itemList.map((item, index ) => ( <PageOneText key={index} imgSrc={item.imgSrc} title={item.title} text={item.text} /> )) } </div> ); } }export default PageOne;
CSS 动画 关于 CSS 动画,这里单独拿出一块来讲,因为这块踩的坑比较多。 我在使用 CSS 动画时,前期采用了 React 官方的一个动画库 react-transition-group
,但是由于教程比较少,一些问题得不到解决,所以后面改用了 CSS 的 keyframe 帧动画。
CSSTransition react-transition-group CSSTransition 使用方法:
安装 react-transition-group1 npm install --save react-transition-group
配置 react-transition-group1 import { CSSTransition } from 'react-transition-group '
使用 CSSTransition 用<CSSTransition></ CSSTransition>
标签包裹需要动画的组件(只能是一个整体,即多个子组件需要用<div>
包裹)1 2 3 4 5 6 7 8 9 <CSSTransition in ={} classNames ={} timeout ={200} onEnter ={(el) => (...)} ... > <subComponent /> </ CSSTransition >
<CSSTransition>
标签参数参考官方文档,必要的参数为in, classNames, timeout
,in 参数接收 true/false,表示动画执行的条件;classNames 参数设定动画的CSS样式;timeout 设定动画执行时间。 classNames 要区别于 className,其次,classNames 有两种声明方式:1 2 3 4 5 6 7 8 9 classNames = {{ enter: "xx" , enterActive: "xxx" , enterDone: "xxxx" , }} ########## .xx {} .xxx {} .xxxx {}
或者1 2 3 4 5 classNames = "xx" ########## .xx-enter {} .xx-enter-active {} .xx-enter-done {}
使用CSSTransition的一些坑 无法设置目标的初始状态,例如一些淡入淡出的效果,目标未进行动画的时候理论上应该隐藏在用户视野之外。但在使用CSSTransition动画时,若设置目标 opacity:0
,则无法播放动画效果。可能原因: 目标还未挂载渲染时候,<CSSTransition>
已经挂载,后续的CSS样式遮盖了动画效果。目前没有什么比较好的解决方法。此外,目标组件内容若是接收传递的props,也会出现上述问题。因此,在PageOne 页面制作的时候,采用了 keyframe 帧动画。
CSS Keyframes 参考链接:CSS动画
定义关键帧 @keyframes 关键帧名称 {}
其中 0%, 100%
代表关键帧位置;opacity
表示透明度,transform
表示相对于目标初始位置的位移。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @keyframes leftToRight { 0%{ opacity : 0 ; transform :translateX (-100% ); } 100%{ opacity : 1 ; transform :translateX (0 ); } }@keyframes topToBottom { 0%{ opacity : 0 ; transform :translateY (-100% ); } 100%{ opacity : 1 ; transform :translateY (0 ); } }
定义animation动画 CSS3 animation animation: name duration timing-function delay iteration-count direction fill-mode;
包含以下几个参数:
name: keyframes 的动画样式名称
duration: 动画时长
timing-function: ease-in 快进慢出;ease-out 慢进快出;ease-in-out;…
delay:动画延迟,等待n秒后开始动画 animation-delay
iteration-count:循环播放次数
direction:方向
fill-mode:规定动画在播放之前或之后,其动画效果是否可见。 none | forwards | backwards | both ; CSS3 animation-fill-mode
其中 animation-fill-mode 是解决动画目标初始化的关键,当 animation-fill-mode 设定为 both 时,以上述代码为例,动画播放前,目标元素动画效果可见,其为opacity:0; transform:translateX(-100%)
,即隐藏,而动画播放结束后,目标元素动画效果将会停留在关键帧100%
的地方,即显示。
1 2 3 4 5 6 7 8 9 10 11 .item_row { animation : leftToRight 1000ms both ease-out; }.item_delay_row { animation : leftToRight 1000ms ease-out 100ms both; }.item_column { animation : topToBottom 1500ms both ease-out; }
定义触发动画的监听事件 监听事件由上述的页面滚动触发。首先我们需要设定一个充当占位的样式(可为空),此处visibility 设为hidden表示该元素不可见但仍保留其布局空间。当监听事件触发时,我们向className中添加相应动画的CSS样式,替换原有的占位样式即可。
1 2 3 .hidden { visibility : hidden; }
1 2 3 4 5 6 7 8 <img className ={ `${this.state.pgoneClassNames.img }`} src ={testImg} alt ="" /> <Title className ={ `title ${this.state.pgoneClassNames.title }`}> {this.props.title}</Title > <Paragraph ellipsis= {{ rows: 5 , expandable: true , symbol: 'more' }} className={`texts paragraph ${this.state.pgoneClassNames.text}`} > {this.props.text} </Paragraph >
采用 CSS Keyframes 的另一个好处是能够将动画 CSS 样式和初始 CSS 样式分开,例如下述代码title ${xxx}
,用 ES6 模板字符串的方式,title 是固定的初始样式,${xxx}
内可以传入动态的动画样式。
CSS 样式布局心得 调整CSS样式心得:
从网页打开F12,选择元素查看其className进行定位。
width,height 宽高100%(或百分比设置)针对的是上一级元素(父级元素), 因此若多余空白的包裹应将其删除,或者将上级空白div设置宽高100%。