搜索框动画效果实现

呃,我们的搜索框,为了不让输入的字符串把放大镜遮住,我们给input 设置padding-right 为30px,如下。

export const NavSearch = styled.input`
  width: 160px;
  height: 38px;
  margin-top: 9px;
  margin-left: 20px;
  padding: 0 30px 0 20px;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-radius: 19px;
  background: #eee;
  font-size: 14px;
  color: #777;
  &::placeholder {
    color: #999;
  }
`;

接下来,我们制作鼠标的移入移出动画效果。

我们可以通过数据,控制页面的变化。在Header 组件中,我们在组件的state 中定义一个变量 focused ,如下。

  constructor(props) {
    super(props);
    this.state = {
      focused: false
    }
  }

然后,我们控制一下 NavSearch 与放大镜 的 className,如下。

    return (
      <HeaderWrapper>
        <Logo href='/'/>
        <Nav>
          <NavItem className='left active'>首页</NavItem>
          <NavItem className='left'>下载</NavItem>
          <NavItem className='right'>登录</NavItem>
          <NavItem className='right'>
            <span className="iconfont">&#xe636;</span>
          </NavItem>
          <SearchWrapper>
            <NavSearch
              placeholder="搜索"
              className={this.state.focused ? "focused" : ""}
            ></NavSearch>
            <span
              className={this.state.focused ? "focused iconfont" : "iconfont"}
            >&#xe623;</span>
          </SearchWrapper>
        </Nav>
        <Addition>
        <Button className='writting'>
          <span className="iconfont">&#xe63a;</span>
          写文章
        </Button>
          <Button className='reg'>注册</Button>
        </Addition>
      </HeaderWrapper>
    )

然后,我们去增加一些样式,如下。

export const SearchWrapper = styled.div`
  position: relative;
  float: left;
  background:red;
  .iconfont {
    position: absolute;
    right: 5px;
    bottom: 5px;
    width: 30px;
    line-height: 30px;
    border-radius: 15px;
    text-align: center;
    &.focused {
      background: #777;
      color: #fff;
    }
  }
`;

export const NavSearch = styled.input`
  width: 160px;
  height: 38px;
  margin-top: 9px;
  margin-left: 20px;
  padding: 0 30px 0 20px;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-radius: 19px;
  background: #eee;
  font-size: 14px;
  color: #777;
  &::placeholder {
    color: #999;
  }
  &.focused {
    width: 200px;
  }
`;

然后,我们增加一些逻辑处理,给输入框 的 focus 和 blur 事件定义逻辑,如下。

            <NavSearch
              placeholder="搜索"
              className={this.state.focused ? "focused" : ""}
              onFocus={this.handleFocus}
              onBlur={this.handleBlur}
            ></NavSearch>
  handleFocus() {
    this.setState({
      focused: true
    })
  }

  handleBlur() {
    this.setState({
      focused: false
    })
  }

下面,我们使用一下动画,使得样式变化可以平滑一些。动画,我们使用react 的第三方模块 react-transition-group 。

github 上我们可以找到它的文档: https://reactcommunity.org/react-transition-group/

然后我们使用这个模块的CSSTransition 模块就行。

下面,我们就使用吧。先下载安装它 yarn add react-transition-group

然后我们在Header 组件中引入

import { CSSTransition } from 'react-transition-group';

然后,我们用 CSSTransition标签 将 NavSearch标签包裹起来。并在 CSSTransition 中设置一些属性 timeout(动画在多少毫秒内完成),in(控制入场和出场),classNames。如下。

          <SearchWrapper>
            <CSSTransition
              in={this.state.focused}
              timeout={200}
              classNames="slide"
            >
              <NavSearch
                placeholder="搜索"
                className={this.state.focused ? "focused" : ""}
                onFocus={this.handleFocus}
                onBlur={this.handleBlur}
              ></NavSearch>
            </CSSTransition>
            <span
              className={this.state.focused ? "focused iconfont" : "iconfont"}
            >&#xe623;</span>
          </SearchWrapper>

好啦,这样的话。接下来,CSSTransition 会做下面的事情。当 in 中内容变为 true 时,它会在CSSTransition 包裹的组件上 挂载 slide-enter , slide-enter-active 样式,在in 中内容变为 false时,它会在CSSTransition 组件上 挂载 slide-exit , slide-exit-active 样式。

好啦,下面我们来写一下样式文件。

