摘要:在编写的过程中,涉及到了中的各种位运算符,对进制色值的处理不再是循环遍历了。只对位运算符感兴趣的建议直接阅读目录中的色值的快速转换。通过阅读类,可以知道最终属性均为一个或构造出来的对象,接下来就具体说说类中的这些位运算符起到了什么作用。
从最近写的一个图表库中多带带抽象出来了颜色类库,功能包括HEX、RGB/RGBA以及HSL/HSLA各种色值的转换以及颜色明暗变化。
在编写的过程中,涉及到了JS中的各种位运算符,对16进制色值的处理不再是循环遍历了。只对位运算符感兴趣的建议直接阅读目录中的“HEX色值的快速转换”。
先上两张图,循环了1600个div,分别设置颜色的渐变和随机。虽然现在css中对颜色的处理方法越来越丰富,但在一些场景——例如可视化图表中我们还是需要用JS来控制颜色。
将各种格式的色值进行统一,方便操作,也确保展示效果一致。
对颜色进行明暗处理,最明时为白色(#fff),最暗时为黑色(#000)。
其中颜色格式包括:
3位Hex值
6位Hex值
整数型RGB
百分比型RGB
整数型RGBA
百分比型RGBA
HSL
HSLA
常见的颜色命名,如black
流程及接口要实现以上的功能,流程上应该包括:
通过正则表达式检测颜色格式。
将颜色统一为一种最易操作的格式。由于我们的操作主要为明暗操作,那么RGB/RGBA格式显然是最方便的,因此将各种格式统一为RGB/RGBA。
为每个格式化后的颜色添加“变明”、“变暗”两个方法,并返回一个新的标准格式颜色对象,以便链式调用。
颜色对象还需要有一个输出颜色字符串的方法,以便在所有操作完成后输出最终的色值添加给对应的Dom。
检测颜色格式注意,此类库使用了部分ES6语法,如需转化为浏览器可直接使用的版本,可用babel进行转换。
检测格式时,主要依靠的是正则表达式,具体如下:
const reHex3 = /^#([0-9a-f]{3})$/ const reHex6 = /^#([0-9a-f]{6})$/ const reRgbInteger = /^rgb(s*([-+]?d+)s*,s*([-+]?d+)s*,s*([-+]?d+)s*)$/ const reRgbPercent = /^rgb(s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)%s*)$/ const reRgbaInteger = /^rgba(s*([-+]?d+)s*,s*([-+]?d+)s*,s*([-+]?d+)s*,s*([-+]?d+(?:.d+)?)s*)$/ const reRgbaPercent = /^rgba(s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)s*)$/ const reHslPercent = /^hsl(s*([-+]?d+(?:.d+)?)s*,s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)%s*)$/ const reHslaPercent = /^hsla(s*([-+]?d+(?:.d+)?)s*,s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)%s*,s*([-+]?d+(?:.d+)?)s*)$/
对于已命名的颜色,则构建了一个named对象,key为颜色名称,value则为16进制色值,例如:
const named = { aliceblue: 0xf0f8ff, antiquewhite: 0xfaebd7, ... yellowgreen: 0x9acd32 } 通过named.hasOwnProperty方法来检测输入的字符串是否是已命名的颜色,如果是,则用其16进制色值替换。
实际上,我创建了3个class,分别为Color、Rgb和Hsl。以上的颜色检测均放在Color的format方法中,将格式化之后的颜色放入Color的f属性里,代码如下:
class Color { constructor () { this.f = {} } format (str) { let m str = (str + "").trim().toLowerCase() if (reHex3.exec(str)) { m = parseInt(reHex3.exec(str)[1], 16) this.f = new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) } else if (reHex6.exec(str)) { m = parseInt(reHex6.exec(str)[1], 16) this.f = this.rgbn(m) } else if (reRgbInteger.exec(str)) { m = reRgbInteger.exec(str) this.f = new Rgb(m[1], m[2], m[3], 1) } else if (reRgbPercent.exec(str)) { m = reRgbPercent.exec(str) const r = 255 / 100 this.f = new Rgb(m[1] * r, m[2] * r, m[3] * r, 1) } else if (reRgbaInteger.exec(str)) { m = reRgbaInteger.exec(str) this.f = this.rgba(m[1], m[2], m[3], m[4]) } else if (reRgbaPercent.exec(str)) { m = reRgbaPercent.exec(str) const r = 255 / 100 this.f = this.rgba(m[1] * r, m[2] * r, m[3] * r, m[4]) } else if (reHslPercent.exec(str)) { m = reHslPercent.exec(str) this.f = this.hsla(m[1], m[2] / 100, m[3] / 100, 1) } else if (reHslaPercent.exec(str)) { m = reHslaPercent.exec(str) this.f = this.hsla(m[1], m[2] / 100, m[3] / 100, m[4]) } else if (named.hasOwnProperty(str)) { this.f = this.rgbn(named[str]) } else if (str === "transparent") { this.f = new Rgb(NaN, NaN, NaN, 0) } else { this.f = null throw new Error("Invalid color format.") } return this.f } rgbn (n) { return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1) } rgba (r, g, b, a) { if (a <= 0) r = g = b = NaN return new Rgb(r, g, b, a) } hsla (h, s, l, a) { if (a <= 0) { h = s = l = NaN } else if (l <= 0 || l >= 1) { h = s = NaN } else if (s <= 0) { h = NaN } return new Hsl(h, s, l, a).rgb() } }
为了方便读者快速理解代码,用了大量的if / else if,实际可以用三元表达式替代,让代码更优雅紧凑。
通过阅读Color类,可以知道最终f属性均为一个 new Rgb 或 new Hsl 构造出来的对象,接下来就具体说说Color类中的这些位运算符起到了什么作用。
HSL和RGB的转换没有什么黑魔法,都是查Wiki之后写的方法,大同小异,所以重点讲讲16进制色值是怎样处理的。
网上资料中,大部分的HEX转Rgb都是通过遍历字符串,将HEX色值分隔,再转化为10进制数字。但在阅读d3.js的源码后,发现还有更巧妙的处理方法。
首先补充一下HEX色值的基本概念。HEX色值可以为3位或者6位,3位可以理解为一种简写,如#123,实际等于#112233。
而对于一个6位的HEX色值,如#112233,在转换为RGB时,实际是每两位对应RGB中的一个值,即11、22、33分别对应R、G、B。
首先以6位HEX色值为例,我们通过正则表达式取出其值后,parseInt(str, 16)转化为16进制数字,也可以通过在前面加上"0x"来达到这一效果,目的都是告诉解析器,它是一个16进制的数。依然以#112233为例,具体看看代码:
const m = parseInt("112233", 16) // 0x112233 // 分别获取R、G、B的值 const r = m >> 16 & 0xff // 17 const g = m >> 8 & 0xff // 34 const b = m & 0xff // 51
那么>>和&分别起什么作用,为什么这样一操作就能直接取出对应数值呢?
>>是JS中的右移运算符,用于将数字的二进制右移n位。对于一个16进制的数字而言,每一位数字都对应4位2进制数字,如0x112233的二进制就是0001 0001 0010 0010 0011 0011。
因此要取出最左端11对应的10进制数字,只需要将其右移16位,剩下左起的8位即可。
那么当我们需要取中间的22和最右端的33时该怎么办呢?这就需要用到&。&是JS中位的与运算,说起来有点绕口,实际就是将两端的值的二进制按位一一取与运算。
所以我们实际看看取22和33时发生了什么:
// 0x112233的二进制为0001 0001 0010 0010 0011 0011 let n = 0x112233 >> 8 // 0001 0001 0010 0010 // 将n和0xff按位与运算,0xff的二进制为1111 1111 n & 0xff // 0010 0010 也就是 0x22 n = 0x112233 & 0xff // 0011 0011 也就是 0x33
简单的说,就是通过与0xff这个二进制最右端8均为1的数与运算,从而取出目标数最右端的八位,并舍弃其余所有位数。
总的来说,就是先用>>调整位置,再用&筛选。
我们接着处理3位HEX值,以#123为例,取出对应的R、G、B。
const m = parseInt("123", 16) // 0x123 const r = (m >> 8 & 0xf) | (m >> 4 & 0x0f0) // 17 const g = (m >> 4 & 0xf) | (m & 0xf0) // 34 const b = ((m & 0xf) << 4) | (m & 0xf) // 51
代码中出现的|是位的或运算符,机制和&相类似。<<则是和>>对应的左移运算符。
同样一步一步看看|是怎么起到作用的:
// 0x123的二进制为0001 0010 0011 0x123 >> 8 & 0xf // 0001 0x123 >> 4 & 0x0f0 // 0001 0000 0001 | 0001 0000 // 0001 0001 也就是 0x11 0x123 >> 4 & 0xf // 0010 0x123 & 0xf0 // 0010 0000 0010 | 0010 0000 // 0010 0010 也就是 0x22 (0x123 & 0xf) << 4 // 0011 0000 0x123 & 0xf // 0011 0011 0000 | 0011 // 0011 0011 也就是 0x33
思路和6位时一样,只是增加了<<和|,更灵活的操作各种位运算。
剩余工作之后要做的主要就是一些HSL转换、明暗变化以及各种错误处理,都是比较常规的做法,这里不多做赘述,有兴趣的可以看看代码:https://github.com/Yuyz0112/v...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80133.html
摘要:检查设定位操作符还有一些其他有用的位屏蔽应用。请注意,位掩码中的位将有效地关闭十进制数中的相应位,因为。 原文标题:Interesting use cases for JavaScript bitwise operators原文地址:https://blog.logrocket.com/in... 本文首发于公众号:符合预期的CoyPan JavaScript提供了几种运算符,可以对...
摘要:也就是说不仅是会产生这种问题,只要是采用的浮点数编码方式来表示浮点数时,则会产生这类问题。到这里我们都理解只要采取的浮点数编码的语言均会出现上述问题,只是它们的标准类库已经为我们提供了解决方案而已。 Brief 一天有个朋友问我JS中计算0.7 * 180怎么会等于125.99999999998,坑也太多了吧!那时我猜测是二进制表示数值时发生round-off error所导致,但并不...
摘要:而通过实现名为的标准模块,完美的解决了模块导入问题。通常都被称为包管理器,而这也是它最大的特色。例如,接受请求发送响应。该模块主要处理文件相关内容,其中大多数都是文件读写功能。 在上一篇文章中,我们简单的介绍了 Node.js 。了解到它基于 JavaScript、天生异步、拥有大量的第三方类库。本文将会在之前的基础上,对 Node.js 进行更深入的介绍。其中主要内容包括: Nod...
摘要:老实说,当时一进入世界的大门就晕了,各种规范概念和英文缩写词能把人整的晕晕乎乎。等新的英文缩写又出现了,一口老血还没来得及喷出,又重新振作开始新的学习征程。 showImg(http://upload-images.jianshu.io/upload_images/1131767-1c5d16e39435df10.jpg?imageMogr2/auto-orient/strip%7Ci...
阅读 3014·2021-09-28 09:42
阅读 3390·2021-09-22 15:21
阅读 1101·2021-07-29 13:50
阅读 3471·2019-08-30 15:56
阅读 3352·2019-08-30 15:54
阅读 1177·2019-08-30 13:12
阅读 1122·2019-08-29 17:03
阅读 1155·2019-08-29 10:59