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

2021-01-06 酷扯兒

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

相關焦點

  • 基於jsoneditor二次封裝一個可實時預覽的json編輯器組件react版
    ,這樣一方面可以提高組件復用性和可擴展性,另一方面也帶來了項目開發的靈活性和可維護,方便多人開發協作.接下來文章將介紹如何使用react,開發一個自定義json編輯器組件.我們這裡使用了jsoneditor這個第三方庫,官方地址: jsoneditor 通過實現一個json在線編輯器,來學習如何一步步封裝自己的組件(不限於react,vue,原理類似).
  • 精通react/vue組件設計之實現一個輕量級可擴展的模態框組件
    模態框(Modal)組件一般會有如下需求點:能控制Modal主體的樣式提供Modal完全關閉後的回調能控制取消按鈕文字和樣式能控制確認按鈕文字和樣式控制modal展示的位置控制是否顯示右上角的關閉按鈕可以配置自定義關閉圖標配置關閉時是否銷毀Modal裡的子元素自定義模態框底部內容控制是否支持鍵盤esc關閉控制是否展示遮罩控制點擊蒙層是否允許關閉自定義遮罩樣式自定義標題控制對話框是否可見自定義對話框寬度暴露點擊遮罩層或右上角叉或取消按鈕的回調提供點擊確定回調
  • 精通react/vue組件設計之配合React Portals實現一個(Drawer)組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第六篇文章,內容依次從易到難,今天會用到react的高級API React Portals,它也是很多複雜組件必用的方法之一.
  • 《前端5分鐘》之使用純css實現網站換膚和焦點圖切換動畫
    你將收穫網站換膚設計方案介紹:target偽類介紹和用法以及如何使用css實現網站換膚transition動畫以及如何用純css實現焦點圖動畫效果展示1.網站換膚2.焦點圖動畫>實現思路1.網站換膚通常我們實現網站換膚都基於如下方式實現:方案一: 使用OOCSS模式,通過js動態切換公共類名來達到換膚效果方案二: 點擊不同的按鈕切換不同的樣式表,如下:theme-green.csstheme-red.csstheme-black.css方案三
  • 精通react/vue組件設計教你實現一個極具創意的加載(Loading)組件
    組件設計思路按照之前筆者總結的組件設計原則,我們第一步是要確認需求. 首先我們設計的不是後臺管理系統專用的加載動畫,而是作為一個C端產品的功用型加載動畫.我們都知道加載動畫的作用是:在用戶等待網頁時能看到有用的信息,比如網站介紹,引導, 公司信息等,緩解用戶焦慮.
  • 《精通react/vue組件設計》之快速實現一個可定製的進度條組件
    每一個組件只負責某一特定的表現或者功能)正文在開始組件設計之前希望大家對css3和js有一定的基礎.我們先看看實現後的組件效果:上圖可以知道封裝後的進度條組件通過對外暴露的接口(react/vue裡面可以看做
  • 精通react/vue組件設計之實現一個Tag(標籤)和Empty(空狀態)組件
    正文在開始組件設計之前希望大家對css3和js有一定的基礎.我們先看看實現後的組件效果:由圖可以知道tag組件可以自定義顏色主題(color theme), 可以手動關閉標籤, 空狀態主要是提供用戶數據展示用的, 實現起來很簡單,重點在圖標的使用上.
  • 精通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系列(一) -邂逅React開發
    聲明式編程組件化開發:組件化開發頁面目前前端的流行趨勢,我們會講複雜的界面拆分成一個個小的組件;如何合理的進行組件的劃分和設計也是後面我會講到的一個重點;原生案例實現為了演練React,我們可以提出一個小的需求:點擊下方的一個按鈕,點擊後文本改變為Hello React點擊下方的一個按鈕,點擊後文本改變為Hello React
  • React SSR 同構入門與原理
    兼容路由同構項目中當在瀏覽器中輸入 URL 後,瀏覽器是如何找到對應的界面?redux 都添加完畢後,最後我們在組件中使用 redux 的方式獲取數據,改造 Home 組件:import React from"react";import { Link } from"react-router-dom";import { connect } from"react-redux
  • CSS Modules 與 scoped 的不一樣
    css 的作用域表現。CSS Modules是一個CSS文件,其中所有類名和動畫名稱默認為局部作用域。使用JS編譯原生的CSS文件,使其具備模塊化的能力,該文件需要import使用。Scoped在vue文件中的style標籤上,有一個特殊的屬性:scoped。
  • 手把手教你用JS/Vue/React實現幸運水果機(80後情懷之作)
    ·所有其他小的物品(小77、小星星、小西瓜、小鈴鐺、小木瓜、小橙子) 1:2蘋果 1:5·橙子 1:10·木瓜 1:15·鈴鐺 1:20·西瓜 1:20·雙星 1:30·77 1:40·小BAR 1:25·中BAR 1:50·大BAR 1:100當玩家中獎之後,玩家點擊「開始」按鈕為收分,再次點擊「開始」按鈕為按照上一次押注再次進行遊戲。
  • 純CSS實現簡單骨骼動畫
    於是拿起工卡開始擺動,看看現實中的擺動效果是咋樣的,最後豁然開朗:原來現實中的心願牌(和工卡同理)在受力的時候,並不會整體擺動,而是會根據節點位置分成幾部分有關聯地擺動,這其實是個簡單的骨骼動畫!那到底怎麼去實現呢?骨骼動畫▼關於前端骨骼動畫的實現可以參考《骨骼動畫原理與前端實現淺談》,裡面簡單提及了css和canvas兩種實現方式。
  • 能解決 80% 需求的 10個 CSS動畫庫
    其它功能Animate CSS提供了一些基本的類來控制動畫的延遲和速度。** delay **可以添加 delay 類來延遲動畫的播放。使用 JQ$(".my-element").addClass("cssanimation fadeIn")還可以添加 infinite 類,這樣動畫就可以循環播放。
  • react
    我決定使用React製作3D輪播,作為一個獨立的練習,該框架如何與小型但可自我維持的組件一起工作。 我的第一個規則是添加和刪除元素,更改布局或旋轉輪播時動畫不會重置。 甚至在商業輪播中也缺少這種硬約束,從而在根本上改變了設計。
  • 尚矽谷前端視頻周 | React新版教程發布,聽著上頭,學到痴迷!
    知識點涵蓋全面,即包括:DOM-Diffing算法、虛擬DOM與JSX、React-Router、生命周期(新版+舊版)等基礎內容的講解;又包括:組件的基本使用、組件的封裝、組件的懶加載、組件的優化等組件核心技術;還包括:Pubsub、Redux、hooks、Context、ErrorBoundary、React-Redux等進階內容;更包括:高階函數、函數柯裡化、this指向分析、ES6
  • react中關於hook介紹及其使用
    前言最近由於公司的項目開發,就學習了在react關於hook的使用,對其有個基本的認識以及如何在項目中去應用hook。能夠解決你在不使用class組件的情況下去體現react的特性需要注意的一點就是hook和class組件是不能夠同時使用的,在實際的使用過程中一定要注意,否則就會出現報錯那麼接下來所要介紹的部分就是如何去使用hookstate hook
  • 兩種CSS 方法論你知道嗎?
    塊(Block):一個塊是視覺上或者語義上的一個整體,它是一個具體且唯一的一個元素,例如,頁面上的一個彈窗,或者是一個搜索框; 元素(Element):一般認為是塊的組成部分,元素比較用它父級的塊名稱做為前綴,例如,彈窗的標題、關閉按鈕、確認按鈕; 修飾符(Modifier):修飾符表示一個具體元素的特定狀態,例如,關閉按鈕在滑鼠沒放上去和放上去的時候,呈現的兩種狀態。