内容简介:昨天公司培训canvas相关内容,然后培训完还留下一道homework,觉得挺有意思的,特来与大家分享分享。大家可以先不看我的实现,自己尝试试试,还是可以学到不少知识的。初看题目内容好像挺简单的,不就是个渐变嘛,看我的,翻翻万能的mdn查查canvas渐变api,
昨天公司培训canvas相关内容,然后培训完还留下一道homework,觉得挺有意思的,特来与大家分享分享。大家可以先不看我的实现,自己尝试试试,还是可以学到不少知识的。
题目内容
初看题目内容好像挺简单的,不就是个渐变嘛,看我的,翻翻万能的mdn查查canvas渐变api,
CanvasGradient
接口表示描述渐变的不透明对象。通过 CanvasRenderingContext2D.createLinearGradient()
或 CanvasRenderingContext2D.createRadialGradient()
的返回值得到. developer.mozilla.org/zh-CN/docs/…
好像哪不对,这两个渐变api只有线性渐变( LinearGradient
)和圆形渐变( RadialGradient
);而题目的意思是绘制一个扇形渐变,从0到360度的一个按照角度渐变的一个圆。然后我就问我们设计的小伙伴,怎么画这种圆锥渐变,毕竟 工具 画图和代码画图思路还是一样的,只不过过程不一样。然而现实是,ps自带角度渐变。
what?好吧,只能自己分析了。
分析题目
首先抛开渐变不谈,我们把颜色分成几块,每块一种颜色是不是就是我们熟悉的饼图。
那么我们运用微分的思想,把圆分成更多份的扇形,每种扇形一个颜色是不是就能实现题目的效果呢?我们来试试。
渐变色的实现
根据我们分析的思路,首先我们先从颜色等份开始做起,颜色常见的表示有四种十六进制颜色值(#000000),RGBA,HSL和HSV。
- HSL:H(hue)色相,S(saturation)饱和度,以及L(lightness)亮度
- HSV:H(hue)色相,S(saturation)饱和度,以及V(value)色调
- RGBA:Red(红色)Green(绿色)Blue(蓝色)和Alpha的色彩空间
色相(Hue):取值范围是从0°到360°正上方为0°的话,0度为R(红)色,120度为G(绿)色,240度为B(蓝)色
因此其实这个题目我认为用这个颜色值是最好的,算出来的渐变比较好看,不过这里我使用的是RGBA。感兴趣的小伙伴可以尝试用HSV写个渐变算法,用过角度变换。
亮度(lightness):最下面是0%也最暗,最上面是100%,最亮
饱和度(saturation):和亮度一样也是通过百分比表示的。
这些作为补充知识,这里我是使用的RGBA颜色。竟然颜色需要等分,那么我把颜色转换成RGBA,然后等分RGB三种颜色,每一份取三种颜色的差值的
/** * * @param startColor 指定起始颜色 * @param endColor 指定结束颜色 * @param step 划分渐变色区域数量 * @returns {Array} 返回渐变色数组 */ let gradientColor = function(startColor, endColor, step) { let startRGB = this.colorRgb(startColor); //转换为rgb数组模式 let startR = startRGB[0]; let startG = startRGB[1]; let startB = startRGB[2]; let endRGB = this.colorRgb(endColor); let endR = endRGB[0]; let endG = endRGB[1]; let endB = endRGB[2]; let sR = (endR - startR) / step; //总差值 let sG = (endG - startG) / step; let sB = (endB - startB) / step; let colorArr = []; for (let i = 0; i < step; i++) { //计算每一步的hex值 let hex = this.colorHex('rgb(' + parseInt((sR * i + startR)) + ',' + parseInt((sG * i + startG)) + ',' + parseInt((sB * i + startB)) + ')'); colorArr.push(hex); } return colorArr; }; 复制代码
我们把相应的十六进制颜色转换成RGB然后根据起始颜色和末颜色,计算出差值,即每份的颜色值。得出的数组就是梯度颜色的数组。相应的颜色装换函数如下:
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式) gradientColor.prototype.colorRgb = function(sColor) { let reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; sColor = sColor.toLowerCase(); if (sColor && reg.test(sColor)) { if (sColor.length === 4) { let sColorNew = "#"; for (let i = 1; i < 4; i += 1) { sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); } sColor = sColorNew; } //处理六位的颜色值 let sColorChange = []; for (let i = 1; i < 7; i += 2) { sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2))); } return sColorChange; } else { return sColor; } }; // 将rgb表示方式转换为hex表示方式 gradientColor.prototype.colorHex = function(rgb) { let _this = rgb; let reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; if (/^(rgb|RGB)/.test(_this)) { let aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(","); let strHex = "#"; for (let i = 0; i < aColor.length; i++) { let hex = Number(aColor[i]).toString(16); hex = hex < 10 ? 0 + '' + hex : hex; // 保证每个rgb的值为2位 if (hex === "0") { hex += hex; } strHex += hex; } if (strHex.length !== 7) { strHex = _this; } return strHex; } else if (reg.test(_this)) { let aNum = _this.replace(/#/, "").split(""); if (aNum.length === 6) { return _this; } else if (aNum.length === 3) { let numHex = "#"; for (let i = 0; i < aNum.length; i += 1) { numHex += (aNum[i] + aNum[i]); } return numHex; } } else { return _this; } }; 复制代码
更多颜色转换方法可以参考张鑫旭大大的文章 www.zhangxinxu.com/wordpress/2…
然后我们可以直接这样调用:
let color_list = new gradientColor("#706caa", "#f2f2b0", 360); console.log(color_list); 复制代码
这样控制台我们就能看到我们计算出的渐变颜色数组了
绘制圆
心急的小伙伴可能想画圆还不简单分分钟画一个圆
context.beginPath(); context.arc(150, 75, 50, 0, Math.PI * 2); context.stroke(); 复制代码
但是如果这么画圆,怎么填充渐变色呢,想想前面的饼图,我们把饼图分成更多份,分成360份呢?是不是就相当于有很多线段,起始点一样,长度一样,围绕起始点排列成一个圆!看到这里聪明的你应该就想到该怎么做了吧。是的,用画线的方式,来画圆,可能你觉得不可思议,moveTo和lineTo怎么可能画圆呢?下面我们就来分析如何画一个圆。
大家还记得圆的极坐标方程吗,我给大家回顾回顾;
圆的极坐标公式:ρ²=x²+y²,x=ρcosθ,y=ρsinθ tanθ=y/x,(x不为0)
下面的动图显示的很详细,圆上任意一点与圆心的线段都是可以通过极坐标表示出来的,并且如果我们每画一根线都保存下面,画满一圈后不就是一个填充圆吗。
通过上面的分析,我们来写代码
var center = [200, 200]; //圆的中心 var r = 100; // 圆的半径 ctx.moveTo(center[0] + r, center[1]); //先把起始点移到圆上 for (var i = 0; i < 360; i++) { var ii = i * Math.PI / 180; //角度转弧度 ctx.lineWidth = 2; var x = r + r * Math.cos(ii); //圆上任一点的横坐标 var y = r - r * Math.sin(ii); //圆上任一点纵坐标 ctx.lineTo(x, y); } ctx.stroke(); 复制代码
这样我们就能看到最后的结果了;
完美,我们通过moveTo和lineTo画出了一个圆,细心的小伙伴应该看到,右边有一点缺失,没连上,那是应为我们把圆分为360份但是,最后一份应该与第一份相连,也就是closePath,因此我们循环多加一次,361次就可以闭合了;
我们现在知道画圆了,那么同理我们把起始点移动到圆形,并且把圆心和圆上每一份的点连起来,不就是一个实心圆了吗。
var center = [200, 200]; //圆的中心 var r = 100; // 圆的半径 for (var i = 0; i < 360; i++) { var ii = i * Math.PI / 180; //角度转弧度 ctx.save(); ctx.beginPath(); ctx.lineWidth = 2; ctx.moveTo(center[0], center[1]); //移动路径到圆心 var x = r + r * Math.cos(ii); //圆上任一点的横坐标 var y = r - r * Math.sin(ii); //圆上任一点纵坐标 ctx.lineTo(x, y); ctx.closePath(); ctx.stroke(); } 复制代码
这样我们就得到一个实心圆,一个由360根线组成的圆
那么对应的,我们把每根线的颜色也由前面我们计算的渐变色来对应上,代码也很简单;
var center = [200, 200]; //圆的中心 var r = 100; // 圆的半径 for (var i = 0; i < 360; i++) { var ii = i * Math.PI / 180; //角度转弧度 ctx.save(); ctx.beginPath(); ctx.lineWidth = 2; ctx.strokeStyle = color_list[i]; ctx.moveTo(center[0], center[1]); //移动路径到圆心 var x = r + r * Math.cos(ii); //圆上任一点的横坐标 var y = r - r * Math.sin(ii); //圆上任一点纵坐标 ctx.lineTo(x, y); ctx.closePath(); ctx.stroke(); } 复制代码
完整代码请看这里canvas圆锥渐变
很完美,一切都按我们设想的一样。如果读者有更好的方法,可以给我留言,一起学习交流交流。
本着只是做个题目,但是发现很有意思,后续我会封装一下,做成一个渐变库,支持各种渐变。
参考资料
- css角度渐变conic-gradient: www.cnblogs.com/coco1s/p/70…
- www.zhangxinxu.com/wordpress/2…
- developer.mozilla.org/zh-CN/docs/…
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- iOS WHGradientHelper(线性、径向渐变;渐变动画;Lable字体渐变及动画)
- Flutter 中渐变的高级用法
- 沉浸式渐变图片轮播器
- CSS3 渐变(Gradients)
- java – 不同dpi的径向渐变
- 简易的iOS导航栏颜色渐变方案
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。