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

2020-12-26 酷扯兒

本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫

前言

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

但隨著對用戶體驗的越來越重視,對交互體驗要求的提高以及css3等新標準的出現,使得web更加大放異彩, 各種動效的實現都變得非常容易.筆者在研究materialUI框架時對於它的交互及其讚嘆.所以為了自己能實現一個類似materialUI的按鈕點擊動畫,並封裝到自己的UI庫中,筆者特地總結了一些思路,希望可以和廣大的前端工程師們一起探討.

正文

首先我們看一下materialUI的按鈕點擊效果:

本質上也是用了css3動畫的特性, 筆者查看原始碼和通過點擊發現materialUI會根據點擊位置不同而作不同位置的動畫,這個有點意思.我們先不講這麼複雜的例子,下面通過css3的方案來實現一個類似的效果.筆者實現的效果如下:

上圖已經是筆者基於react封裝好的一個按鈕Button組件,那麼我們就先一步步實現它吧.

1. 原理

這個動效的原理其實也很簡單,就是利用css3的transition過渡動畫,配合::after偽對象就可以實現,點擊的時候由於元素會激活:active偽類, 然後我們基於這個偽類, 在::after偽對象上做背景的動畫即可. 偽代碼如下:

.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這個在線工具,他可以生成各種不同形式的貝塞爾曲線.工具長這樣:

2. 組件設計思路

僅僅用上述代碼雖然可以實現一個按鈕點擊的動畫效果,但是並不通用, 也不符合作為一個經驗豐富的程式設計師的風格,所以接下來我們要一步步把它封裝成一個通用的按鈕組件,讓它無所不用.

組件的設計思路我這裡參考ant-design的模式, 基於開閉原則,我們知道一個可擴展的按鈕組件一般都具備如下特點:

允許用戶修改按鈕樣式對外暴露按鈕事件方法提供按鈕主題和外形配置可插拔,可組合 基於以上幾點,我們來設計這個react組件.3. 基於react和css3的button組件具體實現

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

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用來控制按鈕是否是塊.具體形式如下:

經過優化後的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庫中, 比如單一職責原理, 組件的開閉原則, 去中心,可組合等,希望對大家今後設計組件有所幫助.

