2019年的春节来的似乎格外的早,过完年相信很多童鞋都开始蠢蠢欲动了;笔者总结了多篇教程,结合平时自己的面试经历,整理了这份文档,希望帮助大家来突击一下前端知识的盲区。文章很长很长很长。。。。(建议先收藏,技术大佬请Ctrl+F4,面向基础)
整理不易,希望大家关注头条号【JAVA后端架构】
常生产生活中,我们会经常读到或使用各种类型的图表。圆环(圆弧)便是一种较常见的类型,用于直观展现某一数据指标占整体的比例。本文以 HTML Canvas 的实现为主(当然,SVG 党可以在了解原理后自行实现),逐层介绍圆环图表开发的一些主要思路和原理。
图1 所示是一些我们平时比较常见的一些圆环(圆弧)效果。虽然图形的主体构成都是圆弧,但不同效果在信息传达的功能上却略有差异。如:
为了更加方便、完善地解决我们在业务开发时的具体需要,可以对这些风格、样式进行一定分析、抽象,总结出一个通用组件需要具备的能力,如:
下面,我们着手于实现这样一个功能全面、业务通用性较强的圆环组件。
绘制圆环造型的第一步,需要先绘制圆环图表构成要素,即一段一段的圆弧。而对于像下图中这样的两种倒角效果(黄色部分圆弧两端的样式),既可以是直角,也可以是半圆。
因此,我们需要实现一个通用的方法来绘制圆弧,提供两种倒角风格给用户。
圆弧绘制的思路如上图所示,按先后顺序大致分为几个步骤:
(1)绘制圆弧起始端的半圆轮廓
(2)绘制圆弧的外边缘轮廓
(3)绘制圆弧终止端的半圆轮廓
(4)绘制圆弧的内边缘轮廓
(5)闭合轮廓并填充色彩
注:由于 canvas 绘制圆弧的方法默认是顺时针方向,因而我们的绘图步骤也是沿着顺时针方向
以下是一些姿势要领:
在绘制端点半圆之前我们需要端点的位置坐标,以其实端为例,根据圆环的半径(内外径的均值,即圆弧中线的半径)和起始端点的角度如何计算圆上一点的坐标:
// 计算圆弧上某点的坐标
// originX, originY - 圆心的坐标
// radius - 圆环半径,等于圆环内、外径的平均值,也即圆弧中线的半径
// alpha - 弧度
function calcPosition(originX, originY, radius, alpha) {
return [
radius * Math.cos(alpha) + originX,
radius * Math.sin(alpha) + originY,
];
}
在 canvas 中绘制一个 arc,需要知道其起始角度和终止角度。由于 canvas 绘制默认方向为屏幕顺时针方向(屏幕 Z轴 的左手螺旋方向),从上面的示意图中可以看出:
起始端半圆弧度范围 - [radianStart - Math.PI, radianStart]
终止端半圆弧度范围 - [radianEnd, radianEnd + Math.PI]
有了端点坐标和起止角度,便可以绘制端点的半圆:
// 以起始端的半圆倒角为例
myCanvas.context.arc(
x,
y,
(radiusOutter - radiusInner) / 2, // 小圆半径,等于圆环线宽的一半
radianStart - Math.PI,
radianStart
);
直角倒角风格的绘制与半圆倒角圆弧的绘制步骤基本相同,主要差别在于不用绘制圆弧两个端点的小半圆,改成绘制直线。背景圆弧的绘制也与前景圆弧方法一致。
上面的步骤可以绘制出圆弧的轮廓,要达到 图1 那样的视觉效果,我们需要给前面绘制出来的轮廓填充图像。
沿着圆周方向的渐变,因为其图像形似圆锥体的俯瞰效果,俗称锥形渐变:
众所周知,CSS 中有一个名为 conic-gradient 的属性直接支持锥形渐变,而 HTML Canvas 的原生 API 目前还没有类似的能力。那么,我们如何在 canvas 中绘制出这样的图像呢?
下面我们讲下大致的原理:
(1)对用户传入的颜色进行插值,得到一个颜色序列。
这里,我们直接使用 canvas 原生的 createLinearGradient 方法,在离屏 canvas 中绘制一个 1px 的线性渐变效果,图像宽度正好是我们要插值的数量,渐变插值的结果也就是 canvas 上对应像素位置的色值。
颜色插值(渐变取色)代码实现如下:
// 用于实现颜色插值的工具类
export default class ColorInterpolate {
// 参数01: stops - 为要插值的颜色序列,数据格式形如:[[0, 'red'], [0.5, 'green'], [1.0, 'yellow']]
// 参数02: segment - 插值段落数,即插值结果的颜色值的数量
constructor(stops = [], segment = 100) {
// 构建离屏 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);
}
// 根据位置偏移量获取插值后的色值
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 的方法,其使用习惯与 canvas 原生的 createLinearGradient 和 createRadialGradient 方法相似。具体代码见 我的 Github(觉得有用的童鞋可以 star 一下)。
在数值发生改变时,我们的图表需要一个能够跟随数据改变的过渡动画效果,对于 canvas 而言,便是清除旧图像然后绘制新一帧图像。这里有一些方法包装上的技巧:
// 注:伪代码,真实场景建议 OOP 方式包装为工具类
let _animTick = null;
let _animFrames = null;
let _frameData = null;
let _animDiff = null;
// 动画方法
function _animate(duration) {
if (_animTick === null) {
// 根据动画时长 duration 计算整个动画一共需要多少帧(以 60fps 计算)
_animFrames = Math.round((duration / 1e3) * 60);
// 相邻两帧动画的数据变化
_animDiff = _calcAnimDiff(_animFrames);
// 动画帧数标识
_animTick = 0;
}
// 当前帧的数据值
_frameData = _caclCurentData(_animDiff, _animTick);
_renderFrame(_frameData);
if (_animTick !== null && _animTick < _animFrames) {
// 继续执行动画
window.requestAnimationFrame(() => {
_animate();
_animTick += 1;
});
} else {
// 动画结束
_renderFrame(_frameData);
_animTick = null;
}
}
// 绘制当前帧
function _renderFrame(data) {
// ...
}
// 计算动画相邻帧的数据差异
function _calcAnimDiff() {
// ...
}
// 根据两帧数据差计算当前帧
function _caclCurentData() {
// ...
}
在过渡动画执行的过程中,需要考虑两种不同的模式:一种是渐变图像不变化,仅是圆弧的轮廓从旧状态变化到新状态;一种是渐变图像的夹角范围跟随轮廓的大小改变。
这两种模式其实都有一定意义:前者可以使用不同颜色代表数值的不同状态;后者仅仅是将渐变的颜色看成一种装饰效果。
比较关键的原理都介绍完了,最后展示一下我们封装的图表组件的效果(右侧 GUI 部分是我们自研的设计引擎的编辑效果):
本文是可视化图表开发的一个小案例,。篇幅比较短,还有很多细节没有展开讨论。感兴趣的童鞋欢迎交流、留言!
“等风来不如追风去,追逐的过程就是人生的意义”。
借朋友吉言,“2018在头条,2019成为头条”,这就是我2019的目标,我已经在追风的路上。你呢?不要停下脚步,继续前行吧。
今天来个实用的小知识,看下图:
CSS3径向渐变实现优惠券波浪造型
很多人看到左右的波浪边框,第一想法,应该是用图片实现。现在我们就打破这一想法,用CSS搞定这个效果。
radial-gradient() 函数用径向渐变创建 "图像"。径向渐变由中心点定义。为了创建径向渐变你必须设置两个终止色。
语法: background: radial-gradient(shape size at position, start-color, ..., last-color);
CSS3径向渐变实现优惠券波浪造型
<div class="coupon"></div>
这里用radial-gradient绘制一个圆,设置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径向渐变实现优惠券波浪造型
看看原本是这样,这里的left是8px
.coupon { ... background-image: radial-gradient( circle at 8px 8px, transparent 6px, #ff9e6d 6px, #ff9e6d 0px); ... }
CSS3径向渐变实现优惠券波浪造型
设置背景大小,y轴默认平铺,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径向渐变实现优惠券波浪造型
同理,我们添加右边波浪,
.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径向渐变实现优惠券波浪造型
<div class="coupon">50元</div>
用:before伪类,制作中间的虚线,:after伪类,添加“立即领取”文字。同时添加金额(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: "立即领取"; font-size: 26px; width: 70px; top: 50%; right: 2%; transform: translate(-50%, -50%); line-height: 40px; letter-spacing: 5px; }
CSS3径向渐变实现优惠券波浪造型
演示地址:CSS3径向渐变实现优惠券波浪造型
CSS3 box-shadow实现背景动画
从浅到深的学习 CSS3阴影(box-shadow)
CSS3线性渐变、阴影、缩放实现动画下雨效果
*请认真填写需求信息,我们会在24小时内与您取得联系。