前言
文字特效設計一直是困擾 Web 前端 css 世界多年的問題, 比如如何用純 Css 實現文字描邊, 漸變, 陰影等, 由于受限于瀏覽器兼容性的問題, 我們不得不使用其他替代方案來實現.
平時工作中我們使用 PS 等設計工具能很容易的實現文字漸變等特效, 但是隨著可視化技術的成熟, 我們需要把傳統軟件的能力移植到 web 端, 讓用戶在 Web 端也能有和桌面軟件一樣的體驗效果, 那么我們就需要想一套優雅的方案了, 接下來我會從文本特效的
- 描邊
- 陰影
- 漸變
這3個緯度來和大家分享一下用前端實現的解決方案, 并提供一種可復用的文本組件設計方案, 讓大家更容易的來是使用文字特效.
你將收獲
- 文字漸變, 陰影, 描邊的3種方案實現
- 組件設計的一些通用方法和原則
- 如果在低代碼平臺中優雅的設計功能強大的文字組件
- 技術腦洞
正文
先來介紹一下文字描邊和陰影的設計方案.
文字描邊的2種css方案
- css3 的 text-stroke
我們可以使用如下樣式來實現簡單的描邊效果:
-webkit-text-stroke: 1px #000000;
在w3c上演示的效果如下:
圖片
但是缺點就是兼容性不是特別好, 如果不考慮太多瀏覽器的兼容問題, 大家可以直接用這種方案.
- 使用text-shadow 模仿描邊效果
使用此方案有點就是兼容性好, 不需要加瀏覽器前綴, 方案如下:
.text-shadow {
text-shadow: #000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0;
color: #fff;
}
顯示效果:
剛才介紹的文字描邊引用了陰影來實現, 自然我們要想實現文字陰影, 可以直接使用text-shadow, 并且我們可以實現多種陰影的效果, 甚至能用它實現3D文字效果(并不建議這么做, 性能有待考量)
實現文字漸變的3種方案
文字漸變css3也提供了對應的特性, 但是兼容性并不是很好, 目前還不推薦使用, 有如下2種方案:
// 方案1. 背景裁切+背景漸變實現
{
background-image: -webkit-gradient(linear, left center, right center, from(blue), to(red)); /* 背景色漸變 */
-webkit-background-clip: text; /* 設置背景的顯示區域 */
-webkit-text-fill-color: transparent; /* 避免字體顏色覆蓋 */
}
// 方案2: mask
{
-webkit-mask-image: -webkit-gradient(linear, 0 0, 0 bottom, from(red), to(blue));
}
效果如下:
根據筆者的實驗, 更推薦用另一種兼容性更好的方案—— svg.
接下來我們看看使用svg實現文字漸變的效果.
具體實現方案大家需要具備一點的 svg 基礎知識, 這里給大家分享一下我的實現代碼:
<svg style={{
width: '100%',
textShadow: `${shadow[1]}px ${shadow[0]}px ${shadowSize}px`,
}}>
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style={{'stopColor': upColor, 'stopOpacity': 1}} />
<stop offset="100%" style={{'stopColor': backColor, 'stopOpacity':1}} />
</linearGradient>
</defs>
<text x={pos[1]} y={pos[0]} fill="url(#grad)" style={{fontSize, textAnchor: 'start'}}>Dooring文字漸變效果</text>
</svg>
以上代碼中upColor(前景色), backColor(背景色), 我們可以動態配置, 同時我們還可以改變漸變的方向, 通過linearGradient 標簽的xy屬性. 大家感興趣的可以私下研究一下.
通過這種方案, 我們就能實現展現能力非常強的文字特效了. 接下來我會分享一下如何用組件化或者低代碼的思路, 將文字特效封裝成一個高可復用的組件.
如果優雅的設計功能強大的文字組件
在介紹組件設計思路之前,有必要介紹一下著名的SOLID原則.
SOLID(單一功能、開閉原則、里氏替換、接口隔離以及依賴反轉)是由羅伯特·C·馬丁提出的面向對象編程和面向對象設計的五個基本原則。利用這些原則,程序員能更容易和高效的開發一個可維護和擴展的系統。 SOLID被典型的應用在測試驅動開發上,并且是敏捷開發以及自適應軟件開發的基本原則的重要組成部分。
- S 單一功能原則: 規定每個類都應該有一個單一的功能,并且該功能應該由這個類完全封裝起來。所有它的服務都應該嚴密的和該功能保持一致。
- O 開閉原則: 規定“軟件中的對象(類,模塊,函數等等)應該對于擴展是開放的,但是對于修改是封閉的”,這意味著一個實體是允許在不改變它的源代碼的前提下變更它的行為。遵循這種原則的代碼在擴展時并不需要改變。
- L 里氏替換原則: 派生類(子類)對象可以在程序中代替其基類(超類)對象,是對子類型的特別定義.
- I 接口隔離原則: 指明應用或者對象應該不依賴于它不使用的方法。接口隔離原則(ISP)拆分非常龐大臃腫的接口成為更小的和更具體的接口,這樣應用或對象只需要知道它們感興趣的方法。這種縮小的接口也被稱為角色接口。接口隔離原則(ISP)的目的是系統去耦合,從而容易重構,更改和重新部署。接口隔離原則是在SOLID (面向對象設計)中五個面向對象設計(OOD)的原則之一,類似于在GRASP (面向對象設計)中的高內聚性。
- D 依賴反轉原則: 是指一種特定的解耦 形式,使得高層次的模塊不依賴于低層次的模塊的實現細節,依賴關系被顛倒(反轉),從而使得低層次模塊依賴于高層次模塊的需求抽象。
掌握好這5個原則將有利于我們開發出更優秀的組件,請默默記住.接下來我們來看看文字特效組件的設計思路.
圖片
以上組件設計是結合 組件化 + 低代碼 的開發思路設計的, 組件化幫助我們將業務實現封裝地更易用, 低代碼的思路讓我們將組件和業務結合起來, 通過一種更系統的思維來設計組件.
具體在組件場景的用法如下:
<Text
fontSize={14}
fontFamily=宋體"
pos ={[0, 20]}
animation="fade in"
>
文字漸變
</Text>
這種方式可以更好的讓前端工程師無需關注底層實現直接使用復雜功能, 更近一步地考慮, 如果要讓組件通過零代碼的方式拖拽配置生成, 我們就需要考慮更多系統性的問題, 比如組件的Schema定義, 系統交互事件如何配置, 如何通過屬性配置面板來實現組件樣式的搭建.
目前我在Dooring零代碼平臺中的實現方式是對組件配置項設計一套DSL結構, 通過DSL來動態渲染配置項:
const Chart: ChartSchema = {
// 配置項列表
editAttrs: [
{
key: 'layerName',
type: 'Text',
cate: 'base',
},
{
key: 'y',
type: 'Number',
cate: 'base',
},
...DataConfig, // 數據配置項
...eventConfig, // 事件配置項
],
// 組件默認屬性
config: {
width: 200,
height: 200,
zIndex: 1,
layerName: '柱狀圖',
labelColor: 'rgba(188,200,212,1)',
// ... 其他配置初始值
multiColor: ['rgba(91, 143, 249, 1)', 'rgba(91, 143, 249, 1)', 'rgba(91, 143, 249,,1)', 'rgba(91, 143, 249, 1)'],
data: [
{
name: 'A',
value: 25,
},
{
name: 'B',
value: 66,
}
],
},
}
我們通過遍歷 editAttrs 結構就可以得到一個屬性配置面板:
圖片
按照這樣的思路, 我們實現一個兼容低代碼平臺的文字特效組件結構可能長這樣:
import React, { memo, useState, useRef, useEffect } from 'react'
import { ITextConfig } from './schema';
import { cpClick, isEditorPage } from '@/utils/cpTool';
import ani from '../animate.css';
import classnames from 'classnames';
const TextCp = memo((props: ITextConfig) => {
const {
cpName,
text,
fontSize,
fontFamily,
pos = [0, 20],
dir,
bgColor,
padding,
radius,
animation,
animationTurn,
delay,
interaction = {},
// ...
} = props;
const { type, content } = interaction;
const [visible, setVisible] = useState(false);
const [isEditable, setEditable] = useState(false);
const textRef = useRef<any>(null);
const [data, setData] = useState<string>('');
const handleClick = () => {
cpClick(type, content, () => {
setVisible(true)
})
}
const handleDoubleClick = () => {
if(!isEditorPage()) {
return
}
setEditable(true);
setTimeout(() => {
textRef.current.focus();
textRef.current.value = text;
}, 30)
}
const handleSaveText = () => {
window.handleTextUpdate(textRef.current.value)
setEditable(false)
}
// 數據源...
const direction = dir === 'hor' ? {x1: "0%", y1: "0%", x2: "100%", y2:"0%"} : {x1: "0%", y1: "0%", x2: "0%", y2:"100%"}
return (
<>
<div
className={classnames('dooring-text', animation !== 'none' && ani[`animate__${animation}`])}
style={{
fontSize: _gaw(fontSize),
fontFamily,
fontWeight: +fontWeight,
backgroundColor: bgColor,
padding,
borderRadius: radius,
animationIterationCount: animationTurn,
animationDelay: delay + 's',
letterSpacing: space + 'px',
}}
onClick={handleClick}
onDoubleClick={handleDoubleClick}
>
<svg style={{
width: '100%',
textShadow: `${shadow[1]}px ${shadow[0]}px ${shadowSize}px`,
}}>
<defs>
<linearGradient id="grad" {...direction}>
<stop offset="0%" style={{'stopColor': upColor, 'stopOpacity': 1}} />
<stop offset="100%" style={{'stopColor': backColor, 'stopOpacity':1}} />
</linearGradient>
</defs>
<text x={pos[1]} y={pos[0]} fill="url(#grad)" style={{fontSize, textAnchor: 'start'}}>{ addSpaceEntity(data || text) }</text>
</svg>
{ isEditable && <input ref={textRef} style={{
border: 'none',
position: 'absolute',
top: 0,
left: 0,
fontSize,
fontFamily,
width: '100%',
height: '100%',
textAlign: 'left',
color: 'rgba(60,60,60,1)'
}} onBlur={handleSaveText} />}
</div>
</>
);
});
export default TextCp;
這樣我們既可以單獨使用組件, 也可以在Dooring零代碼平臺中通過拖拽的方式更簡單地使用組件了.
最后
我們可以通過上面介紹的方式將任何已有組件包裝的更擴展, 組件的開發結構變成了:
- schema(DSL定義)
- index(組件實現)
這樣設計之后組件會有更好的可移植性和擴展性, 當然未來webcompoent更加成熟穩定了, 我們可以通過更健壯的模式來設計組件. 后續我也會持續分享可視化低代碼相關的技術實現, 歡迎大家隨時和我交流.