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

2021-01-21 酷扯兒

本文轉載自【微信公眾號:趣談前端,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庫中, 比如單一職責原理, 組件的開閉原則, 去中心,可組合等,希望對大家今後設計組件有所幫助.

相關焦點

  • 開源組件分享-Avue基於elementUI CRUD最強封裝
    用VUE的同學都知道,VUE生態裡面UI框架用得多的有Element UI、iView、ant-design-vue等。這些框架用過的應該都很熟悉了,今天我我要介紹的是另一款UI組件Avue.js,之所以介紹Avue.js因為我前段時間使用過,裡面有些功能確實很方便,比如使用Avue的表格組件,設置幾個對應的屬性後,頁面的搜索條件、導出、篩選、增刪改查功能都不需要自己去處理了。
  • Meta CSS框架發布
    對此,通常有4種解決方法:  1.每種不同款式,定義一個css。按鈕A一個css,按鈕B雖然長得跟A一樣,但是在右邊,那就copy下A的css代碼,然後改成在右邊。不過在很多情況下,例如大量的動態頁面,我們沒有辦法完全預知定義的css的id或者類名,而無法定義特別樣式。在一些可以控制的情況下,定義太多特殊類名用來區分,又會造成大量後期維護的困難。  3.良好的運用css組合方式,可以比較妥善的解決上面的2個問題。首先,公用樣式都被提取了,你修改一個按鈕,只需要改公共的部分。
  • CSS常考知識點
    彈層內一些固定在某處的元素 往往通過絕對定位來實現,比如關閉按鈕fixed(固定定位): 相對於屏幕視口(viewport)的位置來指定元素位置。元素的位置在屏幕滾動時不會改變,比如那種回到頂部的按鈕一般都是用此定位方式sticky(粘性定位):特性近似於relative和fixed的合體,經常用於實現垂直滾動 "吸頂" 效果面試官:元素設置了非static定位後可以用z-index做分層,說一下你對z-index的理解 (發散題)
  • 10個驚人的復古CSS套件
    NES.cssNES-style(8bit-like)的CSS框架,非常適合您的復古瀏覽器遊戲。https://nostalgic-css.github.io/NES.css/RPG UIWeb中用於老式RPG GUI的輕量級純CSS框架;包括按鈕,複選框,
  • React源碼之組件的實現與首次渲染
    react: v15.0.0 本文講 組件如何編譯 以及 ReactDOM.render 的渲染過程。 babel 的編譯 babel 將 React JSX 編譯成 JavaScript.
  • Axure實現Material Design的按鈕波浪特效
    本文將以實現谷歌材料設計按鈕的波浪特效為例,提供一種新的自定義Axure元件樣式的方法。需要說明的是:本文所提供的方法已經最大程度小白化,簡單易用,但是對於產品經理來說,本文的教程可能不具有實際的意義,僅提供給喜歡研究Axure Or Code的玩家。
  • 微信小程序抽獎轉盤組件怎麼做?
    微信小程序轉盤抽獎組件的實現思路1.界面樣式實現從抽獎轉盤的圖中我們可以看出,抽獎轉盤由外圓、扇面抽獎選項、抽獎按鈕組成,其中外圓不難實現,大家在處理微信小程序頭像的時候估計都已經用過了,那就是利用border-radius:50%來將一個正方形變成圓,這是外圓實現的關鍵
  • 「首席架構師推薦」關於React生態系統的一系列精選資源(3)
    - Javascript的不可變數據集合cortex - 用於使用React集中管理數據的JavaScript庫avers - 一個現代客戶端模型抽象庫imvvm - React的不可變模型 - 視圖 - 視圖模型morearty.js - 在純JavaScript中更好地管理Reactvaluable - React的不可變數據存儲react-resolver - 用於React組件的遞歸延遲加載數據的同構庫
  • Part 2:如何用Oculus Quest將自己公寓打造成MR科幻遊樂場
    來源:映維網 在今年8月,映維網分享了Fanbank首席技術官斯科特·蓋耶(Scott Geye)是如何用Quest和Unity等工具製作一個炫酷的混合現實公寓。現在,蓋耶又通過後續博文介紹了在所述基礎上進一步優化混合現實公寓的一系列方式。
  • 固化特性對光伏組件用封裝膠膜的幾點影響
    北極星太陽能光伏網訊:研究過氧化二異丙苯 ( DCP) 、復配交聯劑 ( T-50) 兩種固化劑與太陽能光伏發電組件用乙烯-醋酸乙烯共聚物 ( EVA) 封裝膠膜各項性能的關係。在使用過程中,研究者發現太陽能電池組件中的封裝材料在很大程度上會影響其光電轉換效率、使用壽命等。而有機高分子乙烯-醋酸乙烯酯 ( EVA) 膠膜以其突出的綜合性價比,以絕對優勢佔據了太陽能電池封裝材料的市場。作為封裝的關鍵材料,膠膜的主要作用是保護太陽能電池並將電池與蓋板、背板材料緊密貼合成為一個整體。
  • 全面掌握CSS基本知識點
    怎麼理解盒子模型答案解析html的每個元素都可以理解成是一個盒子,包含內容有margin、border、padding、content4個屬性。盒子模型常見有兩種w3c 標準模型,IE模型。其中標準模型:width是指的content的寬度,而IE模型是指的content + padding + border的總和。
  • 餓了麼公司前端團隊開源前端基於 Vue的桌面端組件庫-Element
    # npm i --save element-angular或者CDN目前可以通過 unpkg.com/element-ui 獲取到最新版本的資源,在頁面上引入 js 和 css 文件即可開始使用。<!
  • 什麼是css,css選擇器簡介
    什麼是csshtml頁面中引入css的方式1、內聯樣式:在標籤的style屬性中引入標籤引入外部css樣式文件,可多頁面復用,推薦工作中使用,需要單獨創建樣式文件<link rel="stylesheet" href="first.css">優先級(多種引入方式操作同一個標籤,以哪個為準)
  • CSS 編碼中超級有用的工具集合 - OSCHINA - 中文開源技術交流社區
    Pure 是來自雅虎的 CSS 框架,使用 Normalize.CSS 無需任何 JavaScript 代碼。框架基於響應式設計,提供多種樣式的組件,包括表格、表單、按鈕、表、導航等。標識使用非常簡單,整個框架非常輕量級,壓縮後只有 5.7k。 主頁: http://purecss.io/ CSS only responsive navigation
  • CSS3 transform之scale縮放|transition之過渡動畫調整手記
    既然是滑鼠移上去後再發生變化,那首先需在css文件中,針對圖片的hover增加個樣式,縮放比例為1.1。就像以前用flash製作動畫一樣,缺少中間過渡(這個知識點,源於已經過世的flash)。(沒玩過動態的gif錄屏,用ps製作了一個動畫來模擬。)看來沒那麼簡單,還需要對圖片對應的樣式寫上css3的transition(過渡)。
  • 前端工程化(ES6模塊化和webpack打包css,less,scss,圖片,字體...
    文件依賴*/通過模塊化解決上述問題/* 模塊化就是把單獨的一個功能封裝在一個模塊(文件)中,模塊之間相互隔離, 但是可以通過特定的接口公開內部成員,也可以依賴別的模塊.因為當我們訪問默認的 http://localhost:8080/的時候,看到的是一些文件和文件夾,想要查看我們的頁面 還需要點擊文件夾點擊文件才能查看,那麼我們希望默認就能看到一個頁面,而不是看到文件夾或者目錄。
  • HTML5+CSS+JS時間
    html,外觀修飾美觀與否就和css有關了,當然JS可能用的更多,再就是還可能用到css,js設計的框架.功能的多寡,比如造型酷炫,動態特效,多半都是JS來實現的,這裡我寫了一個博客的登錄界面(沒有設密碼,界面綠色按鈕就能進入博客,進入後用的java來實現的時間流動),圖片什麼的因為是別人的這裡就不上了,先上原始碼,我寫的代碼很簡單很容易應該就能看懂.
  • 封裝element-ui表格,我是這樣做的
    使用過element-ui的表格的同學應該都有這樣的體會,做一個簡單的表格還比較容易,但如果這個表格包含了頂部的按鈕,還有分頁,甚至再包含了行編輯,那開發工作量就成倍的增加,特別是在開發管理系統的時候,表格一個接一個的去開發, 即浪費時間,還對個人沒有什麼提升。今天小編帶來了自己封裝的一個表格,讓你用JSON就可以簡單的生成表格。
  • 如何在Vue3框架中使用Element Plus
    那麼,Vue3框架如何使用Element Plus?下面利用實例說明:操作步驟:1、打開電腦硬碟,新建一個文件夾aam,打開文件夾並打開Git命令窗口;輸入命令npm install -g @vue/cli