手搓——实现多角标的颜色/值滑动条
MyVisualMap实现思路(不包含完整代码)
效果类似这种,实现方式想必非常多,接下来仅讲解我的思路,可做一种参考。
描述
实现一个类echarts visualMap的工具bar,可以调整一个高度自定义的echarts图形的values和colors
分析
实现一个带两种模式的多角标(网上普遍叫法是multiple handles)Slider。
模式一(渐变模式)是固定values,修改对应的colors,此时滑动条是一个渐变色块;
模式二(非渐变模式)是固定colors,修改对应的values,此时滑动条是一个多个色块组成的均匀条。
思路
咱们慢慢来,手挫肯定是要画点时间的,喝口水慢慢来。
绘图
- 前置共识:values和colors一一对应,拖拽牵扯到的变动属性(top/height等)在绘图的时候需要考虑到,别框死了。
- 固定一个div作为盛放色块和角标的容器,灵活使用css-position-relative/absolute,对应给div两块子元素。
- 颜色块-colorRef
- 渐变模式时,可以直接给colorRef设置一个
linear-gradient(to bottom, ${colorStr})
的背景色。 - 非渐变模式,可以给colorRef均分成colors.length数量的色块,也就是若干个高度为height/len的不同颜色的div,其top值为idx * height/len。
- 渐变模式时,可以直接给colorRef设置一个
- 角标块-textRef
- 渐变模式时,textRef均分成height/(len-1)个,那么每个角标的top就 *idx 有了。
- 非渐变模式,textRef均分成height/len个,同样页确定好了每个角标的位置。
- 颜色块-colorRef
- 画角标图形:一般使用绘制三角形的方法,或者使用clip-path: polygon(),接下来简单介绍一下后者的使用方法
- 每一个’,’为一组,每组结构是(x长度 y长度):
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
- 每一个’,’为一组,每组结构是(x长度 y长度):
- 其他
不带计算的交互
- 前置共识:最上角标和最下角标不可拖拽,每个角标的拖拽区域在其前后角标位置之间。
- 给遍历的角标孩子添加mousedown监听,绑定开始拖拽函数
- 开始拖拽(startDrag)
- 记录当前角标index
- 记录当前角标距视口顶部距离event.clientY
- 记录当前角标style.top,从textRef.children[index].style.top中拿
- 给当前角标添加mousemove和mouseup监听,分别绑定拖拽中和结束拖拽函数
- 拖拽中(Drag)
- 根据当前新event.clientY和之前记录的初始clientY,计算偏移量
- 根据偏移量和之前记录的角标top,计算出新的top值
- 边界判定(最前后两个不拖拽,)给当前角标的style.top赋新top值
- 结束拖拽(endDrag)
- 将开始拖拽函数中涉及到的几个状态重置
- 将mousemove和mouseup两个监听销毁
计算
非渐变模式,拖拽更新color对应的value
- 根据其上下角标间距(top1-top2)和其相对上角标间距(top-),计算新value值。这里注意数据类型和精确度,精确度完全可以作为一个组件配置项
- 显示:将角标文本部分的innerText更新为新value值
- 显示:将牵扯到的色块高度变一下,角标的上下色块,也就是children[index]和[index-1]
渐变模式,拖拽更新value对应的color
判断角标在哪个区间移动,根据移动量、平均高度、两头的颜色来计算插值颜色
判断移动区间:根据角标的newtop距原top的偏移,确定上下颜色,也能确定偏移的区间。想象一下,如果是向下偏移,那么上下颜色就是本颜色和下颜色;如果是向上偏移,那么上下颜色就是上颜色和本颜色,偏移量一正一负很方便代入计算
插值颜色:可能用到hex-rgb的相互转换,插值计算就是将rgb格式颜色按照比例计算,整点源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33export const interpolateColor = function (color1, color2, factor) {
// TODO: 这里还需要将factor规范一下,使其介于0-1范围
// hex => rgb 1上颜色2下颜色
const color1RGB = hexToRgb(color1);
const color2RGB = hexToRgb(color2);
// 线性插值 比例-factor要根据偏移量和区间总高度来计算
const result = color1RGB.map((val, index) => {
return Math.round(val + (color2RGB[index] - val) * factor);
});
// rgb => hex
return rgbToHex(result[0], result[1], result[2]);
};
const hexToRgb = function (hex) {
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => {
return r + r + g + g + b + b;
});
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16),
]
: null;
};
const rgbToHex = function (r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
};
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ra-Liz's Blog!
评论
ValineGitalk