前言
圖例不只能夠幫助我們在查看地圖的時候更加方便容易地分辨不同顏色代表的要素,本文要介紹的圖例組件還可以按需求過濾掉不用顯示的要素,使地圖的更能清晰的顯示我們需要顯示的內(nèi)容
技術(shù)核心
說到過濾要素,第一時間想到的就是滑塊組件,這里我們選擇了Element-plus的Slider 滑塊組件,主要功能就是根據(jù)滑塊背景的不同顏色,滑動滑塊過濾掉滑塊范圍以外背景顏色的要素。這里遇到一個大麻煩,本來以為很容易解決的,后面發(fā)現(xiàn)沒有這么容易完成,就是滑塊的背景顏色添加,這里想了好幾種方式,最后還是只能使用原生js通過操作dom實現(xiàn)背景顏色的添加和上下兩個滑塊遮罩的添加(將要過濾的顏色范圍遮蓋掉)。核心代碼如下:
watchEffect(() => {
let [min, max] = sliderValue.value
//為了添加滑塊的遮罩,達到想要的效果(重點)
nextTick(() => {
let runway = document.getElementsByClassName('sliderLegend')?.[0]?.querySelectorAll('.el-slider__runway')[0]
if (!runway.querySelector('.maxBar')) {
let div = document.createElement('div')
div.setAttribute('class', 'maxBar maskBar')
runway.appendChild(div)
}
if (!runway.querySelector('.minBar')) {
let div = document.createElement('div')
div.setAttribute('class', 'minBar maskBar')
runway.appendChild(div)
}
let maxMaskBar = document.querySelector('.maxBar')
let minMaskBar = document.querySelector('.minBar')
if (maxMaskBar) {
maxMaskBar.style.height = new BigNumber(100).minus(max).toNumber() + '%'
}
if (minMaskBar) {
minMaskBar.style.height = min + '%'
}
})
})
完整代碼
/**
* sliderLegend.vue 可以拖動滑塊過濾的圖例
* @Author ZhangJun
* @Date 2024/1/19 9:36
**/
<template>
<el-card class='slider-block absolute right-10 bottom-10'
v-if='grades.values&&grades.values.length>0'
style='z-index: 1000;'>
<el-space direction='vertical' :size='20'>
<el-text size='large' type='info'>{{ grades.desc }}({{ grades.unit }})</el-text>
<div style='width: 60px;text-align: left;'>
<el-slider ref='legendRef'
class='sliderLegend'
v-model='sliderValue'
@change='onChange'
:range='true'
vertical
:marks='marks'
v-bind='options' />
</div>
</el-space>
</el-card>
</template>
<script setup>
import { ref, defineProps, computed, reactive, watchEffect, nextTick } from 'vue'
import BigNumber from 'bignumber.js'
const props = defineProps({
gradesValues: { type: Array, default: () => ([]) },//分級的value數(shù)值
gradesColors: { type: Array, default: () => ([]) },//分級的color數(shù)值(需要與gradesValues一一對應)
})
const emits = defineEmits(['update:modeValue'])
let legendRef = ref()
//當前滑塊的值
let sliderValue = ref([0, 100])
//間隔數(shù)值
let interval = ref()
//為了獲取真實數(shù)據(jù)和百分比之間的映射關(guān)系
let percentage2ValueDic = {}
let valueDic2Percentage = {}
/**
* @Description 生成滑塊的背景顏色
* @Param colors 備選顏色集合
* @Author ZhangJun
* @Date 2024-01-19 01:43:56
* @return void
**/
let generationLinearGradient = computed(() => {
let { colors } = grades
let interval = new BigNumber(1).div(colors?.length)
let temp = colors.map((color, index) => {
let percentage = interval.times(index).times(100).toFixed(0) + '%'
let rgba = `rgba(${color[0]},${color[1]},${color[2]},${color.length > 3 ? color[3] / 255 : 1})`
if (index === 0) {
return [rgba]
} else {
return [percentage, rgba]
}
})
return `linear-gradient(to top,${temp.flat()})`
})
//刻度值
const marks = computed(() => {
//清空
percentage2ValueDic = {}
valueDic2Percentage = {}
let { values, unit } = grades
interval.value = new BigNumber(1).div(values?.length - 1)
let marks = {}
values.forEach((value, index) => {
let percentageVal = interval.value.times(index).times(100).toFixed(0)
marks = { ...marks, [percentageVal]: `${value}` }
//做映射使用
percentage2ValueDic = { ...percentage2ValueDic, [percentageVal]: value }
valueDic2Percentage = { ...valueDic2Percentage, [value]: percentageVal }
})
return marks
})
//分級數(shù)據(jù)
let grades = reactive({
values: props.gradesValues,
colors: props.gradesColors,
unit: undefined,
desc: '',
})
//默認的配置參數(shù)
let options = computed(() => {
let { values } = grades
let height = (values?.length - 1) * 40
height = height > 300 ? 300 : height
return {
min: 0,
max: 100,
height: `${height}px`,
step: interval.value.times(100).toFixed(0),
showTooltip: false,
}
})
/**
* @Description 初始化當前control
* @Param gradesData 分級參數(shù)
* @Author ZhangJun
* @Date 2024-01-22 09:38:17
* @return void
**/
function initControl(gradesData) {
sliderValue.value = [0, 100]
grades = Object.assign(grades, gradesData)
}
/**
* @Description 獲取數(shù)組里面最接近這個值的數(shù)
* @Param arr 數(shù)組
* @Param target 目標值
* @Author ZhangJun
* @Date 2024-01-19 07:19:21
* @return void
**/
function getClosestNumber(arr = [], target) {
let minDistance = Math.abs(target - arr[0])
let [closestNumber] = arr
for (let i = 1; i < arr.length; i++) {
let distance = Math.abs(target - arr[i])
if (distance < minDistance) {
minDistance = distance
closestNumber = arr[i]
}
}
return closestNumber
}
/**
* @Description 滑塊數(shù)值改變
* @Param e 事件回調(diào)參數(shù)
* @Author ZhangJun
* @Date 2024-01-22 09:38:59
* @return void
**/
function onChange(e) {
let [min, max] = e
let arr = Object.keys(percentage2ValueDic)
min = getClosestNumber(arr, min)
max = getClosestNumber(arr, max)
emits('update:modeValue', [percentage2ValueDic[min], percentage2ValueDic[max]])
}
watchEffect(() => {
let [min, max] = sliderValue.value
//為了添加滑塊的遮罩,達到想要的效果(重點)
nextTick(() => {
let runway = document.getElementsByClassName('sliderLegend')?.[0]?.querySelectorAll('.el-slider__runway')[0]
if (!runway.querySelector('.maxBar')) {
let div = document.createElement('div')
div.setAttribute('class', 'maxBar maskBar')
runway.appendChild(div)
}
if (!runway.querySelector('.minBar')) {
let div = document.createElement('div')
div.setAttribute('class', 'minBar maskBar')
runway.appendChild(div)
}
let maxMaskBar = document.querySelector('.maxBar')
let minMaskBar = document.querySelector('.minBar')
if (maxMaskBar) {
maxMaskBar.style.height = new BigNumber(100).minus(max).toNumber() + '%'
}
if (minMaskBar) {
minMaskBar.style.height = min + '%'
}
})
})
defineExpose({ initControl })
</script>
<style scoped lang='scss'>
::v-deep .el-slider__runway {
background: v-bind(generationLinearGradient);
.maskBar {
width: 100%;
height: auto;
background: lightgrey;
position: absolute;
border-radius: 6px;
}
.maxBar {
top: -3px;
}
.minBar {
bottom: -3px;
}
}
::v-deep .el-slider__bar {
background: transparent;
}
</style>
效果
過濾后的效果
本來原理很簡單,主要時間還是花費到了slider的背景顏色上,還要添加一個上下滑動的遮罩,才能將要過濾掉的顏色范圍遮掉
進階版
加入可橫向/豎向選擇文章來源:http://www.zghlxwxcb.cn/news/detail-814842.html
/**
* sliderLegend.vue 可以拖動滑塊過濾的圖例
* @Author ZhangJun
* @Date 2024/1/19 9:36
**/
<template>
<el-card
class="slider-block absolute right-10 bottom-10 text-center"
v-if="grades.values && grades.values.length > 0"
:body-style="getBodyStyle"
style="z-index: 600"
>
<el-space direction="vertical" :size="vertical ? 20 : 0">
<el-text type="info">{{ grades.desc }}({{ grades.unit }})</el-text>
<div style="text-align: left">
<el-slider ref="legendRef" class="sliderLegend" v-model="sliderValue" @change="onChange" :range="true" :marks="marks" v-bind="options" />
</div>
</el-space>
</el-card>
</template>
<script setup>
import { ref, defineProps, computed, reactive, watchEffect, nextTick } from 'vue'
import BigNumber from 'bignumber.js'
const props = defineProps({
gradesValues: { type: Array, default: () => [] }, //分級的value數(shù)值
gradesColors: { type: Array, default: () => [] }, //分級的color數(shù)值(需要與gradesValues一一對應)
intervalHeight: { type: Number, default: 40 }, //間隔的高的
maxHeight: { type: Number, default: 195 }, //圖例最高高度限制
width: { type: Number, default: 130 }, //圖例寬度
vertical: { type: Boolean, default: false }, //是否垂直模式
})
const emits = defineEmits(['update:modeValue'])
let legendRef = ref()
//當前滑塊的值
let sliderValue = ref([0, 100])
//間隔數(shù)值
let interval = ref()
//為了獲取真實數(shù)據(jù)和百分比之間的映射關(guān)系
let percentage2ValueDic = {}
let valueDic2Percentage = {}
//得到內(nèi)容部分的樣式
let getBodyStyle = computed(() => {
if (props?.vertical) {
return { padding: `10px 0 0`, width: `${props.width}px` }
} else {
return { padding: `10px`, height: `${props.width}px` }
}
})
/**
* @Description 生成滑塊的背景顏色
* @Param colors 備選顏色集合
* @Author ZhangJun
* @Date 2024-01-19 01:43:56
* @return void
**/
let generationLinearGradient = computed(() => {
let { colors } = grades
let interval = new BigNumber(1).div(colors?.length - 1)
let temp = colors.map((color, index) => {
let percentage = interval.times(index).times(100).toFixed(0) + '%'
let rgba = `rgba(${color[0]},${color[1]},${color[2]},${color.length > 3 ? color[3] / 255 : 1})`
if (index === 0) {
return [rgba]
} else {
return [percentage, rgba]
}
})
if (props?.vertical) {
return `linear-gradient(to top,${temp.flat()})`
} else {
return `linear-gradient(to right,${temp.flat()})`
}
})
//刻度值
const marks = computed(() => {
//清空
percentage2ValueDic = {}
valueDic2Percentage = {}
let { values, unit } = grades
interval.value = new BigNumber(1).div(values?.length - 1)
let marks = {}
values.forEach((value, index) => {
let percentageVal = interval.value.times(index).times(100).toFixed(0)
marks = { ...marks, [percentageVal]: `${value}` }
//做映射使用
percentage2ValueDic = { ...percentage2ValueDic, [percentageVal]: value }
valueDic2Percentage = { ...valueDic2Percentage, [value]: percentageVal }
})
return marks
})
//分級數(shù)據(jù)
let grades = reactive({
values: props.gradesValues,
colors: props.gradesColors,
unit: undefined,
desc: '',
})
//默認的配置參數(shù)
let options = computed(() => {
let { values } = grades
let height = new BigNumber(props.intervalHeight).times(values?.length - 1).toNumber()
height = height > props.maxHeight ? props.maxHeight : height
let options_temp = {
min: 0,
max: 100,
step: interval.value.times(100).toFixed(0),
showTooltip: false,
vertical: props?.vertical,
size: 'small',
}
if (props?.vertical) {
options_temp = {
...options_temp,
height: `${height}px`,
}
} else {
options_temp = {
...options_temp,
style: {
margin: '0 20px 0',
width: `${height}px`,
},
}
}
return options_temp
})
/**
* @Description 初始化當前control
* @Param gradesData 分級參數(shù)
* @Author ZhangJun
* @Date 2024-01-22 09:38:17
* @return void
**/
function initControl(gradesData) {
sliderValue.value = [0, 100]
grades = Object.assign(grades, gradesData)
}
/**
* @Description 獲取數(shù)組里面最接近這個值的數(shù)
* @Param arr 數(shù)組
* @Param target 目標值
* @Author ZhangJun
* @Date 2024-01-19 07:19:21
* @return void
**/
function getClosestNumber(arr = [], target) {
let minDistance = Math.abs(target - arr[0])
let [closestNumber] = arr
arr.forEach((value, index) => {
if (index > 0) {
let distance = Math.abs(target - value)
if (distance < minDistance) {
minDistance = distance
closestNumber = value
}
}
})
return closestNumber
}
/**
* @Description 滑塊數(shù)值改變
* @Param e 事件回調(diào)參數(shù)
* @Author ZhangJun
* @Date 2024-01-22 09:38:59
* @return void
**/
function onChange(e) {
let [min, max] = e
let arr = Object.keys(percentage2ValueDic)
min = getClosestNumber(arr, min)
max = getClosestNumber(arr, max)
emits('update:modeValue', [percentage2ValueDic[min], percentage2ValueDic[max]])
}
watchEffect(() => {
let [min, max] = sliderValue.value
//為了添加滑塊的遮罩,達到想要的效果(重點)
nextTick(() => {
let runway = document.getElementsByClassName('sliderLegend')?.[0]?.querySelectorAll('.el-slider__runway')[0]
if (!runway?.querySelector('.maxBar')) {
let div = document.createElement('div')
div.setAttribute('class', `maxBar maskBar ${props?.vertical ? 'verticalBar' : ''}`)
runway.appendChild(div)
}
if (!runway?.querySelector('.minBar')) {
let div = document.createElement('div')
div.setAttribute('class', `minBar maskBar ${props?.vertical ? 'verticalBar' : ''}`)
runway.appendChild(div)
}
let maxMaskBar = document.querySelector('.maxBar')
let minMaskBar = document.querySelector('.minBar')
if (maxMaskBar) {
if (props?.vertical) {
maxMaskBar.style.height = new BigNumber(100).minus(max).toNumber() + '%'
} else {
debugger
maxMaskBar.style.width = new BigNumber(100).minus(max).toNumber() + '%'
}
}
if (minMaskBar) {
if (props?.vertical) {
minMaskBar.style.height = min + '%'
} else {
minMaskBar.style.width = min + '%'
}
}
})
})
defineExpose({ initControl })
</script>
<style scoped lang="scss">
::v-deep .el-slider__runway {
background: v-bind(generationLinearGradient);
.maskBar {
height: 100%;
width: auto;
top: 0;
&.verticalBar {
width: 100%;
height: auto;
top: auto;
}
background: lightgrey;
position: absolute;
border-radius: 6px;
}
.maxBar {
right: -3px;
&.verticalBar {
top: -3px;
right: auto;
}
}
.minBar {
left: -3px;
&.verticalBar {
bottom: -3px;
left: auto;
}
}
}
::v-deep .el-slider__bar {
background: transparent;
}
</style>
本文為學習筆記,僅供參考文章來源地址http://www.zghlxwxcb.cn/news/detail-814842.html
到了這里,關(guān)于leaflet學習筆記-帶過濾的圖例(九)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!