2019年的春節(jié)來的似乎格外的早,過完年相信很多童鞋都開始蠢蠢欲動了;筆者總結(jié)了多篇教程,結(jié)合平時自己的面試經(jīng)歷,整理了這份文檔,希望幫助大家來突擊一下前端知識的盲區(qū)。文章很長很長很長。。。。(建議先收藏,技術(shù)大佬請Ctrl+F4,面向基礎(chǔ))
整理不易,希望大家關(guān)注頭條號【JAVA后端架構(gòu)】
常生產(chǎn)生活中,我們會經(jīng)常讀到或使用各種類型的圖表。圓環(huán)(圓?。┍闶且环N較常見的類型,用于直觀展現(xiàn)某一數(shù)據(jù)指標(biāo)占整體的比例。本文以 HTML Canvas 的實現(xiàn)為主(當(dāng)然,SVG 黨可以在了解原理后自行實現(xiàn)),逐層介紹圓環(huán)圖表開發(fā)的一些主要思路和原理。
圖1 所示是一些我們平時比較常見的一些圓環(huán)(圓弧)效果。雖然圖形的主體構(gòu)成都是圓弧,但不同效果在信息傳達(dá)的功能上卻略有差異。如:
為了更加方便、完善地解決我們在業(yè)務(wù)開發(fā)時的具體需要,可以對這些風(fēng)格、樣式進(jìn)行一定分析、抽象,總結(jié)出一個通用組件需要具備的能力,如:
下面,我們著手于實現(xiàn)這樣一個功能全面、業(yè)務(wù)通用性較強(qiáng)的圓環(huán)組件。
繪制圓環(huán)造型的第一步,需要先繪制圓環(huán)圖表構(gòu)成要素,即一段一段的圓弧。而對于像下圖中這樣的兩種倒角效果(黃色部分圓弧兩端的樣式),既可以是直角,也可以是半圓。
因此,我們需要實現(xiàn)一個通用的方法來繪制圓弧,提供兩種倒角風(fēng)格給用戶。
圓弧繪制的思路如上圖所示,按先后順序大致分為幾個步驟:
(1)繪制圓弧起始端的半圓輪廓
(2)繪制圓弧的外邊緣輪廓
(3)繪制圓弧終止端的半圓輪廓
(4)繪制圓弧的內(nèi)邊緣輪廓
(5)閉合輪廓并填充色彩
注:由于 canvas 繪制圓弧的方法默認(rèn)是順時針方向,因而我們的繪圖步驟也是沿著順時針方向
以下是一些姿勢要領(lǐng):
在繪制端點(diǎn)半圓之前我們需要端點(diǎn)的位置坐標(biāo),以其實端為例,根據(jù)圓環(huán)的半徑(內(nèi)外徑的均值,即圓弧中線的半徑)和起始端點(diǎn)的角度如何計算圓上一點(diǎn)的坐標(biāo):
// 計算圓弧上某點(diǎn)的坐標(biāo)
// originX, originY - 圓心的坐標(biāo)
// radius - 圓環(huán)半徑,等于圓環(huán)內(nèi)、外徑的平均值,也即圓弧中線的半徑
// alpha - 弧度
function calcPosition(originX, originY, radius, alpha) {
return [
radius * Math.cos(alpha) + originX,
radius * Math.sin(alpha) + originY,
];
}
在 canvas 中繪制一個 arc,需要知道其起始角度和終止角度。由于 canvas 繪制默認(rèn)方向為屏幕順時針方向(屏幕 Z軸 的左手螺旋方向),從上面的示意圖中可以看出:
起始端半圓弧度范圍 - [radianStart - Math.PI, radianStart]
終止端半圓弧度范圍 - [radianEnd, radianEnd + Math.PI]
有了端點(diǎn)坐標(biāo)和起止角度,便可以繪制端點(diǎn)的半圓:
// 以起始端的半圓倒角為例
myCanvas.context.arc(
x,
y,
(radiusOutter - radiusInner) / 2, // 小圓半徑,等于圓環(huán)線寬的一半
radianStart - Math.PI,
radianStart
);
直角倒角風(fēng)格的繪制與半圓倒角圓弧的繪制步驟基本相同,主要差別在于不用繪制圓弧兩個端點(diǎn)的小半圓,改成繪制直線。背景圓弧的繪制也與前景圓弧方法一致。
上面的步驟可以繪制出圓弧的輪廓,要達(dá)到 圖1 那樣的視覺效果,我們需要給前面繪制出來的輪廓填充圖像。
沿著圓周方向的漸變,因為其圖像形似圓錐體的俯瞰效果,俗稱錐形漸變:
眾所周知,CSS 中有一個名為 conic-gradient 的屬性直接支持錐形漸變,而 HTML Canvas 的原生 API 目前還沒有類似的能力。那么,我們?nèi)绾卧?canvas 中繪制出這樣的圖像呢?
下面我們講下大致的原理:
(1)對用戶傳入的顏色進(jìn)行插值,得到一個顏色序列。
這里,我們直接使用 canvas 原生的 createLinearGradient 方法,在離屏 canvas 中繪制一個 1px 的線性漸變效果,圖像寬度正好是我們要插值的數(shù)量,漸變插值的結(jié)果也就是 canvas 上對應(yīng)像素位置的色值。
顏色插值(漸變?nèi)∩┐a實現(xiàn)如下:
// 用于實現(xiàn)顏色插值的工具類
export default class ColorInterpolate {
// 參數(shù)01: stops - 為要插值的顏色序列,數(shù)據(jù)格式形如:[[0, 'red'], [0.5, 'green'], [1.0, 'yellow']]
// 參數(shù)02: segment - 插值段落數(shù),即插值結(jié)果的顏色值的數(shù)量
constructor(stops = [], segment = 100) {
// 構(gòu)建離屏 canvas
const canvas = document.createElement('canvas');
canvas.width = segment;
canvas.height = 1;
this.ctx = canvas.getContext('2d');
// 繪制線性漸變
const gradient = this.ctx.createLinearGradient(0, 0, segment, 0);
for (let [offset, color] of stops) {
gradient.addColorStop(offset, color);
}
this.ctx.fillStyle = gradient;
this.ctx.fillRect(0, 0, segment, 1);
}
// 根據(jù)位置偏移量獲取插值后的色值
getColor(offset) {
const imgData = this.ctx.getImageData(offset, 0, 1, 1);
return `rgba(${imgData.data.slice(0, 3).join(',')}, ${imgData.data[3] / 255})`;
}
}
(2)如下圖所示,我們可以把漸變的圖像看成是由足夠多填充了單個色值的小 “扇面” 拼接而成。
按照這樣的思路,我們只需要遍歷上面色彩插值得到的各個顏色,然后逐個繪制小扇面,便可得到一個錐形漸變圖像。
為此我們封裝了一個名為 createConicalGradient 的方法,其使用習(xí)慣與 canvas 原生的 createLinearGradient 和 createRadialGradient 方法相似。具體代碼見 我的 Github(覺得有用的童鞋可以 star 一下)。
在數(shù)值發(fā)生改變時,我們的圖表需要一個能夠跟隨數(shù)據(jù)改變的過渡動畫效果,對于 canvas 而言,便是清除舊圖像然后繪制新一幀圖像。這里有一些方法包裝上的技巧:
// 注:偽代碼,真實場景建議 OOP 方式包裝為工具類
let _animTick = null;
let _animFrames = null;
let _frameData = null;
let _animDiff = null;
// 動畫方法
function _animate(duration) {
if (_animTick === null) {
// 根據(jù)動畫時長 duration 計算整個動畫一共需要多少幀(以 60fps 計算)
_animFrames = Math.round((duration / 1e3) * 60);
// 相鄰兩幀動畫的數(shù)據(jù)變化
_animDiff = _calcAnimDiff(_animFrames);
// 動畫幀數(shù)標(biāo)識
_animTick = 0;
}
// 當(dāng)前幀的數(shù)據(jù)值
_frameData = _caclCurentData(_animDiff, _animTick);
_renderFrame(_frameData);
if (_animTick !== null && _animTick < _animFrames) {
// 繼續(xù)執(zhí)行動畫
window.requestAnimationFrame(() => {
_animate();
_animTick += 1;
});
} else {
// 動畫結(jié)束
_renderFrame(_frameData);
_animTick = null;
}
}
// 繪制當(dāng)前幀
function _renderFrame(data) {
// ...
}
// 計算動畫相鄰幀的數(shù)據(jù)差異
function _calcAnimDiff() {
// ...
}
// 根據(jù)兩幀數(shù)據(jù)差計算當(dāng)前幀
function _caclCurentData() {
// ...
}
在過渡動畫執(zhí)行的過程中,需要考慮兩種不同的模式:一種是漸變圖像不變化,僅是圓弧的輪廓從舊狀態(tài)變化到新狀態(tài);一種是漸變圖像的夾角范圍跟隨輪廓的大小改變。
這兩種模式其實都有一定意義:前者可以使用不同顏色代表數(shù)值的不同狀態(tài);后者僅僅是將漸變的顏色看成一種裝飾效果。
比較關(guān)鍵的原理都介紹完了,最后展示一下我們封裝的圖表組件的效果(右側(cè) GUI 部分是我們自研的設(shè)計引擎的編輯效果):
本文是可視化圖表開發(fā)的一個小案例,。篇幅比較短,還有很多細(xì)節(jié)沒有展開討論。感興趣的童鞋歡迎交流、留言!
“等風(fēng)來不如追風(fēng)去,追逐的過程就是人生的意義”。
借朋友吉言,“2018在頭條,2019成為頭條”,這就是我2019的目標(biāo),我已經(jīng)在追風(fēng)的路上。你呢?不要停下腳步,繼續(xù)前行吧。
今天來個實用的小知識,看下圖:
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
很多人看到左右的波浪邊框,第一想法,應(yīng)該是用圖片實現(xiàn)?,F(xiàn)在我們就打破這一想法,用CSS搞定這個效果。
radial-gradient() 函數(shù)用徑向漸變創(chuàng)建 "圖像"。徑向漸變由中心點(diǎn)定義。為了創(chuàng)建徑向漸變你必須設(shè)置兩個終止色。
語法: background: radial-gradient(shape size at position, start-color, ..., last-color);
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
<div class="coupon"></div>
這里用radial-gradient繪制一個圓,設(shè)置left為1px,top為8px,形成半圓。
.coupon { position: relative; width: 400px; height: 160px; margin: 50px auto; background-image: radial-gradient( circle at 1px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px); }
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
看看原本是這樣,這里的left是8px
.coupon { ... background-image: radial-gradient( circle at 8px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px); ... }
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
設(shè)置背景大小,y軸默認(rèn)平鋪,x軸不允許平鋪,形成多個半圓,造就波浪造型。
.coupon { ... background-image: radial-gradient( circle at 1px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px); background-size: 200px 18px; background-repeat-x: no-repeat; ... }
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
同理,我們添加右邊波浪,
.coupon { ... background-image: radial-gradient( circle at 1px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px), radial-gradient( circle at 199px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px); background-size: 200px 18px; background-position: 0 0, 200px 0; background-repeat-x: no-repeat; }
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
<div class="coupon">50元</div>
用:before偽類,制作中間的虛線,:after偽類,添加“立即領(lǐng)取”文字。同時添加金額(50元)樣式。
.coupon { ... font-size: 60px; color: #fff; font-weight: bold; line-height: 160px; padding-left: 60px; box-sizing: border-box; cursor: pointer; } .coupon::before { position: absolute; content: ""; left: 240px; top: 0; bottom: 0; width: 0; border-left: 1px dashed #fff; } .coupon::after { position: absolute; content: "立即領(lǐng)取"; font-size: 26px; width: 70px; top: 50%; right: 2%; transform: translate(-50%, -50%); line-height: 40px; letter-spacing: 5px; }
CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
演示地址:CSS3徑向漸變實現(xiàn)優(yōu)惠券波浪造型
CSS3 box-shadow實現(xiàn)背景動畫
從淺到深的學(xué)習(xí) CSS3陰影(box-shadow)
CSS3線性漸變、陰影、縮放實現(xiàn)動畫下雨效果
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。