相關焦點

  • 《前端5分鐘》之使用純css實現網站換膚和焦點圖切換動畫
    你將收穫網站換膚設計方案介紹:target偽類介紹和用法以及如何使用css實現網站換膚transition動畫以及如何用純css實現焦點圖動畫效果展示1.網站換膚2.焦點圖動畫>實現思路1.網站換膚通常我們實現網站換膚都基於如下方式實現:方案一: 使用OOCSS模式,通過js動態切換公共類名來達到換膚效果方案二: 點擊不同的按鈕切換不同的樣式表,如下:theme-green.csstheme-red.csstheme-black.css方案三
  • 精通react/vue組件設計之實現一個輕量級可擴展的模態框組件
    對於react選手來說,如果沒用typescript,建議大家都用PropTypes, 它是react內置的類型檢測工具,我們可以直接在項目中導入. vue有自帶的屬性檢測方式,這裡就不一一介紹了.基於react實現一個Modal組件2.1.
  • 精通react/vue組件設計教你實現一個極具創意的加載(Loading)組件
    組件設計思路按照之前筆者總結的組件設計原則,我們第一步是要確認需求. 首先我們設計的不是後臺管理系統專用的加載動畫,而是作為一個C端產品的功用型加載動畫.我們都知道加載動畫的作用是:在用戶等待網頁時能看到有用的信息,比如網站介紹,引導, 公司信息等,緩解用戶焦慮.
  • 精通react/vue組件設計之實現一個Tag(標籤)和Empty(空狀態)組件
    正文在開始組件設計之前希望大家對css3和js有一定的基礎.我們先看看實現後的組件效果:由圖可以知道tag組件可以自定義顏色主題(color theme), 可以手動關閉標籤, 空狀態主要是提供用戶數據展示用的, 實現起來很簡單,重點在圖標的使用上.
  • 《精通react/vue組件設計》之快速實現一個可定製的進度條組件
    每一個組件只負責某一特定的表現或者功能)正文在開始組件設計之前希望大家對css3和js有一定的基礎.我們先看看實現後的組件效果:上圖可以知道封裝後的進度條組件通過對外暴露的接口(react/vue裡面可以看做
  • 精通React/Vue系列之帶你實現一個功能強大的通知提醒框
    :那麼我們如何實現這樣的調用方式呢?@param {icon} ReactNode 自定義圖標* @param {key} string 當前通知唯一標誌* @param {message} string|ReactNode 通知提醒標題,必選* @param {onClose} func 點擊默認關閉按鈕時觸發的回調函數
  • 《精通react/vue組件設計》之實現一個健壯的警告提示(Alert)組件
    正文在開始組件設計之前希望大家對css3和js有一定的基礎,並了解基本的react/vue語法.我們先看看實現後的組件效果:1. 組件設計思路按照之前筆者總結的組件設計原則,我們第一步是要確認需求.需求收集好之後,作為一個有追求的程式設計師, 會得出如下線框圖:對於react選手來說,如果沒用typescript,建議大家都用PropTypes, 它是react內置的類型檢測工具,我們可以直接在項目中導入. vue有自帶的屬性檢測方式,這裡就不一一介紹了.
  • 一個在線css三角形生成器
    在線css三角形生成器預覽由預覽動畫我們可以看到通過在線工具我們可以輕鬆配置各種想要的三角形, 並且能實時查看css代碼. 開發完這個工具之後筆者再也不用擔心還需要手寫三角形代碼了.(上班摸魚也成了可能, 確實很多時候就是不想寫代碼還想要有錢拿) 在文末筆者會附上css工具的在線地址, 接下來我們來看看具體實現流程.
  • React知識點總結
    函數組件:1.純函數,輸入props,輸出jsx2.沒有實例,沒有生命周期vs 非受控組件:1.優先使用受控組件,符合react設計原則2.必須操作dom,from 'react'import ReactDOM from 'react-dom'import '.
  • 兩種CSS 方法論你知道嗎?
    塊(Block):一個塊是視覺上或者語義上的一個整體,它是一個具體且唯一的一個元素,例如,頁面上的一個彈窗,或者是一個搜索框; 元素(Element):一般認為是塊的組成部分,元素比較用它父級的塊名稱做為前綴,例如,彈窗的標題、關閉按鈕、確認按鈕; 修飾符(Modifier):修飾符表示一個具體元素的特定狀態,例如,關閉按鈕在滑鼠沒放上去和放上去的時候,呈現的兩種狀態。
  • React實戰筆記:React項目詳情頁
    定義路由:第三節 輪播圖組件使用1.請求詳情數據:2.引入公共的輪播圖組件,給組件傳入數據即可第四節 封裝Tab標籤頁>1.介紹:按照antd的tabs標籤頁封裝自己的tabs標籤頁。自己封裝使用效果封裝:MyTabs組件第五節 房屋評價滾動加載更多引入加載更多組件即可第六節 收藏功能登錄驗證
  • react中關於hook介紹及其使用
    前言最近由於公司的項目開發,就學習了在react關於hook的使用,對其有個基本的認識以及如何在項目中去應用hook。能夠解決你在不使用class組件的情況下去體現react的特性需要注意的一點就是hook和class組件是不能夠同時使用的,在實際的使用過程中一定要注意,否則就會出現報錯那麼接下來所要介紹的部分就是如何去使用hookstate hook
  • 微信小程序抽獎轉盤組件怎麼做?
    微信小程序轉盤抽獎組件的實現思路1.界面樣式實現從抽獎轉盤的圖中我們可以看出,抽獎轉盤由外圓、扇面抽獎選項、抽獎按鈕組成,其中外圓不難實現,大家在處理微信小程序頭像的時候估計都已經用過了,那就是利用border-radius:50%來將一個正方形變成圓,這是外圓實現的關鍵
  • 小程序實踐:基礎內容之progress,如何自定義一個環形進度條?
    在啟用progress的active動畫後,每走一段都是一段動畫,每段動畫都是基於css的動畫繪製,都有時間。屬性duration用於標識行走1%需要花費的時間,默認值為30毫秒。這個值越大,動畫越細膩。用於設計的標準屏幕寬度為375px,人類眼睛的動畫覺察閥值是200毫秒,以默認值30毫秒走掉1%計算,200毫秒會走掉大約25px。
  • 純 CSS 實現波浪效果!
    /如有好文章投稿,請點擊 → 這裡了解詳情額,這不是廢話嗎好的,那整這麼個圖形又有什麼用?還能變出波浪來不成?沒錯!就是這麼神奇。:) 我們讓上面這個圖形滾動起來(rotate) ,看看效果:而我們的目的,就是要藉助這個動態變換的起伏動畫,模擬製造出類似波浪的效果。實現當然,這裡看到是全景實現圖,所以感覺並不明顯,OK,讓我們用一個個例子看看具體實現起來能達到什麼樣的效果。我們利用上面原理可以做到的一種波浪運動背景效果圖:
  • 如何設計 React 代碼結構?
    這類的討論就好像兩個人爭論哪種顏色最好看,是藍色還是紅色。他們都會主張自己的意見,闡明自己的立場,然後固執己見。他們永遠也不會折衷,比如認為紫色最好看。因此,我並不期望能夠在這個問題上達成任何共識。在本文中,我只會介紹在設計React代碼結構方面的個人喜好,以及其中的原因。
  • 厲害了-純 CSS 實現波浪效果!
    純 CSS 實現波浪效果好,接下來才是本文的重點!使用純 CSS 的方式,實現波浪的效果。你 TM 在逗我?剛剛不是還說使用 CSS 無能為力嗎?好的,那整這麼個圖形又有什麼用?還能變出波浪來不成?沒錯!就是這麼神奇。
  • 如何使用React Hooks實現容器組件?
    React Hooks是React功能組件中能夠管理狀態和組件生命周期的組件。它能夠有效地使組件都具有功能性,甚至可以說大大減少了對基於類的組件的需求。容器組件中的容器到底是什麼?「對數據獲取,然後將數據呈現在相應的子組件中。這就是容器。」
  • 前端進階:css必知的幾個底層知識和技巧
    在介紹完問題學習法之後,進入我們今天的主題,接下來我會介紹css的一些底層的知識的現象,藉此來讓大家對css有更深入的理解。3.如何讓元素支持height:100%效果知識點:絕對定位的寬高百分比是基於padding-box的,而非絕對定位的寬高百分比是基於content-box方法如下:* 1.設置顯示的高度值
  • 騰訊推出移動端動畫組件PAG,釋放設計生產力!
    設計師使用AE導出插件,能夠直接將製作好的動畫導出成PAG文件,經過桌面預覽工具的確認,再交付給終端由SDK渲染成動效內容。基於以上的目標,PAG採用了二進位的數據結構來存儲動畫信息。因為二進位數據結構能夠非常方便的單文件集成任何資源,並在解碼速度上,比JSON這類文本數據可以快幾十倍。