实现一个颜色选择器
最近经历了一波辞职,找工作,搬家这样一个过程,所以没有空写博客,现在稳定了下来,写一下过年时写过的一些东西;
这次要写的是一个颜色选择器,也许很多人都认为是不需要的,因为有h5的 api 提供类似的功能,但是作为一个探索者,怎么能不直接实现一个呢
-
首先是样式的编写
关于样式方面我仿照的是 elementUI 的结构:<div > <div id="chooseColor" ></div> <div id="pickBox" style="display: none"> <div class="colorBox"> <div class="color" style="background-color: rgb(255, 0, 0);"> <div class="white"></div> <div class="black"></div> <div class="point"> <div class="p"></div> </div> </div> <div class="colorSelect"> <div class="colorBar"></div> <div class="thumb bar"></div> </div> </div> <div class="transparency" style="background-color: rgb(255, 255, 255);"> <div class="transparencyBar"></div> <div class="thumb trans"></div> </div> <div class="operate"> <input autocomplete="off" class="rgbaText" type="text" value="rgba(255,255,255,1)"> <button id="confirm">确认</button> </div> </div> </div> -
css 的添加
说到这里得说一个 css 的渐变色的使用:
linear-gradient
该 API 可以让颜色变成渐变色,相当好用,现在使用率很高
如这般使用:background: linear-gradient(to left, #3f87a6, #ebf8e1, #f69d3c); 第一个参数是颜色旋转角度,后面的参数都是颜色,也可以加上比例
完成后的样式,如下图:
-
初始化
chooseColor.addEventListener('click',()=>{ pick.style.display = 'block'; init(); },false); document.getElementById('confirm').addEventListener('click',()=>{ pick.style.display = 'none'; }); 这2个事件是启动与确认关闭的事件
再就是说到 'init()' 这个函数,他是用来初始化查询器, div 的长宽和距幕距离,至于为什么要这样做呢,具体可以了解一下
display:none
和选择器的关系;const init = (): void => { pick = <HTMLElement>document.getElementById('pickBox'); colorElement = <HTMLElement>pick.querySelector('.color'); colorPoint = <HTMLElement>pick.querySelector('.point'); colorBar = <HTMLElement>pick.querySelector('.colorBar'); rgbaText = <HTMLInputElement>pick.querySelector('.rgbaText'); colorBarThumb = <HTMLElement>pick.querySelector('.bar.thumb'); transparency = <HTMLElement>pick.querySelector('.transparency'); transparencyBar = <HTMLElement>pick.querySelector('.transparencyBar'); transparencyThumb = <HTMLElement>pick.querySelector('.transparency .thumb'); //color长宽 colorWidth = colorElement.clientWidth; colorHeight = colorElement.clientHeight; transparencyBarWidth = transparencyBar.clientWidth; pickBoxOffsetTop = pick.getBoundingClientRect().top; pickBoxOffsetLeft = pick.getBoundingClientRect().left; }; 一些工具函数:
const pxToNumber = (px: string = '0px'): number => { return Number(px.slice(0, -2)); }; const objToRGBA = (obj: rgb): string => { return `rgba(${obj.r},${obj.g},${obj.b},${transparencyCache})`; }; const objToRGB = (obj: rgb): string => { return `rgb(${obj.r},${obj.g},${obj.b})`; }; const rgbToObj = (rgbString: string): rgb => { let array: string[] = rgbString.split(','); return {r: Number(array[0].split('(')[1]), g: Number(array[1]), b: Number(array[2].slice(0, -1))}; }; -
添加 click 事件
pick.addEventListener('click', (ev) => { const target = <HTMLElement>ev.target; const x = ev.offsetX, y = ev.offsetY; switch (target.className) { case 'colorBar': colorBarThumb.style.top = y + 'px'; const result = changeColorBar(y / colorHeight); colorElement.style.backgroundColor = objToRGB(result); return changeColor(pxToNumber(colorPoint.style.left), pxToNumber(colorPoint.style.top)); case 'black': colorPoint.style.left = x + 'px'; colorPoint.style.top = y + 'px'; return changeColor(x, y); case 'transparencyBar': return changeTransparency(x); } }, false); 根据点击元素的 className 判断点击到的元素是什么,做出判断和对于的事件
4.1 先说下 colorBar 的选择
需要实现的是点击 bar 之后,根据点击的位置到bar顶部的长度,通过 bar 颜色的分布来判断出点击位置的颜色
听起来比较繁琐,其实还是很容易的
先获取鼠标点击距离顶端的长度:y = ev.offsetY
,改变 thumb 的位置;
colorBarRange 函数:const colorBarRange = (scale: number): colorBarRangeType => { switch (true) { case scale < .17: return {rank: scale / .17, arr: [{r: 255, g: 0, b: 0}, {r: 255, g: 255, b: 0}]}; case scale < .33: return {rank: (scale - .17) / .16, arr: [{r: 255, g: 255, b: 0}, {r: 0, g: 255, b: 0}]}; case scale < .5: return {rank: (scale - .33) / .17, arr: [{r: 0, g: 255, b: 0}, {r: 0, g: 255, b: 255}]}; case scale < .67: return {rank: (scale - .5) / .17, arr: [{r: 0, g: 255, b: 255}, {r: 0, g: 0, b: 255}]}; case scale < .83: return {rank: (scale - .67) / .16, arr: [{r: 0, g: 0, b: 255}, {r: 255, g: 0, b: 255}]}; default: return {rank: (scale - .83) / .17, arr: [{r: 255, g: 0, b: 255}, {r: 255, g: 0, b: 0}]}; } }; 根据比例获取出颜色的域值和比例值
changeColorBar 函数:const changeColorBar = (scale: number) => { const range = colorBarRange(scale); let rangeArr: rgb[] = range.arr; let diff: rgb = { r: rangeArr[0].r - rangeArr[1].r, g: rangeArr[0].g - rangeArr[1].g, b: rangeArr[0].b - rangeArr[1].b }; let result = rangeArr[1]; for (let i in diff) { result[i] = result[i] + diff[i] * (1 - range.rank) | 0; } return result; }; 通过上面的函数通过和255满值的计算,乘以比例值,获取出颜色值;
changeColor的作用是改变 black的值,具体下面会说
4.2 当前选中项为 black 时:
改变点的位置:colorPoint.style.left = x + 'px'; colorPoint.style.top = y + 'px'; 再次调用
changeColor
函数const changeColor = (x: number = 0, y: number = 0): void => { let {r, g, b} = rgbToObj(colorElement.style.backgroundColor); const difference = { r: 255 - r, g: 255 - g, b: 255 - b }; const scaleX = x / colorWidth; scaleChange(difference, scaleX); const result = { r: 255 - difference.r, g: 255 - difference.g, b: 255 - difference.b }; const scaleY = y / colorHeight; scaleChange(result, 1 - scaleY); const RGBA = objToRGBA(result); chooseColor.style.backgroundColor = RGBA; rgbaText.value = RGBA; transparency.style.backgroundColor = objToRGB(result); }; 传入2个值,一个是x,一个是 y, 各自对比 black 的长宽,得到比例,通过满值
rgb(255,255,255)
乘以比例, 获取 rgb 值,默认透明度为1,转换为 rgba 格式
4.3 当选中项为 transparency 时:
changeTransparency 函数:const changeTransparency = (x: number) => { const transparency = getTransparency(x); transparencyThumb.style.left = x + 'px'; transparencyCache = transparency; let currentColor = rgbaText.value.split(','); currentColor.splice(currentColor.length - 1, 1, transparency + ')'); const changeTransparencyColor = currentColor.join(','); rgbaText.value = changeTransparencyColor; chooseColor.style.backgroundColor = changeTransparencyColor; }; getTransparency
的作用是将宽度转换为比例,转化为满值为1,保留2位小数的值; -
鼠标移动
鼠标按下时改变值:pick.addEventListener('mousedown', (ev) => { const target = <HTMLElement>ev.target; switch (target.className) { case 'p': return isMoveColor = true; case 'point': return isMoveColor = true; case 'thumb bar': return isMoveColorBar = true; case 'thumb trans': return isMoveTransparency = true; } }, false); 监听
mousemove
事件:
5.1 当 isMoveColor 变化时:let diffX = cx - pickBoxOffsetLeft - 7, diffY = cy - pickBoxOffsetTop - 7; if (diffX < 0) diffX = 0; if (diffY < 0) diffY = 0; if (diffX > colorWidth) diffX = colorWidth; if (diffY > colorHeight) diffY = colorHeight; changeColor(diffX, diffY); colorPoint.style.left = diffX + 'px'; colorPoint.style.top = diffY + 'px'; 检测值鼠标是否移动到了选择器外,并且更新颜色值
5.2 当 isMoveColorBar 变化时:let diffY = cy - pickBoxOffsetTop - 7; if (diffY < 0) diffY = 0; if (diffY > colorHeight) diffY = colorHeight; colorBarThumb.style.top = diffY + 'px'; const result = changeColorBar(diffY / colorHeight); colorElement.style.backgroundColor = objToRGB(result); changeColor(pxToNumber(colorPoint.style.left), pxToNumber(colorPoint.style.top)); 同样地判断是否到边框外,以上面改变颜色值的方式同样地获取;
5.3 当 isMoveTransparency 变化时:let diffX = cx - pickBoxOffsetLeft - 7; if (diffX < 0) diffX = 0; if (diffX > transparencyBarWidth) diffX = transparencyBarWidth; changeTransparency(diffX); 检验是否到边框外即可;
-
结语
虽然现在很多情况下,不用写这个东西了,但其中还是蕴含了很多知识点的,尤其是要接触了图形的人,实现这的关键在于,将坐标转换为颜色这一步,如果能够搞懂,那么这么对于你来说就会显得异常简单了;
完;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