export const SearchWrapper = styled.div`
  position: relative;
  float: left;
  .slide-enter {
    transition: all .2s ease-out;
  }
  .slide-enter-active {
    width: 240px;
  }
  .slide-exit {
    transition: all .2s ease-out;
  }
  .slide-exit-active {
    width: 160px;
  }
  .iconfont {
    position: absolute;
    right: 5px;
    bottom: 5px;
    width: 30px;
    line-height: 30px;
    border-radius: 15px;
    text-align: center;
    &.focused {
      background: #777;
      color: #fff;
    }
  }
`;

好啦,这样就实现了搜索框的动画效果。

下面,贴一下整体代码。

Header组件代码

import React, { Component } from 'react';
import {
  HeaderWrapper,
  Logo,
  Nav,
  NavItem,
  NavSearch,
  Addition,
  Button,
  SearchWrapper
} from './style';
import '../../statics/iconfont/iconfont.css';
import { CSSTransition } from 'react-transition-group';

class Header extends Component {
  constructor(props) {
    super(props);
    this.state = {
      focused: false
    }
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
  }
  render () {
    return (
      <HeaderWrapper>
        <Logo href='/'/>
        <Nav>
          <NavItem className='left active'>首页</NavItem>
          <NavItem className='left'>下载</NavItem>
          <NavItem className='right'>登录</NavItem>
          <NavItem className='right'>
            <span className="iconfont">&#xe636;</span>
          </NavItem>
          <SearchWrapper>
            <CSSTransition
              in={this.state.focused}
              timeout={200}
              classNames="slide"
            >
              <NavSearch
                placeholder="搜索"
                className={this.state.focused ? "focused" : ""}
                onFocus={this.handleFocus}
                onBlur={this.handleBlur}
              ></NavSearch>
            </CSSTransition>
            <span
              className={this.state.focused ? "focused iconfont" : "iconfont"}
            >&#xe623;</span>
          </SearchWrapper>
        </Nav>
        <Addition>
        <Button className='writting'>
          <span className="iconfont">&#xe63a;</span>
          写文章
        </Button>
          <Button className='reg'>注册</Button>
        </Addition>
      </HeaderWrapper>
    )
  }

  handleFocus() {
    this.setState({
      focused: true
    })
  }

  handleBlur() {
    this.setState({
      focused: false
    })
  }

}

export default Header;

Header组件的样式代码

import styled from 'styled-components';
import logoPic from '../../statics/logo.png';

export const HeaderWrapper = styled.div`
  position: relative;
  height: 56px;
  border-bottom: 1px solid #f0f0f0;
`;

export const Logo = styled.a`
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 100px;
  height: 56px;
  background: url(${logoPic});
  background-size: contain;
`;

export const Nav = styled.div`
  width: 960px;
  height: 100%;
  padding-right: 70px;
  box-sizing: border-box;
  margin: 0 auto;
`;

export const NavItem = styled.div`
  line-height: 56px;
  padding: 0 15px;
  font-size: 17px;
  color: #333;
  &.left {
    float: left;
  }
  &.right {
    float: right;
    color: #969696;
  }
  &.active {
    color: #ea6f5a;
  }
`;

export const SearchWrapper = styled.div`
  position: relative;
  float: left;
  .slide-enter {
    transition: all .2s ease-out;
  }
  .slide-enter-active {
    width: 240px;
  }
  .slide-exit {
    transition: all .2s ease-out;
  }
  .slide-exit-active {
    width: 160px;
  }
  .iconfont {
    position: absolute;
    right: 5px;
    bottom: 5px;
    width: 30px;
    line-height: 30px;
    border-radius: 15px;
    text-align: center;
    &.focused {
      background: #777;
      color: #fff;
    }
  }
`;

export const NavSearch = styled.input`
  width: 160px;
  height: 38px;
  margin-top: 9px;
  margin-left: 20px;
  padding: 0 30px 0 20px;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-radius: 19px;
  background: #eee;
  font-size: 14px;
  color: #777;
  &::placeholder {
    color: #999;
  }
  &.focused {
    width: 200px;
  }
`;

export const Addition = styled.div`
  position: absolute;
  top: 0;
  right: 0;
  height: 56px;
`;

export const Button = styled.div`
  float: right;
  margin-top: 9px;
  margin-right: 20px;
  padding: 0 20px;
  line-height: 38px;
  border-radius: 19px;
  border: 1px solid #ec6149;
  font-size: 14px;
  &.reg {
    color: #ec6149;
  }
  &.writting {
    color: #fff;
    background: #ec6149;
  }
`;

Done.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值