Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
2019年的春節(jié)來(lái)的似乎格外的早,過(guò)完年相信很多童鞋都開(kāi)始蠢蠢欲動(dòng)了;筆者總結(jié)了多篇教程,結(jié)合平時(shí)自己的面試經(jīng)歷,整理了這份文檔,希望幫助大家來(lái)突擊一下前端知識(shí)的盲區(qū)。文章很長(zhǎng)很長(zhǎng)很長(zhǎng)。。。。(建議先收藏,技術(shù)大佬請(qǐng)Ctrl+F4,面向基礎(chǔ))
整理不易,希望大家關(guān)注頭條號(hào)【JAVA后端架構(gòu)】
常生產(chǎn)生活中,我們會(huì)經(jīng)常讀到或使用各種類型的圖表。圓環(huán)(圓弧)便是一種較常見(jiàn)的類型,用于直觀展現(xiàn)某一數(shù)據(jù)指標(biāo)占整體的比例。本文以 HTML Canvas 的實(shí)現(xiàn)為主(當(dāng)然,SVG 黨可以在了解原理后自行實(shí)現(xiàn)),逐層介紹圓環(huán)圖表開(kāi)發(fā)的一些主要思路和原理。
圖1 所示是一些我們平時(shí)比較常見(jiàn)的一些圓環(huán)(圓弧)效果。雖然圖形的主體構(gòu)成都是圓弧,但不同效果在信息傳達(dá)的功能上卻略有差異。如:
為了更加方便、完善地解決我們?cè)跇I(yè)務(wù)開(kāi)發(fā)時(shí)的具體需要,可以對(duì)這些風(fēng)格、樣式進(jìn)行一定分析、抽象,總結(jié)出一個(gè)通用組件需要具備的能力,如:
下面,我們著手于實(shí)現(xiàn)這樣一個(gè)功能全面、業(yè)務(wù)通用性較強(qiáng)的圓環(huán)組件。
繪制圓環(huán)造型的第一步,需要先繪制圓環(huán)圖表構(gòu)成要素,即一段一段的圓弧。而對(duì)于像下圖中這樣的兩種倒角效果(黃色部分圓弧兩端的樣式),既可以是直角,也可以是半圓。
因此,我們需要實(shí)現(xiàn)一個(gè)通用的方法來(lái)繪制圓弧,提供兩種倒角風(fēng)格給用戶。
圓弧繪制的思路如上圖所示,按先后順序大致分為幾個(gè)步驟:
(1)繪制圓弧起始端的半圓輪廓
(2)繪制圓弧的外邊緣輪廓
(3)繪制圓弧終止端的半圓輪廓
(4)繪制圓弧的內(nèi)邊緣輪廓
(5)閉合輪廓并填充色彩
注:由于 canvas 繪制圓弧的方法默認(rèn)是順時(shí)針?lè)较颍蚨覀兊睦L圖步驟也是沿著順時(shí)針?lè)较?/p>
以下是一些姿勢(shì)要領(lǐng):
在繪制端點(diǎn)半圓之前我們需要端點(diǎn)的位置坐標(biāo),以其實(shí)端為例,根據(jù)圓環(huán)的半徑(內(nèi)外徑的均值,即圓弧中線的半徑)和起始端點(diǎn)的角度如何計(jì)算圓上一點(diǎn)的坐標(biāo):
// 計(jì)算圓弧上某點(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 中繪制一個(gè) arc,需要知道其起始角度和終止角度。由于 canvas 繪制默認(rèn)方向?yàn)槠聊豁槙r(shí)針?lè)较颍ㄆ聊?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)格的繪制與半圓倒角圓弧的繪制步驟基本相同,主要差別在于不用繪制圓弧兩個(gè)端點(diǎn)的小半圓,改成繪制直線。背景圓弧的繪制也與前景圓弧方法一致。
上面的步驟可以繪制出圓弧的輪廓,要達(dá)到 圖1 那樣的視覺(jué)效果,我們需要給前面繪制出來(lái)的輪廓填充圖像。
沿著圓周方向的漸變,因?yàn)槠鋱D像形似圓錐體的俯瞰效果,俗稱錐形漸變:
眾所周知,CSS 中有一個(gè)名為 conic-gradient 的屬性直接支持錐形漸變,而 HTML Canvas 的原生 API 目前還沒(méi)有類似的能力。那么,我們?nèi)绾卧?canvas 中繪制出這樣的圖像呢?
下面我們講下大致的原理:
(1)對(duì)用戶傳入的顏色進(jìn)行插值,得到一個(gè)顏色序列。
這里,我們直接使用 canvas 原生的 createLinearGradient 方法,在離屏 canvas 中繪制一個(gè) 1px 的線性漸變效果,圖像寬度正好是我們要插值的數(shù)量,漸變插值的結(jié)果也就是 canvas 上對(duì)應(yīng)像素位置的色值。
顏色插值(漸變?nèi)∩┐a實(shí)現(xiàn)如下:
// 用于實(shí)現(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)如下圖所示,我們可以把漸變的圖像看成是由足夠多填充了單個(gè)色值的小 “扇面” 拼接而成。
按照這樣的思路,我們只需要遍歷上面色彩插值得到的各個(gè)顏色,然后逐個(gè)繪制小扇面,便可得到一個(gè)錐形漸變圖像。
為此我們封裝了一個(gè)名為 createConicalGradient 的方法,其使用習(xí)慣與 canvas 原生的 createLinearGradient 和 createRadialGradient 方法相似。具體代碼見(jiàn) 我的 Github(覺(jué)得有用的童鞋可以 star 一下)。
在數(shù)值發(fā)生改變時(shí),我們的圖表需要一個(gè)能夠跟隨數(shù)據(jù)改變的過(guò)渡動(dòng)畫(huà)效果,對(duì)于 canvas 而言,便是清除舊圖像然后繪制新一幀圖像。這里有一些方法包裝上的技巧:
// 注:偽代碼,真實(shí)場(chǎng)景建議 OOP 方式包裝為工具類
let _animTick = null;
let _animFrames = null;
let _frameData = null;
let _animDiff = null;
// 動(dòng)畫(huà)方法
function _animate(duration) {
if (_animTick === null) {
// 根據(jù)動(dòng)畫(huà)時(shí)長(zhǎng) duration 計(jì)算整個(gè)動(dòng)畫(huà)一共需要多少幀(以 60fps 計(jì)算)
_animFrames = Math.round((duration / 1e3) * 60);
// 相鄰兩幀動(dòng)畫(huà)的數(shù)據(jù)變化
_animDiff = _calcAnimDiff(_animFrames);
// 動(dòng)畫(huà)幀數(shù)標(biāo)識(shí)
_animTick = 0;
}
// 當(dāng)前幀的數(shù)據(jù)值
_frameData = _caclCurentData(_animDiff, _animTick);
_renderFrame(_frameData);
if (_animTick !== null && _animTick < _animFrames) {
// 繼續(xù)執(zhí)行動(dòng)畫(huà)
window.requestAnimationFrame(() => {
_animate();
_animTick += 1;
});
} else {
// 動(dòng)畫(huà)結(jié)束
_renderFrame(_frameData);
_animTick = null;
}
}
// 繪制當(dāng)前幀
function _renderFrame(data) {
// ...
}
// 計(jì)算動(dòng)畫(huà)相鄰幀的數(shù)據(jù)差異
function _calcAnimDiff() {
// ...
}
// 根據(jù)兩幀數(shù)據(jù)差計(jì)算當(dāng)前幀
function _caclCurentData() {
// ...
}
在過(guò)渡動(dòng)畫(huà)執(zhí)行的過(guò)程中,需要考慮兩種不同的模式:一種是漸變圖像不變化,僅是圓弧的輪廓從舊狀態(tài)變化到新?tīng)顟B(tài);一種是漸變圖像的夾角范圍跟隨輪廓的大小改變。
這兩種模式其實(shí)都有一定意義:前者可以使用不同顏色代表數(shù)值的不同狀態(tài);后者僅僅是將漸變的顏色看成一種裝飾效果。
比較關(guān)鍵的原理都介紹完了,最后展示一下我們封裝的圖表組件的效果(右側(cè) GUI 部分是我們自研的設(shè)計(jì)引擎的編輯效果):
本文是可視化圖表開(kāi)發(fā)的一個(gè)小案例,。篇幅比較短,還有很多細(xì)節(jié)沒(méi)有展開(kāi)討論。感興趣的童鞋歡迎交流、留言!
“等風(fēng)來(lái)不如追風(fēng)去,追逐的過(guò)程就是人生的意義”。
借朋友吉言,“2018在頭條,2019成為頭條”,這就是我2019的目標(biāo),我已經(jīng)在追風(fēng)的路上。你呢?不要停下腳步,繼續(xù)前行吧。
今天來(lái)個(gè)實(shí)用的小知識(shí),看下圖:
CSS3徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
很多人看到左右的波浪邊框,第一想法,應(yīng)該是用圖片實(shí)現(xiàn)。現(xiàn)在我們就打破這一想法,用CSS搞定這個(gè)效果。
radial-gradient() 函數(shù)用徑向漸變創(chuàng)建 "圖像"。徑向漸變由中心點(diǎn)定義。為了創(chuàng)建徑向漸變你必須設(shè)置兩個(gè)終止色。
語(yǔ)法: background: radial-gradient(shape size at position, start-color, ..., last-color);
CSS3徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
<div class="coupon"></div>
這里用radial-gradient繪制一個(gè)圓,設(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徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
看看原本是這樣,這里的left是8px
.coupon { ... background-image: radial-gradient( circle at 8px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px); ... }
CSS3徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
設(shè)置背景大小,y軸默認(rèn)平鋪,x軸不允許平鋪,形成多個(gè)半圓,造就波浪造型。
.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徑向漸變實(shí)現(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徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
<div class="coupon">50元</div>
用:before偽類,制作中間的虛線,:after偽類,添加“立即領(lǐng)取”文字。同時(shí)添加金額(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徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
演示地址:CSS3徑向漸變實(shí)現(xiàn)優(yōu)惠券波浪造型
CSS3 box-shadow實(shí)現(xiàn)背景動(dòng)畫(huà)
從淺到深的學(xué)習(xí) CSS3陰影(box-shadow)
CSS3線性漸變、陰影、縮放實(shí)現(xiàn)動(dòng)畫(huà)下雨效果
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。