如何用純css打造類materialUI的按鈕點擊動畫並封裝成react組件

前言

做爲一個前端框架的重度使用者,在技術選型上也會很是注意其生態和完整性.筆者前後開發過基於vue,react,angular等框架的項目,碧如vue生態的elementUI, ant-design-vue, iView等成熟的UI框架, react生態的ant-design, materialUI等,這些第三方UI框架極大的下降了咱們開發一個項目的成本和複雜度,使開發者更專一於實現業務邏輯和服務化.javascript

但隨着對用戶體驗的愈來愈重視,對交互體驗要求的提升以及css3等新標準的出現,使得web更加大放異彩, 各類動效的實現都變得很是容易.筆者在研究materialUI框架時對於它的交互及其讚歎.因此爲了本身能實現一個相似materialUI的按鈕點擊動畫,並封裝到本身的UI庫中,筆者特意總結了一些思路,但願能夠和廣大的前端工程師們一塊兒探討.css

正文

首先咱們看一下materialUI的按鈕點擊效果: 前端

本質上也是用了css3動畫的特性, 筆者查看源代碼和經過點擊發現materialUI會根據點擊位置不一樣而做不一樣位置的動畫,這個有點意思.咱們先不講這麼複雜的例子,下面經過css3的方案來實現一個相似的效果.筆者實現的效果以下:
上圖已是筆者基於react封裝好的一個按鈕Button組件,那麼咱們就先一步步實現它吧.

1. 原理

這個動效的原理其實也很簡單,就是利用css3的transition過渡動畫,配合::after僞對象就能夠實現,點擊的時候因爲元素會激活:active僞類, 而後咱們基於這個僞類, 在::after僞對象上作背景的動畫便可. 僞代碼以下:vue

.xButton {
  position: relative;
  overflow: hidden;
  display: inline-block;
  padding: 6px 1em;
  border-radius: 4px;
  color: #fff;
  background-color: #000;
  user-select:none;   // 禁止用戶選中
  cursor: pointer;
}

.ripple {
  &::after {
    content: "";
    display: block;
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    background-image: radial-gradient(circle, #fff 10%, transparent 11%);
    background-repeat: no-repeat;
    background-position: 50%;
    transform: scale(12, 12);
    opacity: 0;
    transition: transform .6s cubic-bezier(.75,.23,.43,.82), opacity .6s;
  }
  &:active::after {
    transform: scale(0, 0);
    opacity: .5;
  }
}
複製代碼

以上代碼就是經過設置transform的scale以及透明度, 而且設置一個漸變的徑向背景圖像來實現水波紋動畫的爲了實現更優雅的動畫,上面的css動畫的實現能夠藉助cubic-bezier這個在線工具,他能夠生成各類不一樣形式的貝塞爾曲線.工具長這樣: java

2. 組件設計思路

僅僅用上述代碼雖然能夠實現一個按鈕點擊的動畫效果,可是並不通用, 也不符合做爲一個經驗豐富的程序員的風格,因此接下來咱們要一步步把它封裝成一個通用的按鈕組件,讓它無所不用.node

組件的設計思路我這裏參考ant-design的模式, 基於開閉原則,咱們知道一個可擴展的按鈕組件通常都具有以下特色:react

  • 容許用戶修改按鈕樣式
  • 對外暴露按鈕事件方法
  • 提供按鈕主題和外形配置
  • 可插拔,可組合 基於以上幾點,咱們來設計這個react組件.

3. 基於react和css3的button組件具體實現

首先,咱們的組件是採用react實現, 技術點我會採用比較流行的umi腳手架, classnames庫以及css Module, 代碼很簡單, 咱們來看看吧.webpack

import classnames from 'classnames'
import styles from './index.less'

/** * @param {onClick} func 對外暴露的點擊事件 * @param {className} string 自定義類名 * @param {type} string 按鈕類型 primary | warning | info | default | pure * @param {shape} string 按鈕形狀 circle | radius(默認) * @param {block} boolean 按鈕展現 true | false(默認) */
export default function Button(props) {
  let { children, onClick, className, type, shape, block } = props
  return <div className={classnames(styles.xButton, styles.ripple, styles[type], styles[shape], block ? styles.block : '', className)} onClick={onClick} > { children } </div>
}
複製代碼

這是button的js部分,也是組件設計的核心, 按鈕組件對外暴露了onClick, className, type, shape, block這幾個props, className用於修改組件類名以便控制組件樣式, type主要是控制組件的風格, 相似於antd的primary等樣式, shape用來控制是不是圓形按鈕仍是圓角按鈕, block用來控制按鈕是不是塊.具體形式以下:css3

通過優化後的css長這樣:

.xButton {
  box-sizing: border-box;
  display: inline-block;
  padding: 6px 1em;
  border-radius: 4px;
  color: #fff;
  font-family: inherit;
  background-color: #000;
  user-select:none;   // 禁止用戶選中
  cursor: pointer;
  text-align: center;
  &.primary {
    background-color: #09f;
  }
  &.warning {
    background-color: #F90;
  }
  &.info {
    background-color: #C03;
  }
  &.pure {
    border: 1px solid #ccc;
    color: rgba(0, 0, 0, 0.65);
    background-color: #fff;
    &::after {
      background-image: radial-gradient(circle, #ccc 10%, transparent 11%);
    }
  }

  // 形狀
  &.circle {
    border-radius: 1.5em;
  }

  // 適應其父元素
  &.block {
    // width: 100%;
    display: block;
  }
}

.ripple {
  position: relative;
  overflow: hidden;
  &::after {
    content: "";
    display: block;
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    pointer-events: none;
    background-image: radial-gradient(circle, #fff 10%, transparent 11%);
    background-repeat: no-repeat;
    background-position: 50%;
    transform: scale(12, 12);
    opacity: 0;
    transition: transform .6s, opacity .6s;
  }
  &:active::after {
    transform: scale(0, 0);
    opacity: .3;
    //設置初始狀態
    transition: 0s;
  }
}
複製代碼

咱們實現按鈕樣式的切換徹底是用css module帶來的高靈活性, 使其讓屬性和類名高度關聯. 接下來看看咱們如何使用吧:程序員

// index.js
import { Button } from '@/components'
import styles from './index.css'
export default function() {
  return (
    <div className={styles.normal}> <Button className={styles.btn}>default</Button> <Button className={styles.btn} type="warning">warning</Button> <Button className={styles.btn} type="primary">primary</Button> <Button className={styles.btn} type="info">info</Button> <Button className={styles.btn} type="pure">pure</Button> <Button className={styles.btn} type="primary" shape="circle">circle</Button> <Button className={styles.mb16} type="primary" block>primary&block</Button> <Button type="warning" shape="circle" block onClick={() => { alert('block')}}>circle&block</Button> </div>
  )
}
複製代碼

以前咱們看到的按鈕樣式就是經過如上代碼生成的,是否是很簡單呢? 來咱們再次看看點擊的動效:

其實不只僅是react, 咱們使用一樣的原理也能夠實現一個vue版的按鈕組件或者一個angular版的組件,變得只是語法而已.這樣的組件設計思路和元素被官方用在不少ui庫中, 好比單一職責原理, 組件的開閉原則, 去中心,可組合等,但願對你們從此設計組件有所幫助.

最後

若是想了解本文完整的思惟導圖, 更多H5遊戲, webpacknodegulpcss3javascriptnodeJScanvas數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們一塊兒學習討論,共同探索前端的邊界。

更多推薦

相關文章
相關標籤/搜索